[代碼問題解決錄]C#非模式窗體所遇到的問題與解決方案

這篇文章主要有以下幾個部分組成:(僅個人觀點,供個人思路梳理)

  • 非模式窗體的含義
  • 所遇到的問題與相應的解決方案

非模式窗體的含義:

窗體的顯示有兩種方式:
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

程序成功實現了自己的需求,問題也成功解決!





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章