C#如何多线程创建MDI子窗体

问题描述

我做的东西其实就是类似多标签浏览器那样,每个子窗体都有个axWebbrowser控件,子窗体打开后就自动填表提交,如果不用多线程打开窗体,当同时打开多个子窗体的时候,主窗体的操作就会卡住。我现在用下面的代码可以用多线程打开个子窗体,但是这个子窗体不是主窗体的MDI子窗体,这不是我想要的。我就是在新线程的委托里写frmMdiChild.MdiParent=this;报错,说“线程间操作无效:从不是创建控件""的线程访问它。”。麻烦各位大侠帮忙看看,如果分不够的话我可以再加100分usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Text;usingSystem.Windows.Forms;usingSystem.Threading;usingSystem.Windows.Threading;//需引入WindowsBase组件namespaceWindowsFormsApplication1{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();}privatevoidtoolStripButton1_Click(objectsender,EventArgse){MessageBox.Show("UI线程ID:"+Thread.CurrentThread.ManagedThreadId.ToString());ThreadnewWindowThread=newThread(newThreadStart(ThreadStartingPoint));newWindowThread.SetApartmentState(ApartmentState.STA);newWindowThread.IsBackground=true;newWindowThread.Start();}///<summary>///创建子窗体的新线程///</summary>privatevoidThreadStartingPoint(){FormfrmMdiChild=newForm();frmMdiChild.Load+=newSystem.EventHandler(this.frmMdiChild_Load);//form1.MdiParent=this;MyInvokemi=newMyInvoke(setMdi);BeginInvoke(mi,(object)frmMdiChild);frmMdiChild.Show();System.Windows.Threading.Dispatcher.Run();//如果没这个子窗体打开后就马上关掉}publicdelegatevoidMyInvoke(objectobj);privatevoidsetMdi(objectform){this.textBox1.Text="我是从多线程中写入的";//可以在线程中写入主窗体的textbox1FormfrmMdiChild=(Form)form;frmMdiChild.MdiParent=this;//这里报错,去掉这个就可以了,但新窗体就不是mdi子窗体了//frmMdiChild.Owner=this;}///<summary>///子窗体的Load事件///</summary>privatevoidfrmMdiChild_Load(objectsender,EventArgse){MessageBox.Show("子窗体线程ID:"+Thread.CurrentThread.ManagedThreadId.ToString());}}}

解决方案

解决方案二:

解决方案三:
没人吗?自己顶
解决方案四:
自己顶
解决方案五:
线程之间是不能直接操作的。
解决方案六:
引用4楼huanggreat的回复:

线程之间是不能直接操作的。

所以我用了委托啊
解决方案七:
引用4楼huanggreat的回复:

线程之间是不能直接操作的。

所以我用了委托啊
解决方案八:
引用6楼seeyou8888的回复:

Quote: 引用4楼huanggreat的回复:
线程之间是不能直接操作的。

所以我用了委托啊

FormfrmMdiChild=newForm();也得放在委托里,你把这个放在线程里创建,相当于是线程创建的窗体,你要在UI线程里访问,一样的跨线程了
解决方案九:
因为网上那些杂七杂八的教程都说了这么一个概念,多线程操作控件要用委托,其实根本不是重点
解决方案十:
引用7楼xiaozhu39505的回复:

Quote: 引用6楼seeyou8888的回复:
Quote: 引用4楼huanggreat的回复:
线程之间是不能直接操作的。

所以我用了委托啊

FormfrmMdiChild=newForm();也得放在委托里,你把这个放在线程里创建,相当于是线程创建的窗体,你要在UI线程里访问,一样的跨线程了

这个我试过了,这样做就相当于子窗体还是在主线程上,axWebbrowser一打开网页主窗体又会被卡住了
解决方案十一:
引用8楼KarasCanvas的回复:

因为网上那些杂七杂八的教程都说了这么一个概念,多线程操作控件要用委托,其实根本不是重点

那重点是什么啊,我那种效果实现不了吗?
解决方案十二:
把多线程创建UI的操作串行化到主线程中去做,多线程只做一些创建UI之前耗时的数据准备操作
解决方案十三:

解决方案十四:
引用10楼seeyou8888的回复:

Quote: 引用8楼KarasCanvas的回复:
因为网上那些杂七杂八的教程都说了这么一个概念,多线程操作控件要用委托,其实根本不是重点

那重点是什么啊,我那种效果实现不了吗?

