在WPF中,是否有一个“渲染完成”事件?

6
在 WPF 中,当我加载大量元素的包装面板时,窗口在显示内容之前会暂停一段时间。我想添加等待提示,但是我找不到一种方法来检测包装面板何时完成呈现。
我已经尝试过 "loaded"、"sizechanged"、"initialized" 等方法,但都没有成功。有人对此问题有什么想法吗?
非常感谢!

它在同一个线程上,所以你不需要事件。当你的代码达到加载数据的那一行之后,它就会完成。 - FCin
1
@FCin 谢谢你。但它的表现并不像那样。在将200个元素加载到wrappanel中后,代码会返回,窗口会停顿,大约需要1~2秒钟才能让窗口重新恢复响应。 - captainst
可能绑定仍在进行中,您可以检查线程是否为ApplicationIdle以确定处理何时完成。 - Kevin Cook
如何处理WrapPanel中最后一个元素的Loaded事件? - mm8
2
这似乎是WPF中的一个严重bug。您可以更改UI渲染的DispatcherPriority,以便进行惰性加载并且不会锁定屏幕,但是如果这样做,就无法知道UI何时完成渲染。 - Christian Findlay
在我的情况下,它运行得非常好。https://dev59.com/THRA5IYBdhLWcg3wyRJ7#63259032 - charles kim
3个回答

18
在开始渲染时,使用Dispatcher的优先级为ContextIdleDispatcher.BeginInvoke调用。
我的渲染开始正好是treeview选定项更改事件,但这可以是您需要等待UI完成更新的任何事件。
    private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        Dispatcher.BeginInvoke(new Action(() => DoSomething()), DispatcherPriority.ContextIdle, null);
    }

    private void DoSomething()
    {
       //This will get called after the UI is complete rendering
    }

我有一个ItemsControl,我用很多项来填充它,这需要时间。与ItemsControl的OnSizeChanged一起使用可以完美地解决这个问题。谢谢! - jeppe

10

可以使用Window.ContentRendered 事件


不知道那个。很好! - PilouPili

1
你可以重写 OnRender 方法来检测渲染是否完成。
要推送所有事件,请调用 Dispatcher.DoEvents(),其中 DoEvents 被实现为扩展方法:
public static class DispatcherExtensions
{
    public static void DoEvents(this Dispatcher dispatcher, DispatcherPriority priority = DispatcherPriority.Background)
    {
        DispatcherFrame frame = new DispatcherFrame();
        DispatcherOperation dispatcherOperation = dispatcher.BeginInvoke(priority, (Action<DispatcherFrame>)ExitFrame, frame);
        Dispatcher.PushFrame(frame);

       if (dispatcherOperation.Status != DispatcherOperationStatus.Completed)
           dispatcherOperation.Abort();
   }

    private static void ExitFrame(DispatcherFrame frame)
    {
        frame.Continue = false;
    }

    public static void Flush(this Dispatcher dispatcher, DispatcherPriority priority)
    {
        dispatcher.Invoke(()=> { }, priority);
    }

}

回顾过去,我认为使用这个是一个糟糕的想法,因为它可能会导致难以解决的错误。

// this shows how bad it is to call Flush or DoEvents
int clicker = 0;
private void OnClick(object sender, RoutedEventArgs e)
{
    if (clicker != 0)
    {
        // This is reachable... // Could be skipped for DispatcherPriority.Render but then again for render it seems to freeze sometimes.
    }
    clicker++;
    Thread.Sleep(100);
    this.Dispatcher.Flush(DispatcherPriority.Input);

    //this.Dispatcher.DoEvents(DispatcherPriority.Render);
    //this.Dispatcher.DoEvents(DispatcherPriority.Loaded);
    //this.Dispatcher.DoEvents(DispatcherPriority.Input);
    //this.Dispatcher.DoEvents(DispatcherPriority.Background);
    clicker--;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接