4

I have an Attached Property in a WPF app.

The code below is inside the OnLoad event, but it doesn't work unless I add a hacky 500 millisecond delay in.

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

As per the answer from @Will, "use the dispatcher and select the appropriate priority". 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

If using DevExpress, the LayoutTreeHelper class is useful for scanning up and down the visual tree.

For sample code that deals with scanning up and down the visual tree, see:

Update 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. 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.

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.

For example:

  • Calling window.Show() before the Loaded event has resulted in a crash in some circumstances.
  • Hooking into the event handler for IsVisibleChanged before the Loaded event has result in a crash in some circumstances.

Disclaimer: I am not associated with DevExpress, it one of many good WPF libraries, I also recommend Infragistics.

4

2 回答 2

2

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

如何获取 UI 线程调度程序?

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

于 2016-03-16T14:15:05.340 回答
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();
        }));
    }
}
于 2016-04-19T14:02:13.317 回答