C# 如果字符串太长,则在Onpaint()块UI线程中绘制字符串

C# 如果字符串太长,则在Onpaint()块UI线程中绘制字符串,c#,multithreading,winforms,C#,Multithreading,Winforms,我在winforms项目的表单中添加了两个UserControls,一个用于绘制字符串并使其从右向左滚动,另一个用于绘制另一个字符串并使其从下到上反复滚动,通过在while(true)循环中调用Invalidate()进行控制。但是当我的一个字符串变得太长时,UI线程被阻塞了大约1000个字符,所以我的问题是:我做错了什么?有没有更好的方法使文本滚动??? 下面是我的代码片段: int scrollTextSpeed = 100; bool scrollingText = true; Th

我在winforms项目的表单中添加了两个UserControls,一个用于绘制字符串并使其从右向左滚动,另一个用于绘制另一个字符串并使其从下到上反复滚动,通过在while(true)循环中调用Invalidate()进行控制。但是当我的一个字符串变得太长时,UI线程被阻塞了大约1000个字符,所以我的问题是:我做错了什么?有没有更好的方法使文本滚动??? 下面是我的代码片段:

 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;
        }
    }
int scrollTextSpeed=100;
bool scrollingText=true;
线程更新;
void init(){
updateUI=新线程(updateScrollText);
updateUI.Start();
}
void updateScrollText()
{
while(true)
{
如果(滚动文本){
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal,
新MethodInvoker(Invalidate));
Thread.Sleep(滚动文本速度);
}
}
}
受保护的覆盖无效OnPaint(System.Windows.Forms.PaintEventArgs e)
{
SizeF stringSize=e.Graphics.MeasureString(文本,this.Font);
var yPos=(this.ClientSize.Height/2)-(stringSize.Height/2);
e、 Graphics.TextRenderingHint=TextRenderingHint.antialas;
e、 Graphics.DrawString(文本,此.Font,brus,
当前位置(YPO);
如果(第一时间)
{
currentPos=this.ClientSize.Width-1;
第一时间=假;
}
其他的
{
如果(当前位置<(-1*(stringSize.Width)))
currentPos=this.ClientSize.Width-1;
其他的
currentPos-=滚动像素距离;
}
}

如评论中所述,我无法重现UI线程阻塞的问题。然而,我认为您应该重构这部分代码,这可能也会解决阻塞UI的问题

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

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

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

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

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