【问题标题】:Draw string in Onpaint() block UI thread if string is too long如果字符串太长,则在 Onpaint() 块 UI 线程中绘制字符串
【发布时间】:2017-10-26 08:47:51
【问题描述】:

我有两个用户控件,它们在我的 winforms 项目中添加到一个表单中,一个具有绘制字符串并使其从右向左滚动的功能,另一个绘制另一个字符串并使其从下到上滚动并且通过在 while(true) 循环内调用 Invalidate() 再次控制。但是当我的字符串之一变得太长时,大约 1000 个字符的 UI 线程被阻塞,所以我的问题是:我做错了什么?有没有更好的方法让文本滚动??? 下面是我的sn-p代码:

 int scrollTextSpeed = 100;
 bool scrollingText = true;
 Thread updateUI ;

 void init(){
     updateUI = new Thread(updateScrollText);
     updateUI.Start();
 }

 void updateScrollText()
    {
        while (true)
        {
            if (scrollingText) {

              Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal,
              new MethodInvoker(Invalidate));
              Thread.Sleep(scrollTextSpeed);
            }
        }
    }


 protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
    {

        SizeF stringSize = e.Graphics.MeasureString(text, this.Font);

        var yPos = (this.ClientSize.Height / 2) - (stringSize.Height / 2);
        e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
        e.Graphics.DrawString(text, this.Font, brus,
        currentPos, yPos);
        if (fisttime)
        {
            currentPos = this.ClientSize.Width - 1;
            fisttime = false;
        }
        else
        {
            if (currentPos < (-1 * (stringSize.Width)))
                currentPos = this.ClientSize.Width - 1;
            else
                currentPos -= scrollPixelDistance;
        }
    }

【问题讨论】:

  • 不要在繁忙的循环中失效。无关紧要。计时器存在。如果字符串没有变化,那么每次测量字符串也没有意义。也不要每次都画,因为你可以把它保存为位图
  • 为什么要画文字?为什么不使用Label
  • 我将您的代码复制粘贴到一个新的 WinForms 项目中,并使其编译 - 它一直滚动 50000 个字符的字符串,并且表单仍然响应用户输入。
  • 另外,想想如果scrollingText 变成false,您的while (true) 循环会发生什么...(不是这会导致您的问题,但会导致其他问题。)跨度>
  • 很难猜测 Dispatcher.CurrentDispatcher 是如何在 Winforms 应用程序中结束的。这是不正确的,它不知道哪个线程是正确的 UI 线程。死锁当然并不罕见。请改用 Control.BeginInvoke()。您需要一个 Form 对象,如果您很绝望,请使用 Application.OpenForms[0]。请注意滚动速度,如果 UI 线程跟不上,那么它就会开始消耗 100% 内核,这看起来也像是“冻结”。

标签: c# multithreading winforms


【解决方案1】:

如 cmets 中所述,我无法重现 UI 线程阻塞的问题。但是我认为你应该重构这部分代码,这可能也会解决阻塞 UI 的问题。

目前,您的滚动速度直接取决于 UI 线程何时决定对 Invalidate() 请求采取行动。此外,您指出您将拥有多个滚动文本,因此为这些文本配置不同的滚动速度几乎是不可能的。

通过一些数学运算,您可以在任何给定时间确定滚动文本的位置,即:

float x = ClientRectangle.Width - (((uint)Environment.TickCount / 40f) % 
        (stringSize.Width + ClientRectangle.Width));

现在滚动位置不再取决于刷新率,您将不再需要多个计时器。使用简单的System.Windows.Forms.Timer,您就不必从另一个线程调用任何内容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多