等待控件布局完成
我正在将大量富文本加载到 RichTextBox
(WPF) 中,并且我想滚动到内容末尾:
richTextBox.Document.Blocks.Add(...)
richTextBox.UpdateLayout();
richTextBox.ScrollToEnd();
这不起作用,ScrollToEnd
在以下情况下执行布局尚未完成,因此它不会滚动到末尾,而是滚动到文本的前三分之一左右。
有没有办法强制等待 RichTextBox
完成其绘制和布局操作,以便 ScrollToEnd
实际上滚动到文本的末尾?
谢谢。
不起作用的东西:
编辑: 我已经尝试过 LayoutUpdated
事件,但它立即被触发,同样的问题:当它被触发时,控件仍然在 Richtextbox 内布局更多文本,所以即使是 ScrollToEnd
也没有工作... 我尝试了这个:
richTextBox.Document.Blocks.Add(...)
richTextBoxLayoutChanged = true;
richTextBox.UpdateLayout();
richTextBox.ScrollToEnd();
在 richTextBox.LayoutUpdated
事件处理程序中:
if (richTextBoxLayoutChanged)
{
richTextBoxLayoutChanged = false;
richTextBox.ScrollToEnd();
}
该事件被正确触发,但太快了,richtextbox 在触发时仍在添加更多文本,布局尚未完成,因此 ScrollToEnd< /code> 再次失败。
编辑2: 按照 dowhilefor 的回答:MSDN on InvalidateArrange 说
失效后,元素的布局将更新,这将 异步发生,除非随后被 UpdateLayout 强制。
但甚至
richTextBox.InvalidateArrange();
richTextBox.InvalidateMeasure();
richTextBox.UpdateLayout();
不等待:在这些调用之后,richtextbox 仍在添加更多文本并将其异步放置在自身内部。 ARG!
I am loading quite a lot of rich text into a RichTextBox
(WPF) and I want to scroll to the end of content:
richTextBox.Document.Blocks.Add(...)
richTextBox.UpdateLayout();
richTextBox.ScrollToEnd();
This doesn't work, ScrollToEnd
is executed when the layout is not finished, so it doesn't scroll to the end, it scrolls to around the first third of the text.
Is there a way to force a wait until the RichTextBox
has finished its painting and layout operations so that ScrollToEnd
actually scrolls to the end of the text?
Thanks.
Stuff that doesn't work:
EDIT:
I have tried the LayoutUpdated
event but it's fired immediately, same problem: the control is still laying out more text inside the richtextbox when it's fired so even a ScrollToEnd
there doesn't work...
I tried this:
richTextBox.Document.Blocks.Add(...)
richTextBoxLayoutChanged = true;
richTextBox.UpdateLayout();
richTextBox.ScrollToEnd();
and inside the richTextBox.LayoutUpdated
event handler:
if (richTextBoxLayoutChanged)
{
richTextBoxLayoutChanged = false;
richTextBox.ScrollToEnd();
}
The event is fired correctly but too soon, the richtextbox is still adding more text when it's fired, layout is not finished so ScrollToEnd
fails again.
EDIT 2:
Following on dowhilefor's answer: MSDN on InvalidateArrange says
After the invalidation, the element will have its layout updated, which will
occur asynchronously unless subsequently forced by UpdateLayout.
Yet even
richTextBox.InvalidateArrange();
richTextBox.InvalidateMeasure();
richTextBox.UpdateLayout();
does NOT wait: after these calls the richtextbox is still adding more text and laying it out inside itself asynchronously. ARG!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我遇到过一个相关的情况:我有一个打印预览对话框,可以创建精美的渲染效果。通常,用户会单击一个按钮来实际打印它,但我也想用它来保存图像而无需用户参与。在这种情况下,创建图像必须等到布局完成。
我使用以下方法进行了管理:
关键是 DispatcherPriority.ContextIdle,它会等待后台任务完成。
编辑:根据扎克的要求,包括适用于这种特定情况的代码:
我应该注意到,我对这个解决方案并不满意,因为它感觉非常脆弱。然而,它似乎确实适用于我的具体情况。
I have had a related situation: I have a print preview dialog that creates a fancy rendering. Normally, the user will click a button to actually print it, but I also wanted to use it to save an image without user involvement. In this case, creating the image has to wait until the layout is complete.
I managed that using the following:
The key is the
DispatcherPriority.ContextIdle
, which waits until background tasks have completed.Edit: As per Zach's request, including the code applicable for this specific case:
I should note that I'm not really happy with this solution, as it feels incredibly fragile. However, it does seem to work in my specific case.
特别看看 UpdateLayout
:
因此根据您的需要调用 InvalidateMeasure 或 InvalidateArrange 应该可行。
但考虑到你的代码。我认为这行不通。许多 WPF 加载和创建都被延迟,因此向 Document.Blocks 添加某些内容并不一定会直接更改 UI。但我必须说,这只是一个猜测,也许我是错的。
Have a look at UpdateLayout
especially:
So calling InvalidateMeasure or InvalidateArrange, depending on your needs should work.
But considering your piece of code. I think that won't work. Alot of WPF loading and creating is deffered, so adding something to Document.Blocks does not necesarilly change the UI directly. But i must say, this is just a guess and maybe i'm wrong.
您应该能够使用 Loaded 事件
如果您多次执行此操作,那么您应该查看 LayoutUpdated 事件
you should be able to use the Loaded event
if you are doing this more then one time, then you should look at the LayoutUpdated event
使用 .net 4.5 或 async blc 包,您可以使用以下扩展方法
With .net 4.5 or the async blc package you can use the following extension method
@Andreas 的回答效果很好。
但是,如果控件已经加载怎么办?该事件永远不会触发,并且等待可能会永远挂起。要解决此问题,如果表单已加载,请立即返回:
其他提示
这些提示可能有用,也可能没用。
上面的代码在附加属性中非常有用。附加属性仅在值更改时才会触发。当切换附加属性来触发它时,使用
task.Yield()
将调用置于调度程序队列的后面:上面的代码在附加属性中非常有用。当切换附加属性来触发它时,您可以使用调度程序,并将优先级设置为
Loaded
:The answer by @Andreas works well.
However, what if the control is already loaded? The event would never fire, and the wait would potentially hang forever. To fix this, return immediately if the form is already loaded:
Additional hints
These hints may or may not be useful.
The code above is really useful in an attached property. An attached property only triggers if the value changes. When toggling the attached property to trigger it, use
task.Yield()
to put the call to the back of the dispatcher queue:The code above is really useful in an attached property. When toggling the attached property to trigger it, you can use the dispatcher, and set the priority to
Loaded
:尝试添加 richTextBox.ScrollToEnd();调用 RichTextBox 对象的 LayoutUpdated 事件处理程序。
Try adding your richTextBox.ScrollToEnd(); call to the LayoutUpdated event handler of your RichTextBox object.
试试这个:
Try this:
对我的 WPF 项目有效的唯一(kludge)解决方案是启动一个单独的线程,该线程休眠一段时间,然后要求滚动到末尾。
重要的是不要尝试在主 GUI 上调用这个“休眠”线程,以免用户被暂停。因此,调用一个单独的“休眠”线程并定期在主 GUI 线程上调用 Dispatcher.Invoke 并要求滚动到末尾。
运行完美,用户体验也不错:
并且
The only (kludge) solution that worked for my WPF project was to fire off a separate thread that slept for a time and then asked to scroll to the end.
It is important to not try to invoke this "sleepy" thread on the main GUI, lest the user would be paused. Therefore, invoke a separate "sleepy" thread and periodically Dispatcher.Invoke on the main GUI thread and ask to scroll to the end.
Works perfectly and the user experience is not terrible:
and