Reputation: 80252
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;
});
}
});
}
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);
}
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:
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:
window.Show()
before the Loaded
event has resulted in a crash in some circumstances.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.
Upvotes: 4
Views: 2221
Reputation: 5894
I prefer this version because it works nicer with ReSharper (suggest method stubs) and takes parameters in order of precedence.
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();
}));
}
}
Upvotes: 0
Reputation:
If you want to wait to do something in the UI thread, use the Dispatcher. How do you snag it? That's a dupe.
How do I get the UI thread Dispatcher?
You can use the DispatcherPriority to select a time in the future. Priorities are based on UI activities, so you can't say, for example, next week. But you can say "let me wait until after bindings have been processed", for example.
Upvotes: 2