I'm trying to make a simple appbar on the right side of the screen. I have accomplished this many times before.. but I can't seem to get it working in Windows 7. The placement seems right, and as far as I can tell the messages seem to be in the right order. I have tried both Windows.Forms.FormBorderStyle.None as well as Windows.Forms.FormBorderStyle.FixedToolWindow. What am I doing wrong?
Code:
Public Class Form1
Private Declare Function SetWindowPos Lib "user32" Alias "SetWindowPos" (ByVal hwnd As IntPtr, ByVal hWndInsertAfter As Integer, ByVal x As Integer, ByVal y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal wFlags As Integer) As Integer
Public Declare Auto Function MoveWindow Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal X As Int32, ByVal Y As Int32, ByVal nWidth As Int32, ByVal nHeight As Int32, ByVal bRepaint As Boolean) As Boolean
Declare Function SHAppBarMessage Lib "shell32.dll" Alias "SHAppBarMessage" (ByVal dwMessage As Integer, ByRef pData As APPBARDATA) As Integer
Declare Function SetWindowPos Lib "user32.dll" (ByVal hwnd As Integer, ByVal hWndInsertAfter As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal cX As Integer, ByVal cY As Integer, ByVal wFlags As Integer) As Integer
Structure APPBARDATA
Dim cbSize As Integer
Dim hwnd As Integer
Dim uCallbackMessage As [Delegate]
Dim uEdge As Integer
Dim rc As RECT
Dim lParam As Integer ' message specific
End Structure
Structure RECT
Dim Left As Integer
Dim Top As Integer
Dim Right As Integer
Dim Bottom As Integer
End Structure
Const ABE_LEFT As Integer = 0
Const ABE_TOP As Integer = &H1
Const ABE_RIGHT As Integer = 2
Const ABE_BOTTOM As Integer = 3
Const ABM_NEW As Integer = 0
Const ABM_REMOVE As Integer = 1
Const ABM_QUERYPOS As Integer = 2
Const ABM_SETPOS As Integer = &H3
Const ABM_GETSTATE As Integer = 4
Const ABM_GETTASKBARPOS As Integer = 5
Const ABM_ACTIVATE As Integer = 6
Const ABM_GETAUTOHIDEBAR As Integer = 7
Const ABM_SETAUTOHIDEBAR As Integer = 8
Const ABM_WINDOWPOSCHANGED As Integer = 9
Const ABS_AUTOHIDE As Integer = 1
Const ABS_ALWAYSONTOP As Integer = 2
Const HWND_NOTTOPMOST As Integer = -2
Const HWND_TOPMOST As Integer = -1
Const HWND_TOP As Integer = 0
Const SHOWNORMAL As Integer = 5
Const SWP_NOSIZE As Integer = &H1
Const SWP_NOMOVE As Short = &H2
Const SWP_NOZORDER As Integer = 4
Const SWP_NOACTIVATE As Integer = &H10
Const SWP_DRAWFRAME As Integer = &H20
Const SWP_SHOWWINDOW As Integer = &H40
Dim abd As APPBARDATA
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.TopMost = True
Me.Size = New Size(150, 350)
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedToolWindow
Dim combinedwidth As Integer = Screen.PrimaryScreen.WorkingArea.Width
Dim height As Integer = Screen.PrimaryScreen.WorkingArea.Height
Dim top As Integer = 0
SHAppBarMessage(ABM_NEW, abd)
abd.hwnd = Me.Handle.ToInt32
abd.cbSize = Len(abd)
With abd
.uEdge = ABE_RIGHT
.rc.Top = top
.rc.Right = (combinedwidth)
.rc.Left = .rc.Right - Me.Width
.rc.Bottom = height
SHAppBarMessage(ABM_QUERYPOS, abd)
'abd.rc.Left = abd.rc.Right - Me.Width
SHAppBarMessage(ABM_SETPOS, abd)
'MoveWindow(abd.hwnd, .rc.Left, .rc.Top, .rc.Right - .rc.Left, .rc.Bottom, True)
SetWindowPos(abd.hwnd, HWND_TOP, .rc.Left, .rc.Top, .rc.Right - .rc.Left, .rc.Bottom, SWP_SHOWWINDOW Or SWP_NOACTIVATE)
End With
End Sub
End Class
I think it's a timing issue. This works for me on Windows 7 Ultimate.
Code:
Const ABE_LEFT As Int32 = 0
Const ABE_TOP As Int32 = 1
Const ABE_RIGHT As Int32 = 2
Const ABE_BOTTOM As Int32 = 3
Const ABM_NEW As Int32 = 0
Const ABM_REMOVE As Int32 = 1
Const ABM_QUERYPOS As Int32 = 2
Const ABM_SETPOS As Int32 = 3
Const ABM_GETSTATE As Int32 = 4
Const ABM_GETTASKBARPOS As Int32 = 5
Const ABM_ACTIVATE As Int32 = 6
Const ABM_GETAUTOHIDEBAR As Int32 = 7
Const ABM_SETAUTOHIDEBAR As Int32 = 8
Const ABM_WINDOWPOSCHANGED As Int32 = 9
Const ABS_AUTOHIDE As Int32 = 1
Const ABS_ALWAYSONTOP As Int32 = 2
Const HWND_NOTTOPMOST As Int32 = -2
Const HWND_TOPMOST As Int32 = -1
Const HWND_TOP As Int32 = 0
Const SHOWNORMAL As Int32 = 5
Private Structure APPBARDATA
Public cbSize As Int32
Public hwnd As IntPtr
Public uCallbackMessage As [Delegate]
Public uEdge As Int32
Public rc As RECT
Public lParam As Int32
End Structure
Private Structure RECT
Public rLeft As Int32
Public rTop As Int32
Public rRight As Int32
Public rBottom As Int32
End Structure
Private Enum EDGE As Int32
Left = ABE_LEFT
Top = ABE_TOP
Right = ABE_RIGHT
Bottom = ABE_BOTTOM
End Enum
Private Declare Function apiMoveWindow Lib "user32" Alias "MoveWindow" (ByVal hWnd As IntPtr, ByVal X As Int32, ByVal Y As Int32, ByVal nWidth As Int32, ByVal nHeight As Int32, ByVal bRepaint As Boolean) As Boolean
Private Declare Function apiSHAppBarMessage Lib "shell32" Alias "SHAppBarMessage" (ByVal dwMessage As Int32, ByRef pData As APPBARDATA) As Int32
Private abd As New APPBARDATA
Private Sub Form1_MouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDoubleClick
Me.Close()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
DockToEdge(EDGE.Right)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
DockToEdge(EDGE.Left)
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
DockToEdge(EDGE.Top)
End Sub
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
DockToEdge(EDGE.Bottom)
End Sub
Private Sub DockToEdge(ByVal edge As Int32)
On Error Resume Next
apiSHAppBarMessage(ABM_REMOVE, abd)
abd = New APPBARDATA
abd.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(abd)
abd.hwnd = Me.Handle
abd.uEdge = edge
If edge = ABE_LEFT OrElse edge = ABE_RIGHT Then
Me.Height = Screen.PrimaryScreen.WorkingArea.Height
Me.Width = 75
abd.rc.rTop = Screen.PrimaryScreen.WorkingArea.Top : abd.rc.rBottom = Screen.PrimaryScreen.WorkingArea.Bottom
If edge = ABE_LEFT Then
abd.rc.rLeft = Screen.PrimaryScreen.WorkingArea.Left
abd.rc.rRight = abd.rc.rLeft + 75
Else
abd.rc.rRight = Screen.PrimaryScreen.WorkingArea.Right
abd.rc.rLeft = abd.rc.rRight - 75
End If
ElseIf edge = ABE_TOP OrElse edge = ABE_BOTTOM Then
Me.Width = Screen.PrimaryScreen.WorkingArea.Width
Me.Height = 25
abd.rc.rLeft = Screen.PrimaryScreen.WorkingArea.Left
abd.rc.rRight = Screen.PrimaryScreen.WorkingArea.Right
If edge = ABE_TOP Then
abd.rc.rTop = Screen.PrimaryScreen.WorkingArea.Top
abd.rc.rBottom = abd.rc.rTop + 25
Else
abd.rc.rBottom = Screen.PrimaryScreen.WorkingArea.Bottom
abd.rc.rTop = abd.rc.rBottom - 25
End If
End If
apiSHAppBarMessage(ABM_NEW, abd)
apiSHAppBarMessage(ABM_SETPOS, abd)
Application.DoEvents() 'This seemed to work, although a thread also works
apiMoveWindow(abd.hwnd, abd.rc.rLeft, abd.rc.rTop, abd.rc.rRight - abd.rc.rLeft, abd.rc.rBottom - abd.rc.rTop, True)
End Sub
Private Sub MoveFormOnThread()
Threading.Thread.Sleep(25)
apiMoveWindow(abd.hwnd, abd.rc.rLeft, abd.rc.rTop, abd.rc.rRight - abd.rc.rLeft, abd.rc.rBottom - abd.rc.rTop, True)
End Sub
Last edited by TT(n); August 26th, 2011 at 03:03 AM.
It worked in Form_Load too, but this was just my sample with buttons to be simple. The problem was in the MoveWindow return value. I believe calling this function to early overlaps with the previous call to ABM_SETPOS, and so the work area is not ready yet. Calling ABM_WINDOWPOSCHANGED did not seem to flush anything although DoEvents does, unless you prefer a thread to move it.
Here is a sample with gradient look to it, more like the real taskbar.
Last edited by TT(n); August 26th, 2011 at 02:41 PM.
Reason: Add example file
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.