Wie funktioniert AppBar Docking (Bildschirmrand wie WinAmp) in WPF?

Gibt es eine vollständige Anleitung zum Andocken von AppBar (z. B. Sperren an die Bildschirmkante) in WPF? Ich verstehe, dass es InterOp Aufrufe gibt, die vorgenommen werden müssen, aber ich suche nach einem Proof of Concept, das auf einem einfachen WPF-Formular oder einer komponentenbasierten Version basiert, die konsumiert werden kann.

Ähnliche Resourcen:

  • http://www.codeproject.com/KB/dotnet/AppBar.aspx
  • http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/05c73c9c-e85d-4ecd-b9b6-4c714a65e72b/

Bitte beachten Sie: Diese Frage hat eine gute Menge an Feedback gesammelt, und einige Leute unten haben tolle Punkte oder Fixes gemacht. Daher werde ich zwar den Code hier behalten (und ihn möglicherweise aktualisieren), aber ich habe auch ein WpfAppBar-Projekt auf github erstellt . Zögern Sie nicht, Pull-Anfragen zu senden.

Das gleiche Projekt baut auch zu einem WpfAppBar nugget-Paket auf


Ich nahm den Code aus dem ersten Link in der Frage ( http://www.codeproject.com/KB/dotnet/AppBar.aspx ) und änderte es um zwei Dinge zu tun:

  1. Arbeit mit WPF
  2. Sei “Standalone” – Wenn Sie diese einzelne Datei in Ihr Projekt einfügen, können Sie AppBarFunctions.SetAppBar (…) ohne weitere Änderungen am Fenster aufrufen.

Dieser Ansatz erstellt keine Basisklasse.

Um zu verwenden, rufen Sie einfach diesen Code von irgendwo innerhalb eines normalen wpf-Fensters auf (sagen Sie einen Knopfklick oder das Initialisieren). Beachten Sie, dass Sie dies nicht aufrufen können, bis NACH dem Initialisieren des Fensters, falls das HWND noch nicht erstellt wurde (wie im Konstruktor), ein Fehler auftritt.

Machen Sie das Fenster zu einer Appbar:

AppBarFunctions.SetAppBar( this, ABEdge.Right ); 

Stellen Sie das Fenster in einem normalen Fenster wieder her:

 AppBarFunctions.SetAppBar( this, ABEdge.None ); 

Hier ist der vollständige Code für die Datei – beachten Sie, dass Sie den Namespace in Zeile 7 auf etwas entsprechendes ändern möchten.

 using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; using System.Windows.Threading; namespace AppBarApplication { public enum ABEdge : int { Left = 0, Top, Right, Bottom, None } internal static class AppBarFunctions { [StructLayout(LayoutKind.Sequential)] private struct RECT { public int left; public int top; public int right; public int bottom; } [StructLayout(LayoutKind.Sequential)] private struct APPBARDATA { public int cbSize; public IntPtr hWnd; public int uCallbackMessage; public int uEdge; public RECT rc; public IntPtr lParam; } private enum ABMsg : int { ABM_NEW = 0, ABM_REMOVE, ABM_QUERYPOS, ABM_SETPOS, ABM_GETSTATE, ABM_GETTASKBARPOS, ABM_ACTIVATE, ABM_GETAUTOHIDEBAR, ABM_SETAUTOHIDEBAR, ABM_WINDOWPOSCHANGED, ABM_SETSTATE } private enum ABNotify : int { ABN_STATECHANGE = 0, ABN_POSCHANGED, ABN_FULLSCREENAPP, ABN_WINDOWARRANGE } [DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)] private static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData); [DllImport("User32.dll", CharSet = CharSet.Auto)] private static extern int RegisterWindowMessage(string msg); private class RegisterInfo { public int CallbackId { get; set; } public bool IsRegistered { get; set; } public Window Window { get; set; } public ABEdge Edge { get; set; } public WindowStyle OriginalStyle { get; set; } public Point OriginalPosition { get; set; } public Size OriginalSize { get; set; } public ResizeMode OriginalResizeMode { get; set; } public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == CallbackId) { if (wParam.ToInt32() == (int)ABNotify.ABN_POSCHANGED) { ABSetPos(Edge, Window); handled = true; } } return IntPtr.Zero; } } private static Dictionary s_RegisteredWindowInfo = new Dictionary(); private static RegisterInfo GetRegisterInfo(Window appbarWindow) { RegisterInfo reg; if( s_RegisteredWindowInfo.ContainsKey(appbarWindow)) { reg = s_RegisteredWindowInfo[appbarWindow]; } else { reg = new RegisterInfo() { CallbackId = 0, Window = appbarWindow, IsRegistered = false, Edge = ABEdge.Top, OriginalStyle = appbarWindow.WindowStyle, OriginalPosition =new Point( appbarWindow.Left, appbarWindow.Top), OriginalSize = new Size( appbarWindow.ActualWidth, appbarWindow.ActualHeight), OriginalResizeMode = appbarWindow.ResizeMode, }; s_RegisteredWindowInfo.Add(appbarWindow, reg); } return reg; } private static void RestoreWindow(Window appbarWindow) { RegisterInfo info = GetRegisterInfo(appbarWindow); appbarWindow.WindowStyle = info.OriginalStyle; appbarWindow.ResizeMode = info.OriginalResizeMode; appbarWindow.Topmost = false; Rect rect = new Rect(info.OriginalPosition.X, info.OriginalPosition.Y, info.OriginalSize.Width, info.OriginalSize.Height); appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new ResizeDelegate(DoResize), appbarWindow, rect); } public static void SetAppBar(Window appbarWindow, ABEdge edge) { RegisterInfo info = GetRegisterInfo(appbarWindow); info.Edge = edge; APPBARDATA abd = new APPBARDATA(); abd.cbSize = Marshal.SizeOf(abd); abd.hWnd = new WindowInteropHelper(appbarWindow).Handle; if( edge == ABEdge.None) { if( info.IsRegistered) { SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd); info.IsRegistered = false; } RestoreWindow(appbarWindow); return; } if (!info.IsRegistered) { info.IsRegistered = true; info.CallbackId = RegisterWindowMessage("AppBarMessage"); abd.uCallbackMessage = info.CallbackId; uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd); HwndSource source = HwndSource.FromHwnd(abd.hWnd); source.AddHook(new HwndSourceHook(info.WndProc)); } appbarWindow.WindowStyle = WindowStyle.None; appbarWindow.ResizeMode = ResizeMode.NoResize; appbarWindow.Topmost = true; ABSetPos(info.Edge, appbarWindow); } private delegate void ResizeDelegate(Window appbarWindow, Rect rect); private static void DoResize(Window appbarWindow, Rect rect) { appbarWindow.Width = rect.Width; appbarWindow.Height = rect.Height; appbarWindow.Top = rect.Top; appbarWindow.Left = rect.Left; } private static void ABSetPos(ABEdge edge, Window appbarWindow) { APPBARDATA barData = new APPBARDATA(); barData.cbSize = Marshal.SizeOf(barData); barData.hWnd = new WindowInteropHelper(appbarWindow).Handle; barData.uEdge = (int)edge; if (barData.uEdge == (int)ABEdge.Left || barData.uEdge == (int)ABEdge.Right) { barData.rc.top = 0; barData.rc.bottom = (int)SystemParameters.PrimaryScreenHeight; if (barData.uEdge == (int)ABEdge.Left) { barData.rc.left = 0; barData.rc.right = (int)Math.Round(appbarWindow.ActualWidth); } else { barData.rc.right = (int)SystemParameters.PrimaryScreenWidth; barData.rc.left = barData.rc.right - (int)Math.Round(appbarWindow.ActualWidth); } } else { barData.rc.left = 0; barData.rc.right = (int)SystemParameters.PrimaryScreenWidth; if (barData.uEdge == (int)ABEdge.Top) { barData.rc.top = 0; barData.rc.bottom = (int)Math.Round(appbarWindow.ActualHeight); } else { barData.rc.bottom = (int)SystemParameters.PrimaryScreenHeight; barData.rc.top = barData.rc.bottom - (int)Math.Round(appbarWindow.ActualHeight); } } SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData); SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData); Rect rect = new Rect((double)barData.rc.left, (double)barData.rc.top, (double)(barData.rc.right - barData.rc.left), (double)(barData.rc.bottom - barData.rc.top)); //This is done async, because WPF will send a resize after a new appbar is added. //if we size right away, WPFs resize comes last and overrides us. appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new ResizeDelegate(DoResize), appbarWindow, rect); } } } 

