2013-12-11 40 views
1

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

到目前为止好:

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. 
} 

当DataGrid中的对象之一的用户点击,线的颜色变化(正常工作),但是当程序重新加载表,该画线消失(我打算清除并添加新的对象)。

来对付它,我创建了功能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并将新对象添加到数据网格中。 但如果是这样 - 为什么?我该如何解决它?

在底线,我想,经过每一个重载线重新漆立即..

感谢您的咨询!

回答

1

有几件事情要考虑:

你应该记住的是,DataGrid使用虚拟化,这意味着,在您的项目源的每个项目不获得它自己的UI元素。创建UI元素以填充可见区域,然后根据当前绑定哪个数据源项目(当您滚动实例或更改项目源时发生更改)重新使用。如果您使用目前的方法,这可能会在未来导致您的问题,因此请记住这一点。

另一件事是DataGrid可能需要布局过程更多的“周期”,以更新其用户界面。您可能只是过早地致电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)) !! 
    })); 
} 

编辑:好吧,如果失败,那么也许这将覆盖在上面的解决方案失败的情况下:订阅LoadingRow事件DataGrid,如果该行设置相应的背景颜色是选定的一个。因此,在创建新行的情况下,将调用此事件(由于虚拟化,它不会在项目源中针对每个项目调用,而是按实际行UI元素调用)。在事件参数中,您将有权访问创建的DataGridRow实例。

+0

谢谢@ odyss-jii。你的答案的第二部分是我以为发生了什么,我试过你的代码,但结果是它重新加载后从未突出行。我明白,我的解决方案是真的凌乱,但我没有其他的想法,任何提示? – Yaron

+0

嗯,你可能想尝试'LoadingRow'事件。我已经用关于这方面的一些信息更新了答案。我希望这能更好地工作。 –

+0

It Works!我注册到事件'LoadingRow'并运行'FindSelectionObject'。现在我甚至可以从'loadTaskList'中删除'FindSelectionObject'。谢谢@ odyss-jii! – Yaron

0

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

+0

它的工作原理是因为每个myObject都有id,并且我用equels()来检查这个id,所以IndexOf()可以找到它。 @阿兰老人 – Yaron

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