今天我们来看看VB.NET怎样将程序窗口嵌入到任务栏(Taskbar)中,下图是我们实现的效果。
开始之前,小孟先简单介绍一下任务栏的组成,在这里小孟只介绍与我们的程序有关的部分。整个任务栏是有一个大的窗口容器(该容器类名是 Shell_TrayWnd)和一些窗口共同组成的,在这个类名是Shell_TrayWnd的容器中,有一个类名是ReBarWindow32的二级容 器。这个二级容器中包含的窗口在Win7和WinXP中是不同的(小孟使用的Win7,不过在这个程序中Win7和WinXP的效果都是一样的)。在 WinXP中,包含一个类名是MSTaskSwWClass的窗口外,这个窗口就是来显示任务栏按钮的(就是显示你打开了哪些窗口的那个区域,在这里小孟 叫他最小化窗口),还包含快速启动栏(在左侧以小图标显示,单击后会启动相应程序的那个区域);在Win7中只有一个类名是MSTaskSwWClass 的窗口,因为Win7中没有快速启动栏,虽然左侧也有一个类似快速启动的区域,但那个并不是快速启动,而是属于最小化窗口的一部分。以上就是与我们程序有 关的任务栏组成了,小孟这表达能力很有限,大家可能看不懂,so 小孟在下面贴了一张图。
=====================================================================================
Win7:
红色区域:类名是Shell_TrayWnd的容器 黄色区域:类名是ReBarWindow32的二级容器
绿色区域:类名是MSTaskSwWClass的窗口
WinXP:
红色区域:红色区域——类名是Shell_TrayWnd的容器 黄色区域:类名是ReBarWindow32的二级容器
绿色区域:快速启动区域 紫色窗口:类名是MSTaskSwWClass的窗口
=====================================================================================
任务栏组成我们介绍完了,下面我们说说大体的思路。首先,我们要最小化窗口的width缩短,来给我们的程序腾出地方;然后,将我们的程序设置成任务栏的子窗口,以便于嵌入任务栏;最后,调整程序的大小和在任务栏中的位置。
准备工作做了这么多,下面正式开工。
新建一个【Windows窗体应用程序】。将窗体BorderStyle属性改成None来把窗体的边框去掉。然后在窗体上添加一个文本框,如下图所示:
下面开始写代码。
我们先把程序需要用到的API和structure来声明一下(先在类外面加上ImportsSystem.Runtime.InteropServices):
'寻找窗口列表中第一个符合指定条件的顶级窗口 Private Declare Function FindWindow Lib "user32" _ Alias "FindWindowA" (ByVal lpClassName As String, _ ByVal lpWindowName As String) As Integer '在窗口列表中寻找与指定条件相符的第一个子窗口 Private Declare Function FindWindowEx Lib "user32" _ Alias "FindWindowExA" (ByVal hWnd1 As Integer, _ ByVal hWnd2 As Integer, _ ByVal lpsz1 As String, _ ByVal lpsz2 As String) As Integer '指定一个窗口的新父 Private Declare Function SetParent Lib "user32" _ Alias "SetParent" (ByVal hWndChild As Integer, _ ByVal hWndNewParent As Integer) As Integer '改变指定窗口的位置和大小。顶级窗口可能受最大或最小尺寸的限制,那些尺寸优先于这里设置的参数 Private Declare Function MoveWindow Lib "user32" _ Alias "MoveWindow" (ByVal hWnd As IntPtr, _ ByVal x As Integer, _ ByVal y As Integer, _ ByVal nWidth As Integer, _ ByVal nHeight As Integer, _ ByVal bRepaint As Integer) As Integer '获得整个窗口的范围矩形,窗口的边框、标题栏、滚动条及菜单等都在这个矩形内 Private Declare Function GetWindowRect Lib "user32" _ Alias "GetWindowRect" (ByVal hWnd As IntPtr, _ ByRef lpRect As RECT) As Integer <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _ Private Structure RECT Public Left As Integer Public Top As Integer Public Right As Integer Public Bottom As Integer End Structure
为了方便调试,我们先添加程序退出的代码:
Private Sub cmdTextBox_KeyPress(ByVal sender As Object, ByVal e As _ System.Windows.Forms.KeyPressEventArgs) _ Handles cmdTextBox.KeyPress If e.KeyChar = Chr(13) Then If UCase(cmdTextBox.Text) = "END" Then Me.Close() End If End If End Su
这样,我们就可以通过在文本框里输入“end”,然后按回车来退出程序了。
既然要改变最小化窗口的width,那么我们就先来找最小化窗口的Handle,先声明一个全局变量,来储存我们找到的最小化窗口的Handle:
Dim hMin As Long
接下来,我们查找最小化窗口的句柄(下面的代码都在窗体的Load事件里):
Dim hTaskbar, hBar As Long hTaskbar = FindWindow("Shell_TrayWnd", vbNullString) '寻找类名是Shell_TrayWnd的窗口Handle hBar = FindWindowEx(hTaskbar, 0, "ReBarWindow32", vbNullString) '寻来找二级容器的Handle hMin = FindWindowEx(hBar, 0, "MSTaskSwWClass", vbNullString) '寻找最小化窗口的Handle
找到句柄后,我们来获得二级容器的坐标和最小化窗口的坐标:
Dim rcBar As RECT GetWindowRect(hMin, rcMin) '获得最小化窗口的Handle GetWindowRect(hBar, rcBar) '获得二级容器的Handle
下面我们通过用MoveWindow函数来改变窗口的width:
MoveWindow(hMin, 0, 0, rcMin.Right - rcMin.Left - Me.Width, _ rcMin.Bottom - rcMin.Top, True)
已经为任务栏腾出地方了,是时候把程序嵌入到任务栏里了:
SetParent(Me.Handle, hBar) '把程序窗口设置成任务栏的子窗口
调整程序窗口的大小和位置:
Me.Height = cmdTextBox.Height Me.Width = cmdTextBox.Width MoveWindow(Me.Handle, rcMin.Right - rcMin.Left - Me.Width + 2, _ (rcBar.Bottom - rcBar.Top - Me.Height) / 2, _ Me.Width, Me.Height, True)
把程序窗口嵌入到任务栏里的任务已经完成了,但我们还需要处理一下窗体的Closing事件,使程序关闭的时候,把最小化窗口的width恢复回去(下面这一行代码放在窗体的Closing事件里):
MoveWindow(hMin, 0, 0, rcMin.Right - rcMin.Left, rcMin.Bottom - rcMin.Top, True)
终于完成了,现在可以运行看一下效果了,和文章一开始的效果一样吧?