这篇文章主要有以下几个部分组成:(仅个人观点,供个人思路梳理)
- 非模式窗体的含义
- 所遇到的问题与相应的解决方案
非模式窗体的含义:
窗体的显示有两种方式:
1.以ShowDialog()方式显示——模式窗体;
Form frm=new Form();
frm.ShowDialog();
以这种方法显示窗体,有个特点就是:当且仅当当前窗体显示并关闭后,才可以操作别的窗体。这就跟消息框提示信息类似,除非消息框操作并关闭后才可以去操作别的控件或窗体。这种被称之为模式窗体,它有一个很不好的地方就是:很难实现两个窗体之间的交互;2.以Show()方式显示——非模式窗体;
Show()有两个函数原型,一种是无需带参数,另一种是必须指明当前这个窗体的拥有者窗体参数;
//主窗体
Form mainfrm=new Form();
Form1 subfrm1=new Form1();
subfrm1.Show();//无参
Form1 subfrm2=new Form1();
subfrm2.Show(mainfrm);//带参,指明当前窗体的拥有者是mainfrm窗体
那么,这两个函数原型显示出来的窗体有何不同呢?经过调试会发现这两种方式的区别:
1.无参方法显示窗体容易导致窗体重复生成,而有参则不会;
2.有参方法显示窗体后,当前窗体总是置顶,而无参方法显示窗体则根据焦点来置顶窗体。
非模式窗体较之模式窗体,可以实现窗体之间的交互,即在当前窗体显示的情况下仍然可以操作主窗体。
所遇到的问题与相应解决方案:
在自己的这个项目中,想要实现非模式窗体下的无参方法显示窗体的效果——即根据焦点来置顶窗口。于是,我将有参方法改成了无参方法,在这种改动过程中,我遇到了两个问题:
1.如何防止窗体的重复生成呢?(无参方法生成窗体的诟病)
2.当前有两个窗体出现,一个是子窗体,一个用主窗体,如何实现用户点击主窗体上某一处内容而出现子窗体,点击除该内容的其他部分显示主窗体呢?
问题1的解决过程:
通过网上搜索,我初步认定要通过静态变量来解决这个问题,因为根据静态变量相关知识,我们很容易知道:当一个类被动态实例化时,该类内部的静态变量都会被自动生成一次。因此,这个问题的解决思路大致是这样的:
因此,根据这样的解决思路,需要重新修改该子窗体类的构造函数:
private static frmSubmit1 instance;//声明一个全局静态变量防止窗体被重复实例化
public static frmSubmit1 CreatfrmSubmit1()
{
//判断该静态对象是否为空
if (instance == null || instance.IsDisposed)
{
//为空则实例化该窗体
instance = new frmSubmit1();
}
return instance;
}
问题2的解决过程:
通过网上搜索,我初步决定调用Window API来解决这个问题,并形成这样的解决方案:当用户点击主窗体某一块内容时,实例化子窗体,并将子窗体的句柄作为参数调入API函数SetForegroundWindow()中。
首先,怎么调用Window API呢?格式是这样的:
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GetForegroundWindow(); //获得本窗体的句柄
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetForegroundWindow")]
public static extern bool SetForegroundWindow(IntPtr hWnd);//设置此窗体为活动窗体
但是,在不断调试程序过程中发现一个问题即:子窗体的确会置顶,但置顶的时间非常短,几乎一秒钟不到又被主窗体把焦点抢走,而后主窗体又成为了置顶窗口,达不到个人所希望的那样。
所以问题很清楚地告诉你,需要改变时间,那么,自然而然会想到要使用时间控件Time和委托,在阅读了该篇博文后:http://www.cnblogs.com/jx270/archive/2013/02/20/2919410.html 有了清晰的解决思路:
声明一个全局句柄对象→先赋值给主窗体→构造时间控件Tick事件的相应程序(判断当前句柄对象的值是否等于当前置顶窗口的句柄值,如果不等则将该句柄对象所在窗口设为置顶)→在主窗体某一块内容的事件中将全局句柄对象复制给子窗体并订阅时间控件的Tick事件;
代码:
//声明一个全局句柄对象
public IntPtr ctlHandle;
#region 主窗体的Load事件
ctlHandle=mainfrm.Handle;
#endregion
#region 主窗体点击某一块内容后的事件
//构造子窗体subfrm(略)
ctlHandle=subfrm.Handle;
//订阅事件
Time1.Tick+=new EventHandler(Time1_Tick);
Time1.Interval=100;
#endregion
private void timer1_Tick(object sender, EventArgs e) {
//检查句柄情况
if (ctlHandle != GetForegroundWindow())
{
//将当前所获句柄的窗口进行持续置顶
SetForegroundWindow(ctlHandle);
timer1.Stop();//停止计时从而实现窗体持续置顶 } }
在运行过程中,发现程序丝毫不会相应事件的响应程序,这是为什么呢?在仔细研究代码后,发现:系统是自动将时间控件的Enabled属性设为False(不可用),所以先要将该属性改为True才可以。
于是代码小小改动一下:
#region 主窗体点击某一块内容后的事件
//构造子窗体subfrm(略)
ctlHandle=subfrm.Handle;
//订阅事件
Time1.Enabled=True;//将时间控件设为可用状态
Time1.Tick+=new EventHandler(Time1_Tick);
Time1.Interval=100;
#endregion
程序成功实现了自己的需求,问题也成功解决!