简体   繁体   English

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

[英]WPF: In an attached property, how to wait until visual tree loaded properly?

I have an Attached Property in a WPF app.我在 WPF 应用程序中有一个附加属性。

The code below is inside the OnLoad event, but it doesn't work unless I add a hacky 500 millisecond delay in.下面的代码在OnLoad事件中,但除非我添加一个 hacky 500 毫秒的延迟,否则它不起作用。

Is there some way to avoid this delay, and detect when the visual tree has been loaded?有没有办法避免这种延迟,并检测何时加载了可视化树?

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

Update 1更新 1

As per the answer from @Will, "use the dispatcher and select the appropriate priority".根据@Will 的回答,“使用调度程序并选择适当的优先级”。 This works brilliantly:这非常有效:

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

Update 2更新 2

If using DevExpress, the LayoutTreeHelper class is useful for scanning up and down the visual tree.如果使用 DevExpress, LayoutTreeHelper类对于向上和向下扫描可视化树很有用。

For sample code that deals with scanning up and down the visual tree, see:有关处理向上和向下扫描可视化树的示例代码,请参阅:

Update 3更新 3

If you are in an attached property, the only way to reliably load the visual tree is to execute the code in or after the Loaded event handler.如果您在附加属性中,可靠地加载可视化树的唯一方法是在Loaded事件处理程序中或之后执行代码。 If we are not aware of this limitation, then everything will fail intermittently.如果我们没有意识到这个限制,那么一切都会间歇性地失败。 Waiting until after the OnLoaded event has fired is far superior to any other method that attempts to introduce other random forms of delays, as noted above. OnLoaded等到OnLoaded事件触发后, OnLoaded远远优于试图引入其他随机形式的延迟的任何其他方法。

If you are using DevExpress, this is even more critical: attempting to do anything before the Loaded event can, in some circumstances, result in a crash.如果您正在使用 DevExpress,这甚至更为关键:在某些情况下,尝试在Loaded事件之前执行任何操作可能会导致崩溃。

For example:例如:

  • Calling window.Show() before the Loaded event has resulted in a crash in some circumstances.在某些情况下,在Loaded事件之前调用window.Show()会导致崩溃。
  • Hooking into the event handler for IsVisibleChanged before the Loaded event has result in a crash in some circumstances.在某些情况下,在Loaded事件之前连接到IsVisibleChanged的事件处理程序会导致崩溃。

Disclaimer: I am not associated with DevExpress, it one of many good WPF libraries, I also recommend Infragistics.免责声明:我与 DevExpress 无关,它是众多优秀的 WPF 库之一,我也推荐 Infragistics。

If you want to wait to do something in the UI thread, use the Dispatcher .如果您想等待在 UI 线程中执行某些操作,请使用Dispatcher How do you snag it?你怎么抓住它? That's a dupe.那是个骗子。

How do I get the UI thread Dispatcher? 如何获取 UI 线程 Dispatcher?

You can use the DispatcherPriority to select a time in the future.您可以使用DispatcherPriority来选择未来的时间。 Priorities are based on UI activities, so you can't say, for example, next week.优先级基于 UI 活动,因此您不能说,例如,下周。 But you can say "let me wait until after bindings have been processed", for example.但是,例如,您可以说“让我等到绑定处理完毕”。

I prefer this version because it works nicer with ReSharper (suggest method stubs) and takes parameters in order of precedence.我更喜欢这个版本,因为它与 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();
        }));
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM