WinForm UI-Thread UI-Painting-Behavior [英] WinForm UI-Thread UI-Painting-Behaviour

查看:69
本文介绍了WinForm UI-Thread UI-Painting-Behavior的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:请重新阅读





你好CPians的朋友们!



我刚回答了典型的为什么我的UI在我的方法运行时没有更新 - 问题。但是,我做了一个快速测试项目,我很困惑地发现似乎发生了一些更新。想象一下带有Button,ProgressBar,ListBox和这种复杂方法的System.Windows.Forms.Form:

edited: please re-read


Hello fellow CPians!

I just answered the typical "why does my UI not update while my method is running"-Question. However, I did a quick test project and I was baffled to find out that there seems to be "some" updating happening. Imagine a System.Windows.Forms.Form with a Button, a ProgressBar, a ListBox and this sophisticated method:

private void button1_Click(object sender, EventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        progressBar1.Value = i;
        // alternatively, instead of the line above:
        listBox1.Items.Add(i);

        Thread.Sleep(100);
        // alternatively, instead of Sleep, with same result:
        for (int x = 0; x < 2000000000; x++);
    }
}



代码中没有其他任何内容;没有调用DoEvents()或其他什么。



我会想到在它运行时根本没有UI更新但是:



ProgressBar实际上确实更新了。当试图移动窗口时,标题中出现通常的(无响应),并且对ProgressBar的可视更新停止。有时这也会在几秒钟后自行(不试图移动窗口)发生。



ListBox的垂直滑块确实更新,其行为与ProgressBar相同。但是这些项只有在方法执行后才会显示。



问题:如果UI线程忙于执行上述方法,为什么首先是对ProgressBar / ListBox的可视化更新,为什么在尝试移动窗口时或有时单独停止时会停止?为什么ListBox的项目绘制与滑块不同?


Nothing else is happening in the code; no calls to DoEvents() or whatever.

I would have imagined that there's no UI update at all while it's running but:

The ProgressBar actually does update. When trying to move the window the usual "(Not Responding)" appears in the caption and the visual update to the ProgressBar stops. Sometimes this also happens "on its own" (without trying to move the window) after some seconds.

The ListBox's vertical slider does update with the same behaviour as the ProgressBar. But the items only become visible after the method has executed.

Question: If the UI thread is busy executing the above method, why is there at first a visual update to the ProgressBar/ListBox and why does this stop when trying to move the window or sometimes on its own? And why is the painting of the items of the ListBox a different thing than its slider?

推荐答案

这是我找到的:



将此方法添加到表单中:

Here is what I found:

Add this method to the form:
protected override void WndProc(ref Message m) {
    base.WndProc(ref m);
}





最大化(在进度条和周期),给自己足够的时间。



按下按钮后,在第一个语句 base.WndProc(ref m)处设置一个断点。您将看到消息已被泵送。当你用,例如, ListBox 操作取代进度条 Value 的赋值时,就不会发生这种情况。在我对这个问题的评论中建议。



所以,这就是我如何解释它:这个属性赋值做了一些特别的事情。什么?下一步,如果调用 Application.DoEvents ,我会检查它。不,它没有。我通过更新 ListBox ProgressBar 进行了检查。列表视图仍未呈现。当您使用 Application.DoEvents (已注释掉)时,您可以看到列表框项目已呈现,正如我所料。



Make maximum big enough (on both progress bar and cycle), to give youself enough time.

After button press, put a break point at the first statement base.WndProc(ref m). You will see that the messages are pumped. And it won't happen when you replace assignment of the progress bar Value with, say, ListBox operations, as I suggested in my comment to the question.

So, here is how I can explain it: this property assignment does something special. What? On a next step, I checked up it if calls Application.DoEvents. No, it doesn't. I checked it up by updating both ListBox and ProgressBar. List view is still not rendered. When you use, for comparison, Application.DoEvents (commented out), you can see that list box items are rendered, as I expected.

using System;
using System.Windows.Forms;
using System.Threading;

public partial class MainForm {

    ProgressBar pb = new ProgressBar();
    Button btn = new Button();
    ListBox lb = new ListBox();
    const int max = 100;

    public MainForm() {
        btn.Dock = DockStyle.Top;
        pb.Dock = DockStyle.Bottom;
        pb.Maximum = max;
        lb.Dock = DockStyle.Right;
        btn.Parent = this;
        pb.Parent = this;
        lb.Parent = this;
        btn.Click += (sender, eventArgs) => {
            for (int i = 0; i < max; i++) {
                lb.Items.Add(i);
                pb.Value = i;
                Thread.Sleep(100);
                //Application.DoEvents();
            }
        };
    } //MainForm

    protected override void WndProc(ref Message m) {
        base.WndProc(ref m); // put a break point here,
                             // after clicking the button
    } //WndProc

} //class MainForm



所以,我可以得出结论 ProgressBar.Value 赋值,除了失效,通过消息队列抽取特定消息。可能它只抽取特定于 ProgressBar 的消息。到底是什么?从那时起,我对问题失去了兴趣:如果你监视所有的消息,或者类似的东西,你可以找到更多细节。无论如何,我现在看不到任何奇迹。顺便说一句,有关间谍的示例代码可以在我的文章中找到一个完全不同的主题: 动态方法调度程序







和是,我们发现滚动条是刷新的,与 TreeView 内容相反。它可以用同样的方式解释,如果不是一个问题:我无法观察消息泵送。无论如何,你的问题只是关于 ProgressBar 。对其他一些案件可以进行更多的调查。 ;-)



[结束编辑]



现在,问题是:为什么会这样?我推测这个功能可以解决缓解问题的问题:进度条的设计方式可以无论什么时候刷新,不管用什么样的误用都可以。这个想法怎么样?



-SA


So, I can make a conclusion that the ProgressBar.Value assignment, in addition to invalidation, does something to pump specific messages through the message queue. Probably, it pumps only the messages specific to ProgressBar. What exactly? Since this moment, I lost interest to the "problem": you can find out further detail if you spy on all messages, or something like that. Anyway, I cannot see anything miraculous now. By the way, the sample code for effective spying could be found in my article on a completely different topic: Dynamic Method Dispatcher.



And yes, we observed that the scroll bar is refreshed, in contrast to the TreeView content. It could be explained in the same way, if not one problem: I could not observe message pumping. Anyway, your question was about ProgressBar only. Some more investigation could be done for some other cases. ;-)

[END EDIT]

Now, the question is: why is it so? I would speculate that this functionality addresses the problem of alleviating lamers' problems: the progress bar could be designed the way it "refreshes no matter what", not matter what kind of misuse one could do to it. How about this idea?

—SA


这篇关于WinForm UI-Thread UI-Painting-Behavior的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