我在WPF应用程序中有一个附加属性。
以下代码位于OnLoad
事件中,但除非我添加了500毫秒的hacky延迟,否则它无法正常工作。
有没有办法避免这种延迟,并检测可视树何时被加载?
private static void FrameworkElement_Loaded(object sender, RoutedEventArgs e)
{
// ... snip...
Window window = GetParentWindow(dependencyObject);
// Without this delay, changing properties does nothing.
Task task = Task.Run(
async () =>
{
{
// Without this delay, changing properties does nothing.
await Task.Delay(TimeSpan.FromMilliseconds(500));
Application.Current.Dispatcher.Invoke(
() =>
{
// Set False >> True to trigger the dependency property.
window.Topmost = false;
window.Topmost = true;
});
}
});
}
根据@Will的回答,"使用调度程序并选择适当的优先级"。这非常出色:
private static void FrameworkElement_Loaded(object sender, RoutedEventArgs e)
{
// Wrap *everything* in a Dispatcher.Invoke with an appropriately
// low priority, to defer until the visual tree has finished updating.
Application.Current.Dispatcher.Invoke(
async () =>
{
// This puts us to the back of the dispatcher queue.
await Task.Yield();
// ... snip...
Window window = GetParentWindow(dependencyObject);
window.Topmost = true;
},
// Use an appropriately low priority to defer until
// visual tree updated.
DispatcherPriority.ApplicationIdle);
}
如果使用DevExpress,LayoutTreeHelper类可用于扫描可视树。
有关处理可视树上下扫描的示例代码,请参阅:
如果您在附加属性中,可靠加载可视树的唯一方法是在Loaded
事件处理程序中或之后执行代码。如果我们不知道这个限制,那么一切都会间歇性地失败。如上所述,等待OnLoaded
事件发生后,等待尝试引入其他随机形式的延迟的任何其他方法都要好得多。
如果您使用的是DevExpress,则更为关键:在某些情况下,Loaded
事件之前尝试执行任何操作都会导致崩溃。
例如:
- 在window.Show()
事件之前调用Loaded
导致某些情况下发生崩溃。
- 在IsVisibleChanged
事件导致某些情况下崩溃之前,挂钩Loaded
的事件处理程序。
免责声明:我与DevExpress无关,它是许多优秀的WPF库之一,我也推荐使用Infragistics。
答案 0 :(得分:2)
如果您想等待在UI线程中执行某些操作,请使用Dispatcher。你怎么抓住它?这是一个骗局。
How do I get the UI thread Dispatcher?
您可以使用DispatcherPriority选择将来的时间。优先级基于UI活动,因此您不能说,例如,下周。但是你可以说“让我等到绑定处理完毕后”,例如。
答案 1 :(得分:0)
我更喜欢这个版本,因为它可以更好地使用ReSharper(建议方法存根)并按优先顺序获取参数。
public static class FrameworkElementExtensions
{
public static void BeginInvoke(this DispatcherObject element, Action action, DispatcherPriority priority = DispatcherPriority.ApplicationIdle)
{
element.Dispatcher.BeginInvoke(priority, new Action(async () =>
{
await Task.Yield();
action();
}));
}
}