Sehr glücklich, diese Frage gefunden zu haben. Die obige class ist sehr nützlich, deckt jedoch nicht alle Grundlagen der AppBar-Implementierung ab.

Um das gesamte Verhalten einer AppBar (mit Vollbild-Apps usw.) vollständig zu implementieren, sollten Sie auch diesen MSDN-Artikel lesen.

http://msdn.microsoft.com/en-us/library/bb776821.aspx

Entschuldigung für mein Englisch … Hier ist die Lösung von Philip Rieck mit einigen Korrektionen. Es funktioniert korrekt mit Positions- und Größenänderungen der Taskleiste.

 using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; using System.Windows.Threading; namespace wpf_appbar { public enum ABEdge : int { Left, Top, Right, Bottom, None } internal static class AppBarFunctions { [StructLayout(LayoutKind.Sequential)] private struct RECT { public int Left; public int Top; public int Right; public int Bottom; public RECT(Rect r) { Left = (int)r.Left; Right = (int)r.Right; Top = (int)r.Top; Bottom = (int)r.Bottom; } public static bool operator ==(RECT r1, RECT r2) { return r1.Bottom == r2.Bottom && r1.Left == r2.Left && r1.Right == r2.Right && r1.Top == r2.Top; } public static bool operator !=(RECT r1, RECT r2) { return !(r1 == r2); } public override bool Equals(object obj) { return base.Equals(obj); } public override int GetHashCode() { return base.GetHashCode(); } } [StructLayout(LayoutKind.Sequential)] private struct APPBARDATA { public int cbSize; public IntPtr hWnd; public int uCallbackMessage; public int uEdge; public RECT rc; public IntPtr lParam; } private enum ABMsg : int { ABM_NEW = 0, ABM_REMOVE, ABM_QUERYPOS, ABM_SETPOS, ABM_GETSTATE, ABM_GETTASKBARPOS, ABM_ACTIVATE, ABM_GETAUTOHIDEBAR, ABM_SETAUTOHIDEBAR, ABM_WINDOWPOSCHANGED, ABM_SETSTATE } private enum ABNotify : int { ABN_STATECHANGE = 0, ABN_POSCHANGED, ABN_FULLSCREENAPP, ABN_WINDOWARRANGE } private enum TaskBarPosition : int { Left, Top, Right, Bottom } [StructLayout(LayoutKind.Sequential)] class TaskBar { public TaskBarPosition Position; public TaskBarPosition PreviousPosition; public RECT Rectangle; public RECT PreviousRectangle; public int Width; public int PreviousWidth; public int Height; public int PreviousHeight; public TaskBar() { Refresh(); } public void Refresh() { APPBARDATA msgData = new APPBARDATA(); msgData.cbSize = Marshal.SizeOf(msgData); SHAppBarMessage((int)ABMsg.ABM_GETTASKBARPOS, ref msgData); PreviousPosition = Position; PreviousRectangle = Rectangle; PreviousHeight = Height; PreviousWidth = Width; Rectangle = msgData.rc; Width = Rectangle.Right - Rectangle.Left; Height = Rectangle.Bottom - Rectangle.Top; int h = (int)SystemParameters.PrimaryScreenHeight; int w = (int)SystemParameters.PrimaryScreenWidth; if (Rectangle.Bottom == h && Rectangle.Top != 0) Position = TaskBarPosition.Bottom; else if (Rectangle.Top == 0 && Rectangle.Bottom != h) Position = TaskBarPosition.Top; else if (Rectangle.Right == w && Rectangle.Left != 0) Position = TaskBarPosition.Right; else if (Rectangle.Left == 0 && Rectangle.Right != w) Position = TaskBarPosition.Left; } } [DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)] private static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData); [DllImport("User32.dll", CharSet = CharSet.Auto)] private static extern int RegisterWindowMessage(string msg); private class RegisterInfo { public int CallbackId { get; set; } public bool IsRegistered { get; set; } public Window Window { get; set; } public ABEdge Edge { get; set; } public ABEdge PreviousEdge { get; set; } public WindowStyle OriginalStyle { get; set; } public Point OriginalPosition { get; set; } public Size OriginalSize { get; set; } public ResizeMode OriginalResizeMode { get; set; } public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == CallbackId) { if (wParam.ToInt32() == (int)ABNotify.ABN_POSCHANGED) { PreviousEdge = Edge; ABSetPos(Edge, PreviousEdge, Window); handled = true; } } return IntPtr.Zero; } } private static Dictionary s_RegisteredWindowInfo = new Dictionary(); private static RegisterInfo GetRegisterInfo(Window appbarWindow) { RegisterInfo reg; if (s_RegisteredWindowInfo.ContainsKey(appbarWindow)) { reg = s_RegisteredWindowInfo[appbarWindow]; } else { reg = new RegisterInfo() { CallbackId = 0, Window = appbarWindow, IsRegistered = false, Edge = ABEdge.None, PreviousEdge = ABEdge.None, OriginalStyle = appbarWindow.WindowStyle, OriginalPosition = new Point(appbarWindow.Left, appbarWindow.Top), OriginalSize = new Size(appbarWindow.ActualWidth, appbarWindow.ActualHeight), OriginalResizeMode = appbarWindow.ResizeMode, }; s_RegisteredWindowInfo.Add(appbarWindow, reg); } return reg; } private static void RestoreWindow(Window appbarWindow) { RegisterInfo info = GetRegisterInfo(appbarWindow); appbarWindow.WindowStyle = info.OriginalStyle; appbarWindow.ResizeMode = info.OriginalResizeMode; appbarWindow.Topmost = false; Rect rect = new Rect(info.OriginalPosition.X, info.OriginalPosition.Y, info.OriginalSize.Width, info.OriginalSize.Height); appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new ResizeDelegate(DoResize), appbarWindow, rect); } public static void SetAppBar(Window appbarWindow, ABEdge edge) { RegisterInfo info = GetRegisterInfo(appbarWindow); info.Edge = edge; APPBARDATA abd = new APPBARDATA(); abd.cbSize = Marshal.SizeOf(abd); abd.hWnd = new WindowInteropHelper(appbarWindow).Handle; if (edge == ABEdge.None) { if (info.IsRegistered) { SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd); info.IsRegistered = false; } RestoreWindow(appbarWindow); info.PreviousEdge = info.Edge; return; } if (!info.IsRegistered) { info.IsRegistered = true; info.CallbackId = RegisterWindowMessage("AppBarMessage"); abd.uCallbackMessage = info.CallbackId; uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd); HwndSource source = HwndSource.FromHwnd(abd.hWnd); source.AddHook(new HwndSourceHook(info.WndProc)); } appbarWindow.WindowStyle = WindowStyle.None; appbarWindow.ResizeMode = ResizeMode.NoResize; appbarWindow.Topmost = true; ABSetPos(info.Edge, info.PreviousEdge, appbarWindow); } private delegate void ResizeDelegate(Window appbarWindow, Rect rect); private static void DoResize(Window appbarWindow, Rect rect) { appbarWindow.Width = rect.Width; appbarWindow.Height = rect.Height; appbarWindow.Top = rect.Top; appbarWindow.Left = rect.Left; } static TaskBar tb = new TaskBar(); private static void ABSetPos(ABEdge edge, ABEdge prevEdge, Window appbarWindow) { APPBARDATA barData = new APPBARDATA(); barData.cbSize = Marshal.SizeOf(barData); barData.hWnd = new WindowInteropHelper(appbarWindow).Handle; barData.uEdge = (int)edge; RECT wa = new RECT(SystemParameters.WorkArea); tb.Refresh(); switch (edge) { case ABEdge.Top: barData.rc.Left = wa.Left - (prevEdge == ABEdge.Left ? (int)Math.Round(appbarWindow.ActualWidth) : 0); barData.rc.Right = wa.Right + (prevEdge == ABEdge.Right ? (int)Math.Round(appbarWindow.ActualWidth) : 0); barData.rc.Top = wa.Top - (prevEdge == ABEdge.Top ? (int)Math.Round(appbarWindow.ActualHeight) : 0) - ((tb.Position != TaskBarPosition.Top && tb.PreviousPosition == TaskBarPosition.Top) ? tb.Height : 0) + ((tb.Position == TaskBarPosition.Top && tb.PreviousPosition != TaskBarPosition.Top) ? tb.Height : 0); barData.rc.Bottom = barData.rc.Top + (int)Math.Round(appbarWindow.ActualHeight); break; case ABEdge.Bottom: barData.rc.Left = wa.Left - (prevEdge == ABEdge.Left ? (int)Math.Round(appbarWindow.ActualWidth) : 0); barData.rc.Right = wa.Right + (prevEdge == ABEdge.Right ? (int)Math.Round(appbarWindow.ActualWidth) : 0); barData.rc.Bottom = wa.Bottom + (prevEdge == ABEdge.Bottom ? (int)Math.Round(appbarWindow.ActualHeight) : 0) - 1 + ((tb.Position != TaskBarPosition.Bottom && tb.PreviousPosition == TaskBarPosition.Bottom) ? tb.Height : 0) - ((tb.Position == TaskBarPosition.Bottom && tb.PreviousPosition != TaskBarPosition.Bottom) ? tb.Height : 0); barData.rc.Top = barData.rc.Bottom - (int)Math.Round(appbarWindow.ActualHeight); break; } SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData); switch (barData.uEdge) { case (int)ABEdge.Bottom: if (tb.Position == TaskBarPosition.Bottom && tb.PreviousPosition == tb.Position) { barData.rc.Top += (tb.PreviousHeight - tb.Height); barData.rc.Bottom = barData.rc.Top + (int)appbarWindow.ActualHeight; } break; case (int)ABEdge.Top: if (tb.Position == TaskBarPosition.Top && tb.PreviousPosition == tb.Position) { if (tb.PreviousHeight - tb.Height > 0) barData.rc.Top -= (tb.PreviousHeight - tb.Height); barData.rc.Bottom = barData.rc.Top + (int)appbarWindow.ActualHeight; } break; } SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData); Rect rect = new Rect((double)barData.rc.Left, (double)barData.rc.Top, (double)(barData.rc.Right - barData.rc.Left), (double)(barData.rc.Bottom - barData.rc.Top)); appbarWindow.Dispatcher.BeginInvoke(new ResizeDelegate(DoResize), DispatcherPriority.ApplicationIdle, appbarWindow, rect); } } } 

