【问题标题】:Focus is not "holding" when listview refreshes列表视图刷新时焦点不是“保持”
【发布时间】:2014-12-13 09:07:50
【问题描述】:

我的代码目前存在问题,无法将焦点放在特定项目行上。我创建了int focusReference 来跟踪当前关注的行,但它似乎每隔几秒就会重置为 0,即一旦用户选择了一行,几秒钟后,它就会“忘记”用户的选择。我在下面提供了一个示例:

正如您从下面的代码中看到的那样,我有一个计时器每隔几秒就会做一些事情。我已经被告知每隔几秒钟刷新一次我的列表会导致每隔几秒钟失去焦点。

如何以编程方式将焦点设置在项目上,以保持用户的焦点?我尝试实现FocusItem 方法,但它似乎不起作用。这是我的程序的一个非常重要的方面,我需要它正常工作,否则其他功能(如我正在实现的右键单击上下文菜单)也将无法工作:

public MainWindow()
{
    InitializeComponent();
    int focusReference = 0;
    PlotListView.SelectionChanged += (s, ee) => { PlotListView_SelectionChanged(s, ee, focusReference); };
    var dbObject = new DbConnect();
    dbObject.OpenConnection();
    RefreshPlotTimer(filterReference, focusReference);
}

public void PlotListView_SelectionChanged(object sender, SelectionChangedEventArgs e, int focusReference)
{
    if (PlotListView.SelectedItems.Count == 0) return;
    var selectedItem = (DbConnect.PlotList)PlotListView.SelectedItems[0];
    focusReference = Convert.ToInt32(selectedItem.PlotId);
    FocusItem(focusReference);
}

private void FocusItem(int focusReference)
{
    if (PlotListView.SelectedItems.Count != 0)
    {
        DbConnect.PlotList plotList =
            PlotListView.Items.OfType<DbConnect.PlotList>()
            .FirstOrDefault(p => Convert.ToInt32(p.PlotId) == focusReference);
        if (plotList != null)
        {
            //get visual container
            var container = PlotListView.ItemContainerGenerator.ContainerFromItem(plotList) as ListViewItem;
            if (container != null)
            {
                container.IsSelected = true;
                container.Focus();
            }
        }
    }
}

public void RefreshPlotTimer(int filterReference, int focusReference)
{
    var refreshTimer = new Timer();
    refreshTimer.Elapsed += (sender, e) => RefreshPlot(sender, e, filterReference, focusReference);
    refreshTimer.Interval = 2500;
    refreshTimer.Enabled = true;
}

public void RefreshPlot(object source, ElapsedEventArgs e, int filterReference, int focusReference)
{
    var dbObject = new DbConnect();
    dbObject.OpenConnection();
    dbObject.RefreshPlot();
    Dispatcher.Invoke(() =>
    {
        FocusItem(focusReference);
        if (!string.IsNullOrWhiteSpace(FilterTextBox.Text) &&
            (!Regex.IsMatch(FilterTextBox.Text, "[^0-9]")))
        {
            filterReference = Convert.ToInt32(FilterTextBox.Text);
        }
    });
    ResetPlot(filterReference);
}

public void ResetPlot(int filterReference)
{
    var dbObject = new DbConnect();
    dbObject.OpenConnection();

    List<DbConnect.PlotList> plotList = dbObject.SelectPlotLists(filterReference);
    Dispatcher.BeginInvoke(
        new ThreadStart(() => PlotListView.ItemsSource = plotList));
    int jobSum = 0;
    int bidSum = 0;
    foreach (DbConnect.PlotList item in PlotListView.Items)
    {
        jobSum += Convert.ToInt32(item.Jobs);
        bidSum += Convert.ToInt32(item.Bids);
    }

    Dispatcher.BeginInvoke(
        new ThreadStart(() => JobBidRatioTextBlock.Text = jobSum + " jobs - " + bidSum + " bids"));
}

更新 2

我恢复到我最初使用的代码,是不是 FocusItem 方法没有保留 listview 项目焦点?

