?
從VB 6到VB.NET——窗體特殊應用
李洪根
一、?? 摘要
??? VB .NET做為VB6的升級版本,具備了許多新的功能,它可以簡便快捷地創建 .NET 應用程序(包括 XML Web services 和 ASP.NET Web 應用程序),還是一個功能強大的面向對象的編程語言(如繼承、接口和重載)。新的語言功能包括自由線程處理和結構化異常處理。VB.NET 還完全集成了.NET 框架和公共語言運行庫,.NET 框架和公共語言運行庫共同提供語言互操作性、垃圾回收、增強的安全性和改進的版本支持??梢哉f是一個劃時代的產品!
從VB6到VB.NET的開發過程中,窗體應用始終是一個永恒的話題。任何一個WINDOWS的應用程序,都與窗體密切相關,在許多場合,我們都需要對窗體進行一些特殊的設置或操作,本文用VB6和VB.NET相結合,來說明窗體應用的特殊問題及處理,以及VB.NET給我們帶來的新的功能!
?
二、正文
1、???????????? 創建特殊形狀的窗體
我們還是來看一下在VB6中的實現,VB6中實現(借助API函數)
做一個古怪的窗口必須要用的也是此程序中最重要的一個函數就是SetWindowRgn
它的功能就是對指定的窗口進行重畫,把這個窗口你選擇的部分留下其余的部分抹掉
參數:hWnd:你所要重畫的窗口的句柄,比如你想重畫form1則應該讓此參數為form1.hWnd
???? hRgn:你要保留的區域的句柄,這個句柄是關鍵,你需要通過別的渠道來獲得
在這里的區域是由Combinergn合成的新區域
???? bRedram:是否要馬上重畫,一般設為true
函數CombineRgn將兩個區域組合為一個新區域
函數Createrectrgn為創建一個由點X1,Y1和X2,Y2描述的矩形區域
函數CreateEllipticRgn為創建一個X1,Y1和X2,Y2的橢圓區域
用DeleteObject這個函數可刪除GDI對象,比如畫筆、刷子、字體、位圖、區域以及調色板等等。對象使用的所有系統資源都會被釋放
?
以下是VB6的代碼:
??? PrivateDeclareFunction CreateEllipticRgn Lib "gdi32" (ByVal X1 AsLong, ByVal Y1 AsLong, ByVal X2 AsLong, ByVal Y2 AsLong) AsLong
??? PrivateDeclareFunction CreateRectRgn Lib "gdi32" (ByVal X1 AsLong, ByVal Y1 AsLong, ByVal X2 AsLong, ByVal Y2 AsLong) AsLong
??? PrivateDeclareFunction CombineRgn Lib "gdi32" (ByVal hDestRgn AsLong, ByVal hSrcRgn1 AsLong, ByVal hSrcRgn2 AsLong, ByVal nCombineMode AsLong) AsLong
PrivateDeclareFunction SetWindowRgn Lib "user32" (ByVal hWnd AsLong, ByVal hRgn AsLong, ByVal bRedraw AsBoolean) AsLong
??? PrivateDeclareFunction DeleteObject Lib "gdi32" (ByVal hObject AsLong) AsLong
??? PrivateConst RGN_DIFF = 4
?
??? PrivateSub Form_Load()
??????? Dim rgn AsLong
??????? Dim rgnRect AsLong
??????? Dim rgnDest AsLong
?
??????? rgn = CreateEllipticRgn(0, 0, Me.Width / Screen.TwipsPerPixelX, Me.Height / Screen.TwipsPerPixelY)
??????? rgnRect = CreateRectRgn((Me.Width / Screen.TwipsPerPixelX - 20) / 2, (Me.Height / Screen.TwipsPerPixelY - 20) / 2, (Me.Width / Screen.TwipsPerPixelX + 20) / 2, (Me.Height / Screen.TwipsPerPixelY + 20) / 2)
??????? rgnDest = CreateRectRgn(0, 0, 1, 1)
??? CombineRgn rgnDest, rgn, rgnRect, RGN_DIFF
SetWindowRgn Me.hWnd, rgnDest, True
??? Call DeleteObject(rgnRect)
??? Call DeleteObject(rgnDest)
??? EndSub
?
??? PrivateSub Command1_Click()
??????? End
??? EndSub
?
?
在VB.NET中,我們可以使用.NET 框架類庫System.Drawing.Drawing2D的GraphicsPath 類(應用程序使用路徑來繪制形狀的輪廓、填充形狀內部和創建剪輯區域),來繪制圖形,
然后通過窗體的Me.Region來設置窗口的可見區域。
?
以下是VB.NET的代碼:
??? '聲明一個布爾型變量,判斷窗體是否正常區域
Dim IsNormalRegion AsBoolean = True
?
? ??PrivateSub Button2_Click(ByVal sender As System.Object, _
??????????? ByVal e As System.EventArgs) Handles Button2.Click
?
??????? If (IsNormalRegion) Then
??????????? '構造一個GraphicsPath對象實例
??????????? Dim Graphics AsNew System.Drawing.Drawing2D.GraphicsPath()
??????????? Dim intHeight AsInteger = Me.Size.Height
??????????? Dim intWidth AsInteger = Me.Size.Width
?
??????????? '定義內矩形的左上角坐標
??????????? Dim RectTop AsInteger = 100
??????????? '在窗體上繪制一個大橢圓,左上角的坐標取為(0,0)
??????????? Graphics.AddEllipse(0, 0, intWidth, intHeight)
??????????? '再繪制一個小矩形
??????????? Dim AddRect AsNew Rectangle(RectTop, RectTop, intHeight - (RectTop * 2), intHeight - (RectTop * 2))
??????????? Graphics.AddRectangle(AddRect)
??????????? '設置窗口的可見區域
??????????? Me.Region = New Region(Graphics)
??????? Else
??????????? Me.Region = Nothing
??????? EndIf
??????? IsNormalRegion = Not IsNormalRegion
EndSub
程序運行的結果如下:
2、???????????? 使窗體在其他所有窗體之上(Allway On Top)
VB6中實現(借助API函數SetWindowPos)
??? PrivateDeclareFunction SetWindowPos Lib "user32" (ByVal hwnd AsLong, _
??????????????? ByVal hWndInsertAfter AsLong, ByVal x AsLong, ByVal y AsLong, _
??????????????? ByVal cx AsLong, ByVal cy AsLong, ByVal wFlags AsLong) AsLong
---- hWnd變元是窗口的句柄;x,y是窗口的左上角的坐標;cx、cy是窗口寬度和高度;hWndInsertAfter變元是窗口清單中hWnd窗口前面的窗口句柄,有四個可選值:
序號 可 選 值 作 用
1 HWND_BOTTOM 把窗口放在窗口清單的底部
2 HWND_TOP 把窗口放在窗口清單的字符順序的頂部
3 HWND_TOPMOST 把窗口放在窗口清單的頂部
4 HWND_NOTOPMOST 把窗口放在窗口清單的頂部,最上層窗口之下
---- WFlags變元為整型值,有八個可選值:
序號 可 選 值 作用
1 SWP_DRAWFRAME 在窗口周圍畫一個方框
2 SWP_HIDEWINDOW 隱藏窗口
3 SWP_NOACTIVATE 不激活窗口
4 SWP_NOMOVE 保持窗口當前位置
5 SWP_NOREDRAW 窗口不自動重畫
6 SWP_NOSIZE 保持窗口當前尺寸
7 SWP_NOZORDER 保持窗口在窗口清單中的當前位置
8 SWP_SHOWWINDOW 顯示窗口
??? PrivateDeclareFunction SetWindowPos Lib "user32" (ByVal hwnd AsLong, _
??????????????????? ByVal hWndInsertAfter AsLong, ByVal x AsLong, ByVal y AsLong, _
??????????????????? ByVal cx AsLong, ByVal cy AsLong, ByVal wFlags AsLong) AsLong
??? PrivateConst SWP_NOMOVE = 2
??? PrivateConst SWP_NOSIZE = 1
??? PrivateConst FLAGS = SWP_NOMOVE Or SWP_NOSIZE
??? PrivateConst HWND_TOPMOST = -1
??? PrivateConst HWND_NOTOPMOST = -2
?
??? PrivateSub Command1_Click()
??????? '把窗體放在最前面:
??????? res% = SetWindowPos(Form1.hwnd, HWND_TOPMOST, 0, 0, 0, 0, FLAGS)
??? EndSub
?
??? PrivateSub Command2_Click()
??????? '使窗體恢復普通模式:
??????? res% = SetWindowPos(Form1.hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, FLAGS)
??? EndSub
?
?
在VB.NET中,太簡單了!系統為窗體提供了TopMost屬性,我們將TopMost屬性設置為True,就實現了Allways On Top 的功能,要取消此功能,設置為False即可。
??? PrivateSub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) HandlesMyBase.Load
??????? Me.TopMost = True
EndSub
?
3、???????????? 窗體透明度漸變效果
我們還是來看一下在VB6中的實現,VB6中實現(借助API函數SetLayeredWindowAttributes)
使用這個函數,可以輕松的控制窗體的透明度。按照微軟的要求,透明窗體在創建時應使用WS_EX_LAYERED參數(用CreateWindowEx),或者在創建后設置該參數(用SetWindowLong),我選用后者。
SetLayeredWindowAttributes函數,其中hwnd是透明窗體的句柄,crKey為顏色值,bAlpha是透明度,取值范圍是[0,255],dwFlags是透明方式,可以取兩個值:當取值為LWA_ALPHA時,crKey參數無效,bAlpha參數有效;當取值為LWA_COLORKEY時,bAlpha參數有效而窗體中的所有顏色為crKey的地方將變為透明。
??? Const LWA_COLORKEY = &H1
??? Const LWA_ALPHA = &H2
??? Const GWL_EXSTYLE = (-20)
??? Const WS_EX_LAYERED = &H80000
??? PrivateDeclareFunction GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd AsLong, ByVal nIndex AsLong) AsLong
??? PrivateDeclareFunction SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd AsLong, ByVal nIndex AsLong, ByVal dwNewLong AsLong) AsLong
??? PrivateDeclareFunction SetLayeredWindowAttributes Lib "user32" (ByVal hWnd AsLong, ByVal crKey AsLong, ByVal bAlpha AsByte, ByVal dwFlags AsLong) AsLong
??? PrivateSub Form_Load()
??????? Dim Ret AsLong
??????? 'Set the window style to 'Layered'
??????? Ret = GetWindowLong(Me.hWnd, GWL_EXSTYLE)
??????? Ret = Ret Or WS_EX_LAYERED
SetWindowLong Me.hWnd, GWL_EXSTYLE, Ret
??????? 'Set the opacity of the layered window to 128
??????? '我們可以設置這個數值來控制透明程度
??????? SetLayeredWindowAttributes Me.hWnd, 0, 128, LWA_ALPHA
??? EndSub
?
?
?
在VB.NET中,太簡單了!系統為窗體提供了Opacity屬性,來確定窗體的不透明和透明程度,0%為透明,100%為不透明。
以下程序通過循環顯示窗體的透明度過程,為了讓大家看清楚其變化,在循環過程中使用了System.Threading.Thread.Sleep來停頓。
?
???? PrivateSub button1_Click(ByVal sender As System.Object, _
???????????? ByVal e As System.EventArgs) Handles button1.Click
??????? '窗體的透明度漸變過程
??????? button1.Enabled = False
??????? Dim I AsDouble
??????? For I = 0.01 To 1 Step 0.01
??????????? Me.Opacity = I
??????????? System.Windows.Forms.Application.DoEvents()
??????????? System.Threading.Thread.Sleep(5)
??????? Next
??????? Me.Opacity = 1
??????? button1.Enabled = True
EndSub
?
4、???????????? 使窗體右上角的X無效,禁止Alt+F4關閉窗體
在特殊窗體的應用中,我們有時需要把窗體右上角標題欄上的關閉按鈕屏幕,當用戶點擊其它地方(比如說一個Button)退出,那我們怎么做呢?。
?
我們還是來看一下在VB6中的實現,VB6中實現(借助API函數)
??? PrivateDeclareFunction GetSystemMenu Lib "user32" (ByVal hwnd AsLong, ByVal bRevert AsLong) AsLong
??? PrivateDeclareFunction GetMenuItemCount Lib "user32" (ByVal hMenu AsLong) AsLong
??? PrivateDeclareFunction DrawMenuBar Lib "user32" (ByVal hwnd AsLong) AsLong
??? PrivateDeclareFunction RemoveMenu Lib "user32" (ByVal hMenu AsLong, ByVal nPosition AsLong, ByVal wFlags AsLong) AsLong
??? Const MF_BYPOSITION = &H400&
??? Const MF_REMOVE = &H1000&
??? PrivateSub Form_Load()
??????? Dim hSysMenu AsLong, nCnt AsLong
??????? ' Get handle to our form's system menu
??????? ' (Restore, Maximize, Move, close etc.)
??????? hSysMenu = GetSystemMenu(Me.hwnd, False)
?
??????? If hSysMenu Then
??????????? ' Get System menu's menu count
??????????? nCnt = GetMenuItemCount(hSysMenu)
??????????? If nCnt Then
???? ???????????' Menu count is based on 0 (0, 1, 2, 3...)
??????????????? RemoveMenu hSysMenu, nCnt - 1, MF_BYPOSITION Or MF_REMOVE
??????????????? RemoveMenu hSysMenu, nCnt - 2, MF_BYPOSITION Or MF_REMOVE ' Remove the seperator
??????????????? DrawMenuBar(Me.hwnd)
??????????????? ' Force caption bar's refresh. Disabling X button
??????????????? Me.Caption = "Try to close me!"
??????????? EndIf
??????? EndIf
EndSub
?
'如果還要屏蔽Alt+F4,加上
??? PrivateSub Form_QueryUnload(ByVal Cancel AsInteger, ByVal UnloadMode AsInteger)
??????? Cancel = 1
??? EndSub
?
在VB.NET中,這次需要借助API了,因為系統沒有提供這樣的類,這個例子,同時給大家提供了一個API的使用范例。(因為系統類庫包裝了絕大部分API,所以不推薦使用)
以下是VB.NET的代碼:
??? 'API聲明
??? PrivateDeclareFunction GetSystemMenu Lib "User32" (ByVal hwnd AsInteger, ByVal bRevert AsLong) AsInteger
??? PrivateDeclareFunction RemoveMenu Lib "User32" (ByVal hMenu AsInteger, ByVal nPosition AsInteger, ByVal wFlags AsInteger) AsInteger
??? PrivateDeclareFunction DrawMenuBar Lib "User32" (ByVal hwnd AsInteger) AsInteger
??? PrivateDeclareFunction GetMenuItemCount Lib "User32" (ByVal hMenu AsInteger) AsInteger
??? PrivateConst MF_BYPOSITION = &H400&
??? PrivateConst MF_DISABLED = &H2&
?
??? PrivateSub disableX(ByVal wnd As Form)
??????? Dim hMenu AsInteger, nCount AsInteger
???? ???'得到系統Menu
??????? hMenu = GetSystemMenu(wnd.Handle.ToInt32, 0)
??????? '得到系統Menu的個數
??????? nCount = GetMenuItemCount(hMenu)
??????? '去除系統Menu
??????? Call RemoveMenu(hMenu, nCount - 1, MF_BYPOSITION Or MF_DISABLED)
??????? '重畫MenuBar
??????? DrawMenuBar(Me.Handle.ToInt32)
??? EndSub
?
??? PrivateSub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) HandlesMyBase.Load
??????? '使用X不能用
??????? disableX(Me)
??? EndSub
?
??? PrivateSub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
??????? '關閉窗口
??????? Me.Close()
EndSub
?
??? '如果還要屏蔽Alt+F4,加上
??? ProtectedOverridesSub WndProc(ByRef m As System.Windows.Forms.Message)
??????? Dim SC_CLOSE AsInteger = 61536
??????? Dim WM_SYSCOMMAND AsInteger = 274
??????? '判斷是系統消息,是不是關閉窗體,使Alt+F4無效
??????? If m.Msg = WM_SYSCOMMAND AndAlso m.WParam.ToInt32 = SC_CLOSE Then
??????????? ExitSub
??????? EndIf
??????? MyBase.WndProc(m)
??? EndSub
?
?
程序運行的結果如下:
5、????????????? 無標題欄的窗體的拖動問題
在特殊窗體的應用中,我們有時需要把窗體的標題欄屏蔽掉,以窗體換上自己的外殼。是,當去掉了窗體標題欄后,移動窗體就成了一個問題。
我們還是來看一下在VB6中的實現,VB6中實現(借助API函數SendMessage)
在設計時將窗體的BorderStyle屬性設置為0-none
??? PrivateDeclareFunction SendMessage Lib "User32" Alias "SendMessageA" (ByVal hWnd AsLong, ByVal wMsg AsLong, ByVal wParam AsLong, ByVal lParam As Any) AsLong
??? PrivateDeclareSub ReleaseCapture Lib "User32" ()
??? Const WM_NCLBUTTONDOWN = &HA1
??? Const HTCAPTION = 2
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
??????? Dim lngReturnValue AsLong
??????? If Button = 1 Then
??????????? 'Release capture
??????????? Call ReleaseCapture()
??????????? 'Send a 'left mouse button down on caption'-message to our form
??????????? lngReturnValue = SendMessage(Me.hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0&)
????? ??EndIf
??? EndSub
??? PrivateSub Form_Paint()
??????? Me.Print("Click on the form, hold the mouse button and drag it")
??? EndSub
?
?
在VB.NET中,這次需要借助API SendMessage 了
在設計時將Form.FormBorderStyle 屬性設置為None,然后添加以下代碼:
??? DeclareFunction SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd AsInteger, ByVal wMsg AsInteger, ByVal wParam AsInteger, ByVal lParam AsInteger) AsInteger
?
??? PrivateDeclareSub ReleaseCapture Lib "User32" ()
??? Const WM_NCLBUTTONDOWN = &HA1
??? Const HTCAPTION = 2
?
??? PrivateSub Form1_MouseDown(ByVal sender AsObject, ByVal e As System.Windows.Forms.MouseEventArgs) HandlesMyBase.MouseDown
??????? ReleaseCapture()
??????? SendMessage(Me.Handle.ToInt64, WM_NCLBUTTONDOWN, HTCAPTION, 0)
EndSub
?
三、結束語
以上實例在Windows 2000,VB6,VS.NET環境下運行通過。從以上實例,我們可以看到,以前VB6沒有的好多屬性和方法,在VB.NET中已經提供了出來,而且.NET提供了許多類庫,可以完成在VB6中需要借助大量的API才能實現的操作。比如說構建一個多線程應用程序,用VB.NET就很容易了!更值得一提的就是,VB.NET是完全的面向對象,更加容易封裝我們的業務邏輯,構建N層應用程序等企業級應用。我愛VB6,更愛.NET!
?