我必须在代码中构建一个相当复杂的WPF UI(一个hughe Grid)(没有xaml invloved)。有没有办法在构建网格的过程中阻止主UI线程阻塞?是否有一些构建UI的部分可以外包给workerthread? UI线程的UI创建实际上必须包含哪些部分?
在构建UI时,我还有哪些其他选项可以提高性能?暂停调度程序或调用FrameworkElement.BeginInit是个好主意吗?
答案 0 :(得分:4)
如果您可以将UI的构建分解为多个步骤,则可以将这些步骤中的每个步骤作为单独的消息执行。这将允许在其间处理其他消息。类似的东西:
private delegate void BuildHandler();
private void BuildGridPart1()
{
//build first part of grid
Dispatcher.BeginInvoke(new BuildHandler(BuildGridPart2), DispatcherPriority.Background);
}
private void BuildGridPart2()
{
//build second part of grid
Dispatcher.BeginInvoke(new BuildHandler(BuildGridPart3), DispatcherPriority.Background);
}
//etc
您可以将其重构为更清晰(例如,一个状态机的方法告诉您在网格中的位置),并自动将下一个消息排入较低的优先级,以便始终首先处理输入。 Bingo,您将在UI线程上构建UI时具有响应式UI。
答案 1 :(得分:1)
我的感觉是,除了在UI线程上运行之外,您可能没有很多选择。
如果涉及的长时间是由于收集和排列数据,那么后台线程将是最佳选择,然后使用Dispatcher对象调用任何UI依赖项属性。我假设现在的时间不是收集任何数据,而是仅仅是编程创建UIElements。
据我了解,UI元素派生自DispatcherObject
,从当前工作线程获取Dispatcher
,以便排除在另一个线程上创建控件。
然后所有依赖属性在写入时调用Dispatcher.VerifyAccess
,如果在错误的线程上访问,将引发异常。因此,这排除了更新不同线程上的数据绑定和属性。
我的第一反应是像@Kent写的那样
稍微详细说一下,如果每行有一个foreach
循环,每列有另一个foreach
,那么即使你在正确的UI线程中,也可以调用Dispatcher.BeginInvoke
来“屈服” “暂时离开你的长跑方法。即将其分解,然后将控制权传递回UI线程,以便以后继续添加到网格中。布局工作量可能会大得多,总体时间会更长,但响应速度会更快。
private void Window_Loaded(object sender, RoutedEventArgs e)
{
CreateRow(1);
}
private void CreateRow(int i)
{
//
// Construct row i of the grid
//
Dispatcher.BeginInvoke(DispatcherPriority.Background, new EventHandler(delegate { CreateRow(i + 1); }));
}
答案 2 :(得分:0)
您是否有必要在代码中构建它?如果是因为网格是动态的(直到运行时才知道I.E.列),那么就有办法解决它。
要求您在代码中创建UI的确切问题是什么?一旦我们知道我们可以将它概括为模板和绑定。
无论如何,要解决您的直接问题,您可以将工作传递给工作线程,并让其使用Dispatcher更新UI,就像其他人提到的那样。我还想指出,最好将您的工作细分为细粒度单位...例如,如果您要为列表中每个项目的每个列创建UI,则应至少将其分解为项目级别,最好是在列级别。
如果您使用的是GOOD网格,它可能会使用虚拟化,并且有一个API可供您连接到虚拟化事件。 (基本上,网格只在需要时构建UI或在需要时请求UI元素)。这样可以避免在加载时构建整个网格。