Der gleiche Code, den Sie für die linken und rechten Kanten schreiben können. Gute Arbeit, Philip Rieck, danke!

Ich habe Code von Philip Rieck geändert (übrigens vielmals) um in mehreren Displayeinstellungen zu arbeiten. Hier ist meine Lösung.

 using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; using System.Windows.Threading; namespace AppBarApplication { public enum ABEdge : int { Left = 0, Top, Right, Bottom, None } internal static class AppBarFunctions { [StructLayout(LayoutKind.Sequential)] private struct RECT { public int left; public int top; public int right; public int bottom; } [StructLayout(LayoutKind.Sequential)] private struct APPBARDATA { public int cbSize; public IntPtr hWnd; public int uCallbackMessage; public int uEdge; public RECT rc; public IntPtr lParam; } [StructLayout(LayoutKind.Sequential)] private struct MONITORINFO { public int cbSize; public RECT rcMonitor; public RECT rcWork; public int dwFlags; } private enum ABMsg : int { ABM_NEW = 0, ABM_REMOVE, ABM_QUERYPOS, ABM_SETPOS, ABM_GETSTATE, ABM_GETTASKBARPOS, ABM_ACTIVATE, ABM_GETAUTOHIDEBAR, ABM_SETAUTOHIDEBAR, ABM_WINDOWPOSCHANGED, ABM_SETSTATE } private enum ABNotify : int { ABN_STATECHANGE = 0, ABN_POSCHANGED, ABN_FULLSCREENAPP, ABN_WINDOWARRANGE } [DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)] private static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData); [DllImport("User32.dll", CharSet = CharSet.Auto)] private static extern int RegisterWindowMessage(string msg); [DllImport("User32.dll", CharSet = CharSet.Auto)] private static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags); [DllImport("User32.dll", CharSet = CharSet.Auto)] private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO mi); private const int MONITOR_DEFAULTTONEAREST = 0x2; private const int MONITORINFOF_PRIMARY = 0x1; private class RegisterInfo { public int CallbackId { get; set; } public bool IsRegistered { get; set; } public Window Window { get; set; } public ABEdge Edge { get; set; } public WindowStyle OriginalStyle { get; set; } public Point OriginalPosition { get; set; } public Size OriginalSize { get; set; } public ResizeMode OriginalResizeMode { get; set; } public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == CallbackId) { if (wParam.ToInt32() == (int)ABNotify.ABN_POSCHANGED) { ABSetPos(Edge, Window); handled = true; } } return IntPtr.Zero; } } private static Dictionary s_RegisteredWindowInfo = new Dictionary(); private static RegisterInfo GetRegisterInfo(Window appbarWindow) { RegisterInfo reg; if (s_RegisteredWindowInfo.ContainsKey(appbarWindow)) { reg = s_RegisteredWindowInfo[appbarWindow]; } else { reg = new RegisterInfo() { CallbackId = 0, Window = appbarWindow, IsRegistered = false, Edge = ABEdge.Top, OriginalStyle = appbarWindow.WindowStyle, OriginalPosition = new Point(appbarWindow.Left, appbarWindow.Top), OriginalSize = new Size(appbarWindow.ActualWidth, appbarWindow.ActualHeight), OriginalResizeMode = appbarWindow.ResizeMode, }; s_RegisteredWindowInfo.Add(appbarWindow, reg); } return reg; } private static void RestoreWindow(Window appbarWindow) { RegisterInfo info = GetRegisterInfo(appbarWindow); appbarWindow.WindowStyle = info.OriginalStyle; appbarWindow.ResizeMode = info.OriginalResizeMode; appbarWindow.Topmost = false; Rect rect = new Rect(info.OriginalPosition.X, info.OriginalPosition.Y, info.OriginalSize.Width, info.OriginalSize.Height); appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new ResizeDelegate(DoResize), appbarWindow, rect); } public static void SetAppBar(Window appbarWindow, ABEdge edge) { RegisterInfo info = GetRegisterInfo(appbarWindow); info.Edge = edge; APPBARDATA abd = new APPBARDATA(); abd.cbSize = Marshal.SizeOf(abd); abd.hWnd = new WindowInteropHelper(appbarWindow).Handle; if (edge == ABEdge.None) { if (info.IsRegistered) { SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd); info.IsRegistered = false; } RestoreWindow(appbarWindow); return; } if (!info.IsRegistered) { info.IsRegistered = true; info.CallbackId = RegisterWindowMessage("AppBarMessage"); abd.uCallbackMessage = info.CallbackId; uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd); HwndSource source = HwndSource.FromHwnd(abd.hWnd); source.AddHook(new HwndSourceHook(info.WndProc)); } appbarWindow.WindowStyle = WindowStyle.None; appbarWindow.ResizeMode = ResizeMode.NoResize; appbarWindow.Topmost = true; ABSetPos(info.Edge, appbarWindow); } private delegate void ResizeDelegate(Window appbarWindow, Rect rect); private static void DoResize(Window appbarWindow, Rect rect) { appbarWindow.Width = rect.Width; appbarWindow.Height = rect.Height; appbarWindow.Top = rect.Top; appbarWindow.Left = rect.Left; } private static void GetActualScreenData(ABEdge edge, Window appbarWindow, ref int leftOffset, ref int topOffset, ref int actualScreenWidth, ref int actualScreenHeight) { IntPtr handle = new WindowInteropHelper(appbarWindow).Handle; IntPtr monitorHandle = MonitorFromWindow(handle, MONITOR_DEFAULTTONEAREST); MONITORINFO mi = new MONITORINFO(); mi.cbSize = Marshal.SizeOf(mi); if (GetMonitorInfo(monitorHandle, ref mi)) { if (mi.dwFlags == MONITORINFOF_PRIMARY) { return; } leftOffset = mi.rcWork.left; topOffset = mi.rcWork.top; actualScreenWidth = mi.rcWork.right - leftOffset; actualScreenHeight = mi.rcWork.bottom - mi.rcWork.top; } } private static void ABSetPos(ABEdge edge, Window appbarWindow) { APPBARDATA barData = new APPBARDATA(); barData.cbSize = Marshal.SizeOf(barData); barData.hWnd = new WindowInteropHelper(appbarWindow).Handle; barData.uEdge = (int)edge; int leftOffset = 0; int topOffset = 0; int actualScreenWidth = (int)SystemParameters.PrimaryScreenWidth; int actualScreenHeight = (int)SystemParameters.PrimaryScreenHeight; GetActualScreenData(edge, appbarWindow, ref leftOffset, ref topOffset, ref actualScreenWidth, ref actualScreenHeight); if (barData.uEdge == (int)ABEdge.Left || barData.uEdge == (int)ABEdge.Right) { barData.rc.top = topOffset; barData.rc.bottom = actualScreenHeight; if (barData.uEdge == (int)ABEdge.Left) { barData.rc.left = leftOffset; barData.rc.right = (int)Math.Round(appbarWindow.ActualWidth) + leftOffset; } else { barData.rc.right = actualScreenWidth + leftOffset; barData.rc.left = barData.rc.right - (int)Math.Round(appbarWindow.ActualWidth); } } else { barData.rc.left = leftOffset; barData.rc.right = actualScreenWidth + leftOffset; if (barData.uEdge == (int)ABEdge.Top) { barData.rc.top = topOffset; barData.rc.bottom = (int)Math.Round(appbarWindow.ActualHeight) + topOffset; } else { barData.rc.bottom = actualScreenHeight + topOffset; barData.rc.top = barData.rc.bottom - (int)Math.Round(appbarWindow.ActualHeight); } } SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData); SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData); Rect rect = new Rect((double)barData.rc.left, (double)barData.rc.top, (double)(barData.rc.right - barData.rc.left), (double)(barData.rc.bottom - barData.rc.top)); //This is done async, because WPF will send a resize after a new appbar is added. //if we size right away, WPFs resize comes last and overrides us. appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new ResizeDelegate(DoResize), appbarWindow, rect); } } } 

