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

时间:2016-03-16 14:03:34

标签: c# wpf .net-4.5 attached-properties visual-tree

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

更新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类可用于扫描可视树。

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

更新3

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

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

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

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

2 个答案:

答案 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();
        }));
    }
}