我的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
}));
}
问题:一切正常,但有时当程序重新加载时,该行每秒闪烁然后突出显示,有时它根本不会绘制它(直到下一次重新加载)。
我无法理解为什么会这样。我想也许FindSelectionObject()
开始在loadTaskList()
结束之前运行以调用all并将新对象添加到datagrid中。
但如果是这样 - 为什么?我该如何解决?
在底线,我希望在每次重新加载后立即重新绘制线条。
感谢您的任何建议!
答案 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();
}
IsBusy
和BusyText
是您可以删除的特定属性。 action
变量将是后台执行的操作(例如,加载项目)。 beforeVisualAction
和afterVisualAction
是您在后台操作之前和之后可能要执行的视觉操作。以下是任何可视更新,例如选择项目,更改颜色,设置引发绑定更新的视图模型变量,...(更新视图的任何操作)。
希望这种方法有所帮助。
答案 2 :(得分:0)
您是否在某处保留对lastSelectionObject的引用?你说你正在添加新对象,如果它们是真正的新对象,那么引用将会有所不同,并且IndexOf中发生的引用比较将无法找到它。