Es gibt einen exzellenten MSDN-Artikel von 1996, der unterhaltsam auf dem neuesten Stand ist: Erweitern Sie die Windows 95 Shell um Application Desktop Toolbars . Der Anleitung folgt eine WPF-basierte Appbar, die eine Reihe von Szenarien behandelt, die der andere auf dieser Seite nicht beantwortet:

  • Erlaube Dock auf eine beliebige Seite des Bildschirms
  • Dock auf einem bestimmten Monitor zulassen
  • Erlaube die Größenänderung der Appbar (falls gewünscht)
  • Änderungen am Bildschirmlayout vornehmen und Verbindungsabbrüche überwachen
  • Handle Win + Shift + Links und versucht, das Fenster zu minimieren oder zu verschieben
  • Zusammenarbeit mit anderen Appbars (OneNote et al.)
  • Behandeln Sie die DPI-Skalierung pro Monitor

Ich habe sowohl eine Demo-App als auch die Implementierung von AppBarWindow auf GitHub .

Beispiel Verwendung:

         

Codebehind:

 public partial class MainWindow { public MainWindow() { InitializeComponent(); this.cbEdge.ItemsSource = new[] { AppBarDockMode.Left, AppBarDockMode.Right, AppBarDockMode.Top, AppBarDockMode.Bottom }; this.cbMonitor.ItemsSource = MonitorInfo.GetAllMonitors(); } private void btClose_Click(object sender, RoutedEventArgs e) { Close(); } private void rzThumb_DragCompleted(object sender, DragCompletedEventArgs e) { this.DockedWidthOrHeight += (int)(e.HorizontalChange / VisualTreeHelper.GetDpi(this).PixelsPerDip); } } 

