C# WinForm获得主窗体——如何判断哪个是主窗体

摘要:
但是,有些程序在显示主窗体Windowwindow=Application之前会有一个登录或指导窗体。现在的主窗口;互联网上的一些文章说你可以使用Process。GetCurrentProcess()。MainWindowHandle用于确定当前窗体是否是主窗体,但不能在进程中使用此属性来确定哪个窗体是进程的主窗体;

一. 关于定义

主窗体的定义一般有两种。第一种就是一般上,普遍意义认为是程序中第一个被创建出来的窗体,但是由于一些程序在显示主窗体之前会有一个登录或者引导窗体,在使用完了之后直接隐藏而不是关闭。这个时候,主窗体并不会是第一个窗体。所以,第二种说法就是说,包含了软件整体功能的展示性界面所在的窗体,我们称之为主窗体。

而在本文中,所有叙述中所指的主窗体都是指的是第二种定义的主窗体

二. WPF中的主窗体与引子

对于 WPF 用户来说,我们可以直接使用:

Window window = Application.Current.MainWindow;

而对于 Winform 来说就比较麻烦,因为它并没有提供任何获得主窗体的接口属性(?)。网上有的文章说可以通过

Process.GetCurrentProcess().MainWindowHandle

来判断当前的窗体是不是主窗体,经过博主的测试发现,这个方法并不能很好的判断。经过测试核实 MainWindowHandle 有如下特点:

1. 首先,虽然这个属性的名称是 MainWindowHandle,但是不能在某个进程中使用这个属性来判断哪一个窗体是进程的主窗体;进程之间获得主窗体可以考虑使用这个方式(但不是特别好用),所以这个方法适合做运行外接程序时使用,并且仅当进程有图形界面(有窗体)时,该进程才具有与其关联的主窗口。如果关联进程没有主窗口,则 MainWindowHandle 值为零。如果刚启动了一个进程,并且想使用其主窗口句柄,则需要考虑使用 WaitForInputIdle 方法让该进程完成启动,从而确保创建了主窗体窗口的句柄。否则,将引发异常。

一般避免这个异常的代码如下:

Process startProcess=... //获得创建一个进程
startProcess.WaitForInputIdle();
Intptr handle=startProcess.MainWindowHandle;

2. 如果这个函数是在 Show(null) 或者 Application.Run(Form) 中的窗体打开的,那么这个值是这个打开窗体的句柄;如果这个函数是在 ShowDialog(null) 或者 ShowDialog(parentForm) 或者 Show(parentForm) 的时候,这个值是父级窗体的句柄;

具体的文章参考可以见下面地址链接的文章:

参考文章

三. 三种博主的方法

博主目前有如下面所述的三种处理方式,如果有更好的方式可以在评论中和博主交流。


  • 通过公共配置类的接口方式

原理:使用 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 会作为所有类的下端,所以所有的类都可以访问到这个主窗体。


  • 使用派生 AppliactionContext 的方式

原理: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 集合找到主窗体

原理:通过 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. 使用之前需要注入一次,操作和代码都比较繁琐;

五. 程序代码下载

下载地址

免责声明:文章转载自《C# WinForm获得主窗体——如何判断哪个是主窗体》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇MySQL按指定字符合并及拆分SAP CRM 7.0中的BOL(Business Object Layer)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

window.open() | close()方法

Window对象的open()方法可以打开一个新的浏览器窗口(或标签页),window.open()载入指定的URL到新的或已存在的窗口中,返回代表那个窗口的window对象,它有4个可选的参数    1.open()第一个参数是要在新窗口中显示的文档的URL,可为空(为空就是about: blank)    2.open()第二个参数是新打开的窗口的名字...

AVD模拟器怎么配置上网

转自:http://blog.csdn.net/you_jinjin/article/details/7228303 方法一 首先,Windows下,配置Adroid环境变量(Win7为例) 1、桌面右键——》我的电脑——》高级系统设置 2、高级——》环境变量——》系统变量——》Path 3、添加Android SDK目录到系统变量Path中,如下图:...

在Eclipse中使用JUnit4进行单元测试(图文教程一)

在Eclipse中使用JUnit4进行单元测试    单元测试,JUnit4。    这两个有什么关系呢?这就好比(草)单元测试和(割草机)。用这个JUnit4工具去辅助我们进行测试。其实不理解这个也没关系,听多了见多了用多了,自然而然地就会懂了。    有人可能会想,那我直接自己编写个测试的方法不就可以了。例如写个System.out.print输出,看...

C#-WebForm-JS知识:基础部分、BOM部分、DOM部分、JS事件

---恢复内容开始--- 一、基础部分: 1、JavaScript 是什么? 是一门脚本语言,是属于弱类型(语言语法很随意),C#是强类型(语言语法非常严格)(李献策lxc) 优点:JS 执行速度快 2、JS 与java有什么关系?  没有任何关系 3、JS 能做什么事情?   能控制浏览器 BOM   控制元素 DOM JS不能做什么?   不能操作文件...

dwr报错整理【一】

对于dwr,又一个非常重要的问题的记录.在使用dwr与spring整合时,发现当js文件里使用对应的java服务器端代码时,只要你在对应的方法里使用dao,就会报A server error has occured的错误.我采取的方法是在对应的方法里本是用dao的地方,从新写业务端的代码,比如我的以查询对应内容,findcontext()方法里使用了dao...

初学安卓开发随笔之 Menu、toast 用法、活动的四种启动模式 以及 一个方便的Base活动类使用方法

Toast toast 是安卓系统的一种非常棒的提醒方式 首先定义一个弹出Toast的触发点,比如可以是按钮之类 其中 Toast.LENGTH_SHORT是指显示时长 还有一个内置变量为Toast.LENGTH_LONG可以选用。 Toast.makeText(FirstActivity.this, "You clicked Button 1...