UI线程是单线程,你任何操作UI的界面的操作都需要在UI线程里执行,所以如果你UI线程里面操作太多,UI不卡不可能的,你必须把费时的跟UI无关的操作放到其他线程里执行,尽量减少在UI线程跑的代码
解决方案十五:
引用11楼wayu002的回复:

把多线程创建UI的操作串行化到主线程中去做,多线程只做一些创建UI之前耗时的数据准备操作

这个不懂,其实也没什么其他的操作,就是单单是让axWebbrowser打开一个网页,就这么简单的操作都会卡住几秒钟.这个是axWebbrowser的问题,查了网上的说要把axWebbrowser放到不同的进程上去,这样不知道能不能做成mdi子窗体的形式了
解决方案:
usingSystem.Threading;usingSystem.Windows.Forms;namespaceWindowsFormsApplication1{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();this.IsMdiContainer=true;}privatevoidaaaToolStripMenuItem_Click(objectsender,System.EventArgse){ThreadthNewForm=newThread(()=>{Formf=newForm();//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.MdiParent=this));}#ifDEBUGSystem.Console.WriteLine("created");#endif//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.Show()));}f.Text="IntheChildThread";//此句在子线程中执行});thNewForm.Start();}}}

与一切与主窗体有关的操作都需要跨线程,这个跨线程是伪的,实际上是在主线程上执行,如果没有特殊需要,多线程还是少用,毕竟由调度产生的上下文切换带来的性能损失,以及由线程创建和销毁带来的内存消耗并不低。
解决方案:
usingSystem.Threading;usingSystem.Windows.Forms;namespaceWindowsFormsApplication1{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();this.IsMdiContainer=true;}privatevoidaaaToolStripMenuItem_Click(objectsender,System.EventArgse){ThreadthNewForm=newThread(()=>{Formf=newForm();//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.MdiParent=this));}#ifDEBUGSystem.Console.WriteLine("created");#endif//新加的,测试通过f.Load+=(sender1,e1)=>{f.Text="aaa";f.Width=150;f.Height=200;};//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.Show()));}f.Text="IntheChildThread";//此句在子线程中执行});thNewForm.Start();}}}

注册了个Load事件处理,目测在子线程中执行,测试通过
解决方案:
Navigate方法是异步的,不应该卡界面才对,你给的例子里面也没用到相关控件,不知道你是如何被卡住的。
解决方案:
引用16楼Coffee_MX的回复:

usingSystem.Threading;usingSystem.Windows.Forms;namespaceWindowsFormsApplication1{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();this.IsMdiContainer=true;}privatevoidaaaToolStripMenuItem_Click(objectsender,System.EventArgse){ThreadthNewForm=newThread(()=>{Formf=newForm();//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.MdiParent=this));}#ifDEBUGSystem.Console.WriteLine("created");#endif//新加的,测试通过f.Load+=(sender1,e1)=>{f.Text="aaa";f.Width=150;f.Height=200;};//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.Show()));}f.Text="IntheChildThread";//此句在子线程中执行});thNewForm.Start();}}}

注册了个Load事件处理,目测在子线程中执行,测试通过

你这个子窗体的线程还是主线程啊MessageBox.Show("线程ID:"+Thread.CurrentThread.ManagedThreadId.ToString());你把这个代码放到主窗体和子窗体的Load事件看看,我在子窗体上放了个WebBrowser,然后打开网页,主窗体就会被卡住了//新加的,测试通过f.Load+=(sender1,e1)=>{f.Text="aaa";f.Width=150;f.Height=200;MessageBox.Show("线程ID:"+Thread.CurrentThread.ManagedThreadId.ToString());WebBrowserweb1=newWebBrowser();f.Controls.Add(web1);web1.Navigate("http://www.pconline.com.cn");};
解决方案:
访问Windows窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。.NETFramework有助于在以非线程安全方式访问控件时检测到这一问题。在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个InvalidOperationException,并提示消息:“从不是创建控件controlname的线程访问它。”此异常在调试期间和运行时的某些情况下可靠地发生。强烈建议您在显示此错误信息时修复此问题。在调试以.NETFramework2.0版之前的.NETFramework编写的应用程序时,可能会出现此异常。注意可以通过将CheckForIllegalCrossThreadCalls属性的值设置为false来禁用此异常。这会使控件以与在VisualStudio2003下相同的方式运行。CheckForIllegalCrossThreadCalls=false;
解决方案:
引用18楼seeyou8888的回复:

Quote: 引用16楼Coffee_MX的回复:
usingSystem.Threading;usingSystem.Windows.Forms;namespaceWindowsFormsApplication1{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();this.IsMdiContainer=true;}privatevoidaaaToolStripMenuItem_Click(objectsender,System.EventArgse){ThreadthNewForm=newThread(()=>{Formf=newForm();//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.MdiParent=this));}#ifDEBUGSystem.Console.WriteLine("created");#endif//新加的,测试通过f.Load+=(sender1,e1)=>{f.Text="aaa";f.Width=150;f.Height=200;};//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.Show()));}f.Text="IntheChildThread";//此句在子线程中执行});thNewForm.Start();}}}

注册了个Load事件处理,目测在子线程中执行,测试通过

你这个子窗体的线程还是主线程啊MessageBox.Show("线程ID:"+Thread.CurrentThread.ManagedThreadId.ToString());你把这个代码放到主窗体和子窗体的Load事件看看,我在子窗体上放了个WebBrowser,然后打开网页,主窗体就会被卡住了//新加的,测试通过f.Load+=(sender1,e1)=>{f.Text="aaa";f.Width=150;f.Height=200;MessageBox.Show("线程ID:"+Thread.CurrentThread.ManagedThreadId.ToString());WebBrowserweb1=newWebBrowser();f.Controls.Add(web1);web1.Navigate("http://www.pconline.com.cn");};

UI线程最好是单线程,否则就如楼上所说,线程安全就不好了,根据你的问题,最好是直接用WebClient提交表单,或者HttpWebRequest发送请求什么的,这些都不涉及UI,所以异步调用应该是不成问题,没必要非要用Browser,涉及UI的东西,用多线程反倒会成为负担
解决方案:
延后执行Navigate方法应该不会有问题,因为Navigate方法本身是异步的,也许是你直接在初始化窗口中执行才出现的问题,未测试纯属猜测。
解决方案:
引用20楼Coffee_MX的回复:

UI线程最好是单线程,否则就如楼上所说,线程安全就不好了,根据你的问题,最好是直接用WebClient提交表单,或者HttpWebRequest发送请求什么的,这些都不涉及UI,所以异步调用应该是不成问题,没必要非要用Browser,涉及UI的东西,用多线程反倒会成为负担

没办法,一定要用Browser,因为还要人去操作
解决方案:
引用21楼qldsrx的回复:

延后执行Navigate方法应该不会有问题,因为Navigate方法本身是异步的,也许是你直接在初始化窗口中执行才出现的问题,未测试纯属猜测。

你自己试一下就知道可,就打开个http://www.pconline.com.cn然后后快速移动窗体,你就发现会卡住那两,三秒,电脑好的话可能1秒
解决方案:
正常情况下,IE也是多线程的,但是IE里面访问也有停顿,因为界面的产生,HTML元素的布局,图片的显示等都非常消耗资源,界面里面东西越多,停顿越大,这个停顿就好比UI刷新,如果不是数据获取时的停顿,那么你就无法优化,那个显示部分是IE自己慢造成的。
解决方案:
引用24楼qldsrx的回复:

正常情况下,IE也是多线程的,但是IE里面访问也有停顿,因为界面的产生,HTML元素的布局,图片的显示等都非常消耗资源,界面里面东西越多,停顿越大,这个停顿就好比UI刷新,如果不是数据获取时的停顿,那么你就无法优化,那个显示部分是IE自己慢造成的。

我现在都想换其他的浏览器控件了

时间: 2024-09-11 08:15:45

C#如何多线程创建MDI子窗体的相关文章

保证相同类型的MDI子窗体只会被打开一次的方法

看到论坛中有朋友问,如何可以保证在 MDI主窗体中,同一类型的子窗体只能打开一次,再 打开只是将原来打开的窗体激活.这个要求我以前写程序的时候也遇到过.我实现的基本思路 是: 一般每个子窗体都是继承自 Form类型,不同子窗体分别代表不同的功能,因此只需要判断 当前 MDI打开的子窗体的类型中有没有当前需要打开的子窗体类型就行了,如果有,则直接激 活,如果没有则创建一个并加到主窗体中.具体实现起来有很多种办法,这里只写出一个利用 泛型的实现: /// <summary> /// 打开MDI子窗

MDI子窗体尺寸如何超过屏幕分辨率

问题描述 我创建了一个MDI程序,然后显示一个MDI子窗体,然后一个问题.例如在1366*768分辨率下,无法创建一个300*1024尺寸的窗体.1024高于屏幕分辨率的768.窗体倒是会显示,只是下面的部分被截断了.各位达人,有什么方式可以解决么. 解决方案 解决方案二:你自己这不是很清楚吗,尺寸比屏幕大,肯定遮住了不被遮住,就限制大小在屏幕范围内解决方案三:子窗子的大小要根据屏幕的大小改变而改变,这样才能显示出来.有获取屏幕大小的方法,可以搜一下.解决方案四:引用1楼bdmh的回复: 你自己

急:请教C#2005中用DLL封装MDI子窗体的方法以及主窗体是如何调用?

问题描述 我的主窗体是单独的一个exe,MDI子窗体是在DLL中封装的,每个DLL都封装有三个子窗体(每次这三个子窗体都可以同时打开,相互访问其中方法的),目前我在EXE中的主窗体中定义了三个公用的窗体,在用反射创建(CreateInstance)这三个form窗体实例,在每个子窗体中的构造的函数中定义了主窗体的实例,本来是想着这样就可以互相访问之间的函数,结果由于在主窗体中定义的三个公用窗体并不知道每个子窗体的类型,这样都只是开始定义的form类了,就并不能访问每个子窗体中的方法了,只是访问主

Delphi中在DLL动态链接库中封装VCL的MDI子窗体

不多说了,看代码就应该明白了,曾经我遇到的问题,现在放出来大家共享! 这里是工程文件的部分: 在DLL中封装MDI子窗体需要重写DLL入口函数,具体代码如下: 1var 2 DllApp: TApplication;//定义保存原DLL的TApplication对象 3 DllScr: TScreen;//定义保存原DLL的TScreen对象 4 5procedure UnProcDll(Reason: Integer); register; 6//重新定义DLL入口函数 7begin 8 if

vb.net 中MDI子窗体对其父窗体属性的获取与修改

兄弟前些日子做项目,第一次使用vb.net,碰上不少问题,相信很多初学者多多少少都会遇到这些问题,为了初学者学习方便,小弟总结了一些小经验,供大家参考讨论.第一篇:如何在MDI子窗体中控制父窗体的属性等等 功能:比如打开一个子窗体后,就要设置父窗体中的某个菜单项或者按钮为不可见状态,诸如此类. 内容:MDI父窗体和MDI子窗体类定义如下:MDI父窗体:Class MDIForm Inherits System.Windows.Forms.Form......... 'member mnuMain

vb.net 防止MDI子窗体被多次实例化的四种方法

方法一            可能没有什么实用性,只适合简单项目:       Dim frmTmp As ClassForm        "实现已经设计好了一个称作(类名为)ClassForm的表单              frmTmp = New ClassForm          "在此定义它的一个实例         frmTmp.ShowDialog()                "作为模式窗体显示这样在当前窗体没有关闭以前,其他form上的菜单按钮都不可

如何在MDI子窗体中使用TransparencyKey属性

问题描述 如何在MDI子窗体中使用TransparencyKey属性如果窗体不是MDI子窗体,则可以通过设置TransparencyKey属性使得窗体透明.但如果窗体时MDI子窗体,这个属性就无效了.请教有什么办法使得MDI子窗体透明或其TransparencyKey属性生效. 解决方案 解决方案二: 解决方案三:感觉是来踢馆的解决方案四:是不可以的,让MDI子窗体再穿透MDI主窗体实在有点为难windows了不知为何一定要MDI呢?解决方案五:引用3楼xuzuning的回复: 是不可以的,让M

c# mdi 键盘事件-C#怎么使MDI子窗体响应键盘事件

问题描述 C#怎么使MDI子窗体响应键盘事件 定义了一个父窗体Form1,ismdicontainer设置为true: 定义一个子窗体Form2,将其mdifather设置为Form1,在Form2中编写该窗体的key_down事件,让其捕获shift+F1的按键消息,并且form2的keypreview属性设置为True,运行程序,按下shift+F1组合键,发现不能捕捉到该键盘事件. 将父窗体Form1的keypreview属性设置为True,同样Form1中编写Form1的key_down

单例模式——解决MDI子窗体实例化的问题

                机房收费系统进行有一段时间了,但是始终有些历史遗留问题.比如,如何MDI子窗体如何显示在上层的问题和MDI子窗体实例化的问题.         对于如何显示在上层的问题,这次采用的还是SetParent函数,在模块里面添加: <span style="font-size:18px;"><span style="font-size:18px;"> '定义一个用来设置子窗体的函数 Declare Function