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