WPF:在附件 属性 中,如何等待可视化树正确加载?
WPF: In an attached property, how to wait until visual tree loaded properly?
我在 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 的回答,"use the dispatcher and select the appropriate priority"。这非常有效:
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 class 可用于上下扫描可视化树。
有关处理可视化树上下扫描的示例代码,请参阅:
- DevExpress: In an attached property, how to wait until we are 100% sure that DevExpress has finished building its Visual Tree?
- DevExpress and How to ensure that a LayoutPanel is always in front of everything else?
更新 3
如果您在附加的 属性 中,可靠加载可视化树的唯一方法是在 Loaded
事件处理程序中或之后执行代码。如果我们没有意识到这个限制,那么一切都会间歇性地失败。如上所述,等待 OnLoaded
事件触发远优于任何其他试图引入其他随机形式延迟的方法。
如果您使用的是 DevExpress,这就更重要了:在某些情况下,在 Loaded
事件之前尝试做任何事情都可能导致崩溃。
例如:
- 在
Loaded
事件之前调用 window.Show()
在某些情况下会导致崩溃。
- 在
Loaded
事件之前连接到 IsVisibleChanged
的事件处理程序在某些情况下会导致崩溃。
免责声明:我与 DevExpress 无关,它是许多优秀的 WPF 库之一,我也推荐 Infragistics。
如果您想等待在 UI 线程中执行某些操作,请使用 Dispatcher。你怎么抓住它?那是个骗子。
How do I get the UI thread Dispatcher?
您可以使用DispatcherPriority到select以后的某个时间。优先级基于 UI 项活动,因此您不能说例如下周。但是你可以说 "let me wait until after bindings have been processed",例如
我更喜欢这个版本,因为它可以更好地与 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();
}));
}
}
我在 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 的回答,"use the dispatcher and select the appropriate priority"。这非常有效:
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 class 可用于上下扫描可视化树。
有关处理可视化树上下扫描的示例代码,请参阅:
- DevExpress: In an attached property, how to wait until we are 100% sure that DevExpress has finished building its Visual Tree?
- DevExpress and How to ensure that a LayoutPanel is always in front of everything else?
更新 3
如果您在附加的 属性 中,可靠加载可视化树的唯一方法是在 Loaded
事件处理程序中或之后执行代码。如果我们没有意识到这个限制,那么一切都会间歇性地失败。如上所述,等待 OnLoaded
事件触发远优于任何其他试图引入其他随机形式延迟的方法。
如果您使用的是 DevExpress,这就更重要了:在某些情况下,在 Loaded
事件之前尝试做任何事情都可能导致崩溃。
例如:
- 在
Loaded
事件之前调用window.Show()
在某些情况下会导致崩溃。 - 在
Loaded
事件之前连接到IsVisibleChanged
的事件处理程序在某些情况下会导致崩溃。
免责声明:我与 DevExpress 无关,它是许多优秀的 WPF 库之一,我也推荐 Infragistics。
如果您想等待在 UI 线程中执行某些操作,请使用 Dispatcher。你怎么抓住它?那是个骗子。
How do I get the UI thread Dispatcher?
您可以使用DispatcherPriority到select以后的某个时间。优先级基于 UI 项活动,因此您不能说例如下周。但是你可以说 "let me wait until after bindings have been processed",例如
我更喜欢这个版本,因为它可以更好地与 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();
}));
}
}