【问题标题】:Wpf application and ThreadsWpf 应用程序和线程
【发布时间】:2013-12-11 13:46:17
【问题描述】:

我的 GUI 和线程有问题。 GUI 包含 DataGrid。每次程序执行一些查询并获取我想要填充到 DataGrid 中的项目列表。

到目前为止一切顺利:

private void loadTaskList() //Call every X time
{
    List<myObject> myList = myquery();
    this.Dispatcher.Invoke((Action)(() =>
    {
        TaskListTable.Items.Clear(); //Clear the DataGrid
        foreach (myObject O in myList) //Add the items from the new query.
        {
            TaskListTable.Items.Add(O);
        }
    }));                    
    FindSelectionObject(); // <-- see next explanation.
}

当用户单击数据网格中的一个对象时,线条颜色发生了变化(它工作正常),但是当程序重新加载表格时,绘制的线条消失了(因为我清除并添加了新对象)。

为了处理它,我创建了函数 FindSelectionObject():

private void FindSelectionObject()
{
    this.Dispatcher.Invoke((Action)(() =>
    {
       this.SelectedIndex = TaskListTable.Items.IndexOf((myObject)lastSelectionObject); //find index of the new object that equels to the last selection object.
       var row = TaskListTable.ItemContainerGenerator.ContainerFromIndex(SelectedIndex) as DataGridRow; //get the row with the index
       row.Background = Brushes.LightGoldenrodYellow; //repaint
    }));
}

问题:一切正常,但有时当程序重新加载时,该行每秒闪烁一次,然后又高亮显示,有时根本不绘制它(直到下一次重新加载)。

我不明白为什么会这样。我认为FindSelectionObject() 可能在loadTaskList() 结束之前开始运行以调用所有对象并将新对象添加到数据网格中。 但如果是这样 - 为什么?我该如何解决?

最后,我希望在每次重新加载后立即重新绘制线..

感谢您的建议!

【问题讨论】:

    标签: c# wpf multithreading datagrid


    【解决方案1】:

    需要考虑的一些事情:

    您应该记住,DataGrid 使用虚拟化,这意味着您的项目源中的每个项目都拥有自己的 UI 元素。创建 UI 元素以填充可见区域,然后根据当前绑定到每个数据源项的数据源项重新使用(例如,当您滚动或更改项源时,这会发生变化)。如果您使用当前的方法,这可能会在将来给您带来问题,因此请记住这一点。

    另一件事是DataGrid 可能需要更多的布局过程“周期”才能更新其 UI。您可能只是过早地致电FindSelectionObject。在loadTaskList 中调用之后,您已排队FindSelectionObject。如果DataGrid 需要在项目源更改后执行一些在调度程序上排队的操作,这些操作将在FindSelectionObject 中的调用之后执行。 试试这个:

    private void loadTaskList() //Call every X time
    {
        List<myObject> myList = myquery();
        this.Dispatcher.Invoke((Action)(() =>
        {
            TaskListTable.Items.Clear(); //Clear the DataGrid
            foreach (myObject O in myList) //Add the items from the new query.
            {
                TaskListTable.Items.Add(O);
            }
    
            // The items of the grid have changed, NOW we QUEUE the FindSelectionObject
            // operation on the dispatcher.
    
            FindSelectionObject(); // <-- (( MOVE IT HERE )) !!
        }));
    }
    

    编辑:好的,所以如果这失败了,那么也许这将涵盖上述解决方案失败的情况:订阅DataGridLoadingRow 事件,如果该行是选定的行,则设置适当的背景颜色。因此,在创建新行的情况下,将调用此事件(由于虚拟化,它不是按项目源中的每个项目调用,而是按实际行 UI 元素调用)。在事件 args 中,您将有权访问创建的 DataGridRow 实例。

    【讨论】:

    • 感谢@odyss-jii。你答案的第二部分是我认为发生的事情,我尝试了你的代码,但结果是它在重新加载后从不突出显示该行。我知道我的解决方案真的很混乱,但我没有其他想法,任何提示?
    • 嗯,你可能想试试LoadingRow 事件。我已经用一些相关信息更新了答案。我希望效果更好。
    • 它有效!我注册到事件LoadingRow 并运行FindSelectionObject。现在我什至可以从loadTaskList 中删除FindSelectionObject。谢谢@odyss-jii!
    【解决方案2】:

    我认为这个问题可能是视觉线程同步。为此,您可以创建和使用类似的方法:

    public void LockAndDoInBackground(Action action, string text, Action beforeVisualAction = null, Action afterVisualAction = null)
        {
            var currentSyncContext = SynchronizationContext.Current;
            var backgroundWorker = new BackgroundWorker();
            backgroundWorker.DoWork += (_, __) =>
            {
                Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
                Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
                currentSyncContext.Send((t) =>
                {
                    IsBusy = true;
                    BusyText = string.IsNullOrEmpty(text) ? "Espere por favor..." : text;
                    if (beforeVisualAction != null)
                        beforeVisualAction();
                }, null);
                action();
                currentSyncContext.Send((t) =>
                {
                    IsBusy = false;
                    BusyText = "";
                    if (afterVisualAction != null)
                        afterVisualAction();
                }, null);
            };
            backgroundWorker.RunWorkerAsync();
        }
    

    IsBusyBusyText 是可以删除的特殊属性。 action 变量将是在后台执行的操作(例如加载您的项目)。 beforeVisualActionafterVisualAction 是您在后台操作之前和之后可能想要执行的视觉操作。这是任何视觉更新,例如选择您的项目,更改颜色,设置引发绑定更新的视图模型变量,...(更新视图的任何操作)。 希望这个方法有帮助。

    【讨论】:

      【解决方案3】:

      您是否在某处维护对 lastSelectionObject 的引用?您说您正在添加新对象,如果它们是真正的新对象,那么引用将不同,并且在 IndexOf 中发生的引用比较将找不到它。

      【讨论】:

      • 它之所以有效,是因为每个 myObject 都有 id,我覆盖 equels() 来检查这个 id,所以 IndexOf() 确实找到了它。 @艾伦长老
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-01
      • 2014-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-03
      • 1970-01-01
      相关资源
      最近更新 更多