WPF 控制问题

发布于 2024-09-07 10:11:16 字数 271 浏览 4 评论 0原文

我面临一个问题,其中 WPF 应用程序无法更新进度栏,甚至 wpf 窗口上的按钮也停止发光,直到我移动窗口,然后一切都很好。

我每次更新进度条时都尝试调用 InvalidateRect,这实际上生成了 WM_PAINT 消息,但没有解决问题。这个问题是随机的,有时发生,有时不发生。

我的应用程序在后台使用 DirectShow 和增强视频渲染来进行视频渲染。如果我用 VMR 替换增强型视频渲染器,一切都会正常。

如果有人能帮助我,我将不胜感激。

谢谢 索拉博。

I am facing one issue in which WPF application unable to update the progress bar even the button on the wpf window stop glowing untill i move the window then everything is fine.

I have try to call InvalidateRect everytime i update the progress bar which actually generate the WM_PAINT message but doesn't solve the issue. This issue is random some time happend some time doesn't happend.

My application is using DirectShow at the backened with Enhanced Video Rendering for video rendering. If i replace the Enhanced Video Renderer with VMR everything works fine.

I will appreciate if any one can help me .

Thanks
Sohrab.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

雪若未夕 2024-09-14 10:11:16

我明白您为什么尝试 WM_PAINT 来解决您的问题。然而,WM_PAINT 实际上与 WPF 渲染完全无关。

基本上,当 Window(或 HwndSource、ElementHost 等)首次显示在屏幕上时,WPF 是如何进行渲染的:

  1. Window 对象的 Visibility 设置为“Visible”
  2. Win32 窗口 (hWnd) 被分配并显示,但它的 WM_PAINT 被忽略
  3. 分配 Direct3D Surface 来覆盖 Win32 窗口区域
  4. 在 DispatcherPriority.Render 调度 Dispatcher 回调
  5. 当 Dispatcher 回调触发时,将测量并排列窗口的可视树
  6. 在排列过程中,每个控件都会生成绘图指令绘制自身并将其存储在 DrawingContext 中
  7. 在排列过程结束时,WPF 读取 DrawingContext 数据并使用 Direct3D 更新屏幕

从那时起,每当进行影响显示的属性更改时,步骤 4-7 的相关部分以极其有效的增量方式重复,仅更新需要更新的内容。

这里需要注意的关键是,任何时候需要完成渲染工作,都会安排调度程序回调来完成它。这意味着只要满足以下条件,您的 UI 就会继续渲染:

  1. 调度程序正在处理其队列,并且
  2. 您没有在渲染优先级或更高级别上连续添加调度程序操作

此外,输入事件的处理发生在输入优先级,因此您需要如果您希望鼠标悬停在按钮上,至少允许调度程序执行至输入优先级。

由于 WPF 的工作方式,发送 WM_PAINT 根本不会执行任何操作。您想要做的是让调度程序有机会处理其队列。通常,这是通过从 UI 线程调用 Dispatcher.Invoke 并传递低于要刷新的事件的优先级来完成的,例如:

Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, new Action(() => {}));

请注意,该操作根本不执行任何操作。但是,在空操作执行之前,对 Dispatcher.Invoke 的调用无法返回,并且在所有更高优先级的操作执行之前,空操作也无法执行。因此,上面的代码具有强制渲染同步发生的效果,类似于在 Win32 中执行 SendMessage(WM_PAINT, ...) 时发生的情况。

诊断问题的一种方法是设置 DispatcherTimer 以定期写入控制台。通过以不同的优先级运行此计时器,您可以确定是什么阻碍了调度程序队列。如果任何级别都没有运行,则可能是外部锁定了 UI 线程,在这种情况下,中断进程并检查堆栈可能会有所帮助。分析工具对此来说甚至更好。在每种情况下,确定进程是正在执行代码还是正在等待锁定。如果它正在执行代码,请找出为什么该代码永远不会返回以使调度程序有机会运行。如果它正在等待锁,请找出谁持有该锁。

I understand why you tried WM_PAINT to fix your problem. However WM_PAINT really has nothing to do with WPF rendering at all.

Basically, here is how WPF does its rendering when a Window (or HwndSource, ElementHost, ...) is first shown on the screen:

  1. A Window object has its Visibility set to "Visible"
  2. A Win32 window (hWnd) is allocated and shown, but its WM_PAINT is ignored
  3. A Direct3D Surface is allocated to cover the Win32 window's area
  4. A Dispatcher callback is scheduled at DispatcherPriority.Render
  5. When the Dispatcher callback fires, the window's visual tree is measured and arranged
  6. During the arrange pass every control generates drawing instructions to draw itself and stores these in a DrawingContext
  7. At the end of the arrange pass WPF reads the DrawingContext data and uses Direct3D to update the screen

From then on, any time a property change is made that affects the display, relevant portions of steps 4-7 are repeated in an extremely efficient incremental fashion, updating only what needs to be updated.

The key thing to note here is that any time there is rendering work to be done, a Dispatcher callback is scheduled to do it. This means that your UI will continue to render as long as:

  1. The Dispatcher is processing its queue, and
  2. You aren't continuously adding dispatcher operations at or above Render priority

In addition, processing of input events happens at Input priority, so you need to at least allow the Dispatcher to execute down to Input priority if you want mouse over to work on buttons.

Because of the way WPF works, sending WM_PAINT doesn't do anything at all. What you want to do is to give the Dispatcher a chance to process its queue. Generally this is done by calling Dispatcher.Invoke from the UI thread and passing a priority lower than the events you want to flush, for example:

Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, new Action(() => {}));

Notice that the action does nothing at all. But the call to Dispatcher.Invoke cannot return until the empty action executes, and the empty action cannot execute until all higher priority actions execute. So the above code has the effect of forcing the rendering to happen synchronously, similar to what would happen when doing SendMessage(WM_PAINT, ...) in Win32.

One way do diagnose the problem is to set a DispatcherTimer to periodically write to the console. By running this timer at various priority levels you can determine what is holding up the Dispatcher queue. If nothing is running at any level it is probably something external locking up the UI thread, in which case breaking the process and checking the stack may help. A profiling tool is even better for this. In each case, determine whether the process is executing code or waiting for a lock. If it is executing code, figure out why that code never returns to give the Dispatcher a chance to run. If it is waiting for a lock, figure out who is holding that lock.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文