主窗体的定义通常有两种。第一种就是通常上,广泛意义认为是程序中第一个被建立出来的窗体,可是因为一些程序在显示主窗体以前会有一个登陆或者引导窗体,在使用完了以后直接隐藏而不是关闭。这个时候,主窗体并不会是第一个窗体。因此,第二种说法就是说,包含了软件总体功能的展现性界面所在的窗体,咱们称之为主窗体。函数
而在本文中,全部叙述中所指的主窗体都是指的是第二种定义的主窗体。工具
对于 WPF 用户来讲,咱们能够直接使用:测试
Window window = Application.Current.MainWindow;
而对于 Winform 来讲就比较麻烦,由于它并无提供任何得到主窗体的接口属性(?)。网上有的文章说能够经过spa
Process.GetCurrentProcess().MainWindowHandle
来判断当前的窗体是否是主窗体,通过博主的测试发现,这个方法并不能很好的判断。通过测试核实 MainWindowHandle 有以下特色:.net
1. 首先,虽然这个属性的名称是 MainWindowHandle,可是不能在某个进程中使用这个属性来判断哪个窗体是进程的主窗体;进程之间得到主窗体能够考虑使用这个方式(但不是特别好用),因此这个方法适合作运行外接程序时使用,而且仅当进程有图形界面(有窗体)时,该进程才具备与其关联的主窗口。若是关联进程没有主窗口,则 MainWindowHandle 值为零。若是刚启动了一个进程,而且想使用其主窗口句柄,则须要考虑使用 WaitForInputIdle 方法让该进程完成启动,从而确保建立了主窗体窗口的句柄。不然,将引起异常。线程
通常避免这个异常的代码以下:code
Process startProcess=... //得到建立一个进程 startProcess.WaitForInputIdle(); Intptr handle=startProcess.MainWindowHandle;
2. 若是这个函数是在 Show(null) 或者 Application.Run(Form) 中的窗体打开的,那么这个值是这个打开窗体的句柄;若是这个函数是在 ShowDialog(null) 或者 ShowDialog(parentForm) 或者 Show(parentForm) 的时候,这个值是父级窗体的句柄;orm
具体的文章参考能够见下面地址连接的文章:对象
参考文章blog
博主目前有以下面所述的三种处理方式,若是有更好的方式能够在评论中和博主交流。
原理:使用 Form 做为一个公共配置类的接口,全部的上端都经过这个公共配置类来访问主窗体的对象。
这边的公共配置类的代码通常为:
/// <summary> /// 公共配置类 /// </summary> public class CommonBase { /// <summary> /// 主窗体对象的引用 /// </summary> public static Form MainForm { get; set; } }
上端调用代码,经过在使用窗体的时候,将主窗体注入到公共配置类的接口中:
//建立主窗体 Form1 form1 = new Form1(); //设置主窗体 CommonBase.MainForm = form1; //添加到消息循环 Application.Run(form1);
因为这个公共配置类 CommonBase 会做为全部类的下端,因此全部的类均可以访问到这个主窗体。
原理:Application.Run 有多种重载的方式,咱们通常使用的是
// // 摘要: // 在当前线程上开始运行标准应用程序消息循环,并使指定窗体可见。 // // 参数: // mainForm: // 一个 System.Windows.Forms.Form,它表明要使之可见的窗体。 // // 异常: // T:System.InvalidOperationException: // 主消息循环已在当前线程上运行。 public static void Run(Form mainForm);
这边传递的是一个窗体的对象;而这个 Run 方法还有一个别的重载:
// 摘要: // 在特定的 System.Windows.Forms.ApplicationContext 中,在当前线程上开始运行标准应用程序消息循环。 // // 参数: // context: // 一个 System.Windows.Forms.ApplicationContext,应用程序将在其中运行。 // // 异常: // T:System.InvalidOperationException: // 主消息循环已在此线程上运行。 public static void Run(ApplicationContext context);
这个重载须要一个派生自 ApplictionContext 的对象。经过派生这个上下文对象,并在其中包装一个主窗体的对象,而后在这个派生的子类中给出获取自身的静态方法,让用户能够得到这个派生类的全局实例,最好是使用单例的方式来获取。接着,利用 AppliactionContext 中的 MainForm 属性来得到注册到 ApplicationContext 的主窗体。
下面给出一个派生类 MainFormContext 的代码:
/// <summary> /// 派生ApplicationContext的方法 /// </summary> public class MainFormContext : ApplicationContext { /// <summary> /// 线程锁 /// </summary> private static object objLock = new object(); /// <summary> /// 全局单例 /// </summary> private static MainFormContext context = null; /// <summary> /// 隐藏构造函数不让外部调用建立 /// </summary> /// <param name="mainForm"></param> private MainFormContext(Form mainForm) : base(mainForm) { } /// <summary> /// 得到MainFormContext /// </summary> /// <param name="mainForm"></param> /// <returns></returns> public static MainFormContext GetInstance(Form mainForm = null) { if (mainForm == null) { return context; } //建立单例 if (context == null) { lock (objLock) { if (context == null) { context = new MainFormContext(mainForm); } } } return context; } }
上端调用的代码,建立包括 MainFormContext 的上下文,并使用 Application.Run(Application)进行执行:
//主窗体 Form1 form = new Form1(); //建立上下文 MainFormContext context = MainFormContext.GetInstance(form); //开启消息循环 Application.Run(context);
原理:经过 Application.OpenForms 集合遍历全部的呈打开展现的窗体对象,而后经过比对窗体的名称(或者别的特色)来找到主窗体的 Form 类型的对象。
注意:网上不少的文章都说,Application.OpenForms[0] 就是主窗体。其实,按照第一大点的定义,若是在出现主窗体以前还有别的窗体建立而且没有被关闭(销毁),那么这个 0 号序号的窗体就会是别的窗体(非主窗体)。这边 Application.OpenForms 窗体的集合,是按照你在程序运行的过程当中建立显示窗体的顺序来进行排列的。
好比,主窗体的名称 Text 为 "MainForm",那么用来得到主窗体的帮助类的代码以下:
/// <summary> /// 匹配得到主窗体的工具类 /// </summary> public class MainFormHelper { public Form GetMainForm(string mainFormName= "MainForm") { if(string.IsNullOrEmpty(mainFormName)) { return null; } foreach(Form frm in Application.OpenForms) { if(frm.Text.Trim()== mainFormName) { return frm; } } return null; } }
这边三种方案都存在必定的问题:
1. 要建立一个额外的帮助类,而且这个类要是全部类的下端,若是有一个类是这个帮助类的下端,那么这个类会访问不到帮助类中的成员,也就是说不能访问到主窗体的对象属性;
2. 因为这个帮助类是全部类的下端,因此这个配置类中的主窗体只能以窗体的 Form 基类出现,致使其余的使用端不能直接使用主窗体具体类型的方法(须要转换,甚至不能转换);
3. 使用以前须要注入一次,操做和代码都比较繁琐;