private void FocusItem(int focusReference)
{
    Dispatcher.Invoke(() =>
    {
        if (PlotListView.SelectedItems.Count != 0)
        {
            DbConnect.PlotList plotList =
                PlotListView.Items.OfType<DbConnect.PlotList>()
                    .FirstOrDefault(p => Convert.ToInt32(p.PlotId) == focusReference);
            if (plotList != null)
            {
                //get visual container
                var container = PlotListView.ItemContainerGenerator.ContainerFromItem(plotList) as ListViewItem;
                if (container != null)
                {
                    container.IsSelected = true;
                    container.Focus();
                }
            }
        }
    }
}

根据控制台中写的内容,我在正确的地方调用它,索引绝对是正确的。

PlotListView_SelectionChanged 4
Before refresh 4
After refresh 4
PlotListView_SelectionChanged 7
Before refresh 7
After refresh 7

Before refresh 7
After refresh 7

都在刷新绘图方法中...

public void RefreshPlot(object source, ElapsedEventArgs e)
{
    var dbObject = new DbConnect();
    dbObject.OpenConnection();
    dbObject.RefreshPlot();
    Console.WriteLine("\rBefore refresh " + focusReference);
    Dispatcher.Invoke(() =>
    {
        if (!string.IsNullOrWhiteSpace(FilterTextBox.Text) &&
            (!Regex.IsMatch(FilterTextBox.Text, "[^0-9]")))
        {
            filterReference = Convert.ToInt32(FilterTextBox.Text);
        }
    });
    ResetPlot(filterReference);
    Console.WriteLine("After refresh " + focusReference);
    FocusItem(focusReference);
}

还有重置绘图方法……

public void ResetPlot(int filterReference)
{
    // Establish MySQL connection
    var dbObject = new DbConnect();
    dbObject.OpenConnection();

    // Fill plot list view
    List<DbConnect.PlotList> plotList = dbObject.SelectPlotLists(filterReference);
    Dispatcher.BeginInvoke(
        new ThreadStart(() => PlotListView.ItemsSource = plotList));
    int jobSum = 0;
    int bidSum = 0;
    foreach (DbConnect.PlotList item in PlotListView.Items)
    {
        jobSum += Convert.ToInt32(item.Jobs);
        bidSum += Convert.ToInt32(item.Bids);
    }
    FocusItem(focusReference);

    // Determine job/bid list ratio
    Dispatcher.BeginInvoke(
        new ThreadStart(() => JobBidRatioTextBlock.Text = jobSum + " jobs - " + bidSum + " bids"));
}

【问题讨论】:

    标签: c# wpf listview


    【解决方案1】:

    在进行刷新之前,您需要记住SelectedItem 并在刷新后将其重新设置。这就是理论。

    如果是对象,您需要找到现在与之前相同的对象(因为它不再是同一个实例),因此您必须在新结果列表中搜索它并将其分配给SelectedItem

    最简单的方法是重新设置SelectedIndex,但由于列表可能会更改,它迟早也会重新选择错误的项目。

    // Gets current selection.
    public DbConnect.PlotList SelectedPlotList
    {
        get
        {
            return PlotListView.SelectedItem as DbConnect.PlotList;
        }
    }
    public void ResetPlot(int filterReference)
    {
        // Get current plot number;
        int? plotNumber = SelectedPlotList == null ? (int?)null : SelectedPlotList.PlotNumber;
    
        var dbObject = new DbConnect();
        dbObject.OpenConnection();
    
        List<DbConnect.PlotList> plotList = dbObject.SelectPlotLists(filterReference);
    
        // Find the plot list in the new list.  
        DbConnect.PlotList selectPlotList = 
            plotNumber.HasValue
            ? plotList.Where(x => x.PlotNumber == plotNumber.Value).FirstOrDefault();
            : null;
        Dispatcher.BeginInvoke(new ThreadStart(() => PlotListView.ItemsSource = plotList));
    
        // Re-select plot list if found in the new list.
        if (selectPlotList != null)
        {
            PlotListView.SelectedItem = selectPlotList;
        }
        int jobSum = 0;
        int bidSum = 0;
    
    
        foreach (DbConnect.PlotList item in PlotListView.Items)
        {
            jobSum += Convert.ToInt32(item.Jobs);
            bidSum += Convert.ToInt32(item.Bids);
        }
    
        Dispatcher.BeginInvoke(
            new ThreadStart(() => JobBidRatioTextBlock.Text = jobSum + " jobs - " + bidSum + " bids"));
    }
    

    编辑:

    我刚刚测试了一个快速而肮脏的演示,它可以工作。其他地方一定有错误。

    这会生成 7 个具有唯一 id 的项目,如果再次绘制,则重新选择最后选择的项目:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            List<ListViewItem> items = new List<ListViewItem>();
            Random rnd = new Random(DateTime.Now.Millisecond);
            HashSet<int> ids = new HashSet<int>();
            for (int i = 0; i < 7; i++)
            {
                int id = 0;
                do
                {
                    id = rnd.Next(0, 10);
                } while (ids.Contains(id));
                ids.Add(id);
                items.Add(new ListViewItem() { Id = id, Name = "Item-" + i });
            }
            int? selectedId = listView1.SelectedItem != null ? (listView1.SelectedItem as ListViewItem).Id : (int?)null;
            listView1.ItemsSource = items;
    
            if (selectedId.HasValue)
            {
                listView1.SelectedItem = items.Where(x => x.Id == selectedId).FirstOrDefault();
                listView1.Focus();
            }
        }
    }
    
    class ListViewItem
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    

    【讨论】:

    • 感谢您的回复 - 您知道这适合我的代码吗?我以为我是通过调用FocusItem 方法在PlotListView_SelectionChanged 中执行此操作的,然后我再次在RefreshPlot 中执行此操作,但我认为我采用的逻辑有点不稳定。我的逻辑有问题吗?
    • 哦,不,我在手机上不小心删除了自己的评论。我会尝试重写它。
    • 查看我编辑的答案。我添加了一些示例代码。它可能无法 100% 工作,因为我无法编译它,但它应该会给你这个想法。
    • 我已经实施了您建议的更改,但列表项仍然失去焦点。知道为什么吗?到目前为止,这是我的代码:pastebin.com/v5KyDbWU
    • 我已经测试了一个快速演示,它可以工作。其他地方一定有错误。
    猜你喜欢
    • 2016-05-20
    • 2012-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-06
    • 2012-05-12
    • 2023-03-09
    相关资源
    最近更新 更多