Angekoppelte Position ändern:

AppBar an Kanten angedockt

Größenanpassung mit dem Daumen:

Größe ändern

Zusammenarbeit mit anderen Appbars:

Koordinierung

Klonen Sie von GitHub, wenn Sie es verwenden möchten. Die Bibliothek selbst besteht nur aus drei Dateien und kann in einem Projekt einfach gelöscht werden.

Entschuldigung, der letzte Code, den ich gepostet habe, funktionierte nicht, wenn die Größe der Taskleiste geändert wurde. Die folgende Codeänderung scheint besser zu funktionieren:

  SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData); if (barData.uEdge == (int)ABEdge.Top) barData.rc.bottom = barData.rc.top + (int)Math.Round(appbarWindow.ActualHeight); else if (barData.uEdge == (int)ABEdge.Bottom) barData.rc.top = barData.rc.bottom - (int)Math.Round(appbarWindow.ActualHeight); SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData); 

Eine kommerzielle Alternative finden Sie in der gebrauchsfertigen ShellAppBar- Komponente für WPF, die alle Fälle und Secnarios unterstützt, wie z. B. Taskleiste links, rechts, oben, unten, Unterstützung für mehrere Monitore, Drag-Docking, Autohide usw. Es kann Ihnen Zeit und Geld sparen, wenn Sie versuchen, all diese Fälle selbst zu behandeln.

HAFTUNGSAUSSCHLUSS : Ich arbeite für LogicNP Software, den Entwickler von ShellAppBar.

Ich habe einige Wochen damit verbracht, diese Herausforderung zu erforschen, und schließlich ein sehr solides NuGet-Paket entwickelt, das diese functionalität auf sehr freundliche Art und Weise liefert. Erstellen Sie einfach eine neue WPF-App und ändern Sie dann die class des Hauptfensters von Window in DockWindow (im XAML) und das war’s!

Holen Sie sich das Paket hier und sehen Sie sich das Git Repo für eine Demo-App an.