Wpf应用程序和线程

时间:2013-12-11 13:46:18

标签: c# wpf multithreading datagrid

我的GUI和线程有问题。 GUI包含DataGrid。每次X程序执行一些查询并获取我想要填充到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
    }));
}

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

enter image description here

我无法理解为什么会这样。我想也许FindSelectionObject()开始在loadTaskList()结束之前运行以调用all并将新对象添加到datagrid中。 但如果是这样 - 为什么?我该如何解决?

在底线,我希望在每次重新加载后立即重新绘制线条。

感谢您的任何建议!

3 个答案:

答案 0 :(得分:1)

要考虑的一些事情:

您应该记住DataGrid使用虚拟化,这意味着您的商品来源中的每件商品都会获得自己的UI元素。创建UI元素以填充可见区域,然后根据当前绑定到每个数据源项目的数据源项目重新使用(当您滚动实例或更改项目源时,这会更改)。如果你使用当前的方法,这可能会在将来引起你的问题,所以请记住这一点。

另一件事是DataGrid可能需要更多“周期”的布局过程才能更新其UI。你可能只是过早地打电话给FindSelectionObject。您在FindSelectionObject中调用后立即排队loadTaskList。如果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 )) !!
    }));
}

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

答案 1 :(得分:1)

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

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是您在后台操作之前和之后可能要执行的视觉操作。以下是任何可视更新,例如选择项目,更改颜色,设置引发绑定更新的视图模型变量,...(更新视图的任何操作)。 希望这种方法有所帮助。

答案 2 :(得分:0)

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