C# 自定义控件锁定表单UI中需要调用
我有一个自定义控件。我正在更改它的图像不透明度,然后加载其他图像。我在控件中使用C# 自定义控件锁定表单UI中需要调用,c#,multithreading,C#,Multithreading,我有一个自定义控件。我正在更改它的图像不透明度,然后加载其他图像。我在控件中使用invokererequired,它按预期工作。 但是,当我在窗体上添加控件时,直到我的不透明度更改完成,窗体才会响应。 尽管我选中了invokererequired,为什么我的控件要锁定表单 我的项目提交代码 我的自定义控件代码 void ChangeImageOpacity(int opacity,string image_path) { if (this.InvokeRequired)
invokererequired
,它按预期工作。
但是,当我在窗体上添加控件时,直到我的不透明度更改完成,窗体才会响应。
尽管我选中了invokererequired
,为什么我的控件要锁定表单
我的项目提交代码
我的自定义控件代码
void ChangeImageOpacity(int opacity,string image_path)
{
if (this.InvokeRequired)
{
this.Invoke(new Action<int, string>(ChangeImageOpacity), new object[] { opacity, image_path });
}
else
{
this.Image = ImageOperation.ChangeImageOpacity(image_path, opacity);
Application.DoEvents();
}
}
void ChangeImageOpacityStarter(PictureBoxMode mode)
{
if(mode==PictureBoxMode.OPENED)
{
for (int i = 80; i >= 0; i-=5)
{
ChangeImageOpacity(i, string.Format(@"icons\{0}.png",0));
Thread.Sleep(20);
}
for (int i = 0; i < 80; i += 5)
{
ChangeImageOpacity(i, string.Format(@"icons\{0}.png", this.ImageID));
Thread.Sleep(20);
}
}
else if(mode==PictureBoxMode.OPENED_TO_CLOSING)
{
for (int i = 100; i >= 0; i--)
{
ChangeImageOpacity(i, string.Format(@"icons\{0}.png", this.imageID));
}
for (int i = 0; i < 100; i++)
{
ChangeImageOpacity(i, string.Format(@"icons\{0}.png", 0));
}
}
else if(mode==PictureBoxMode.DESTROY)
{
for (int i = 100; i >= 0; i--)
{
ChangeImageOpacity(i, string.Format(@"icons\{0}.png", this.imageID));
}
}
}
void ChangeImageOpacity(整数不透明度,字符串图像\u路径)
{
if(this.invokererequired)
{
Invoke(新操作(ChangeImageOpacity),新对象[]{opacity,image_path});
}
其他的
{
this.Image=ImageOperation.ChangeImageOpacity(图像路径,不透明度);
Application.DoEvents();
}
}
void ChangeImageOpacityStarter(PictureBoxMode模式)
{
如果(模式==PictureBoxMode.OPENED)
{
对于(int i=80;i>=0;i-=5)
{
ChangeImageOpacity(i,string.Format(@“icons\{0}.png”,0));
睡眠(20);
}
对于(int i=0;i<80;i+=5)
{
ChangeImageOpacity(i,string.Format(@“icons\{0}.png”,this.ImageID));
睡眠(20);
}
}
else if(mode==PictureBoxMode.OPENED\u TO\u CLOSING)
{
对于(int i=100;i>=0;i--)
{
ChangeImageOpacity(i,string.Format(@“icons\{0}.png”,this.imageID));
}
对于(int i=0;i<100;i++)
{
ChangeImageOpacity(i,string.Format(@“icons\{0}.png”,0));
}
}
else if(mode==PictureBoxMode.DESTROY)
{
对于(int i=100;i>=0;i--)
{
ChangeImageOpacity(i,string.Format(@“icons\{0}.png”,this.imageID));
}
}
}
将Invoke更改为BeginInvoke,以便它不会锁定您的UI:
void ChangeImageOpacity(int opacity,string image_path)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new Action<int, string>(ChangeImageOpacity), new object[] { opacity, image_path });
}
else
{
this.Image = ImageOperation.ChangeImageOpacity(image_path, opacity);
Application.DoEvents();
}
}
void ChangeImageOpacity(整数不透明度,字符串图像\u路径)
{
if(this.invokererequired)
{
this.BeginInvoke(新操作(ChangeImageOpacity),新对象[]{opacity,image_path});
}
其他的
{
this.Image=ImageOperation.ChangeImageOpacity(图像路径,不透明度);
Application.DoEvents();
}
}
这应该是显而易见的。在执行不透明动画时,您将占用UI线程,并阻止它。一个线程在同一时间只能做一件事,所以要做除动画以外的任何事情,您需要暂时放弃工作,以允许其他UI操作发生
您的应用程序也会显示为“无响应”,在无效时不会重新呈现,对吗?在旧窗口上,您甚至无法移动窗口。同样,原因是相同的-您没有抽取消息队列,因此无法处理诸如WM_PAINT
或WM_MOVE
之类的windows消息
解决方案是确保在制作动画时没有阻塞UI。这是很棘手的,尤其是如果你想让它看起来平滑。您可以使用wait Task.Delay
而不是Thread.Sleep
,或计时器,或后台工作人员
或
作为一个非常粗糙的解决方案,您可以在每个线程之后执行应用程序.DoEvents
。Sleep
(或ChangeImageOpacity
),这有点类似于使用Wait
,允许抽取和执行消息队列。不过,它还允许代码执行交错,这可能非常难以安全处理
您似乎不太了解
Invoke
或InvokeRequired
是如何工作的-这是很明显的,因为您首先使用的是InvokeRequired
:)Invoke
只是将要在UI线程上执行的操作排队,然后等待它完成执行。无论您是从不同的线程执行(因此invokererequired
为true
并调用Invoke
)还是不执行(因此invokererequired
为false
并直接调用该方法),最终结果都是在休眠1600ms的UI线程上执行代码,做一些额外的CPU工作来引导。在它完成执行之前,表单不会发生任何其他事情(除了Windows现在所做的使内容看起来更具响应性(即使编码错误)。使用这个。BeginInvoke?ImageOperation.ChangeImageOpacity做什么?这是一个耗时的过程吗?还有,为什么要使用DoEvents
和Sleep
?它正在将picturebox当前图像更改为新图像。在当前图像更改期间,我增加了一个图像不透明度。我正在这样的游戏中编写一个图像匹配()在类似代码MyPictureBox=sender as MyPictureBox的控件上添加控件;box.BoxMode=PictureBoxMode.OPENED;是的,你说得对。它已成功渲染,但表单无法移动。当我在自定义控件中使用begininvoke时,我在想这足以避免交叉线程异常。所以它可以是izolated uıthread,但它是以我的形式创建的,并添加到tableadapter中。所以这取决于uıthread。我说得对吗?@IbrahimArac是的,除了UI线程之外,你不能从任何地方更新UI——这包括将数据绑定到数据表之类的东西;如果更改数据表导致UI更改,则必须从UI线程执行。这是一个很好的理由,你真的想在UI和你正在做的任何事情之间进行很多解耦。我正在与一个项目合作,该项目将UI和非UI工作进行这种特殊的混合,修复这种随意多线程产生的所有(被忽略的)错误是一件非常痛苦的事情。