WPF:在附加属性中,如何等到可视树正确加载?

我在WPF应用程序中有一个附加属性。

下面的代码在OnLoad事件中,但它不起作用,除非我添加一个hacky 500毫秒延迟。

有没有办法避免这种延迟,并检测可视树何时加载?

 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; }); } }); } 

更新1

根据@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); } 

更新2

如果使用DevExpress,则LayoutTreeHelper类可用于扫描可视树。

有关处理可视树上下扫描的示例代码,请参阅:

  • DevExpress:在附属物中,如何等到我们100%确定DevExpress已经完成构建其Visual Tree?
  • DevExpress以及如何确保LayoutPanel总是在其他所有方面?

更新3

如果您在附加属性中,可靠加载可视树的唯一方法是在Loaded事件处理程序中或之后执行代码。 如果我们不知道这个限制,那么一切都将间歇性地失败。 等待OnLoaded事件发生后,等待尝试引入其他随机forms的延迟的任何其他方法都要好得多,如上所述。

如果您使用的是DevExpress,则更为关键:在某些情况下,在Loaded事件之前尝试执行任何操作都会导致崩溃。

例如: – 在Loaded事件之前调用window.Show()在某些情况下导致崩溃。 – 在Loaded事件导致某些情况下崩溃之前,挂钩IsVisibleChanged的事件处理程序。


免责声明:我与DevExpress无关,它是许多优秀的WPF库之一,我也推荐使用Infragistics。

如果您想等待在UI线程中执行某些操作,请使用Dispatcher 。 你怎么抓住它? 这是一个骗局。

如何获取UI线程Dispatcher?

您可以使用DispatcherPriority选择将来的时间。 优先级基于UI活动,因此您不能说,例如,下周。 但是你可以说“让我等到绑定处理完毕后”,例如。

我更喜欢这个版本,因为它可以更好地使用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(); })); } }