Come si fa l’AppBar docking (a bordo schermo, come WinAmp) in WPF?

Esiste una guida completa su come eseguire AppBar docking (come il blocco sul bordo dello schermo) in WPF? Capisco che ci sono chiamate InterOp che devono essere fatte, ma sto cercando una prova di concetto basata su un semplice modulo WPF, o una versione componentizzata che può essere consumata.

Risorse correlate:

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

Nota: questa domanda ha raccolto una buona quantità di feedback, e alcune persone di seguito hanno fatto ottimi punti o correzioni. Pertanto, mentre terrò qui il codice (e probabilmente lo aggiornerò), ho anche creato un progetto WpfAppBar su github . Sentiti libero di inviare richieste di pull.

Lo stesso progetto crea anche un pacchetto nuget WpfAppBar


Ho preso il codice dal primo collegamento fornito nella domanda ( http://www.codeproject.com/KB/dotnet/AppBar.aspx ) e l’ho modificato per fare due cose:

  1. Lavora con WPF
  2. Sii “autonomo”: se metti questo singolo file nel tuo progetto, puoi chiamare AppBarFunctions.SetAppBar (…) senza ulteriori modifiche alla finestra.

Questo approccio non crea una class base.

Per usare, basta chiamare questo codice da qualsiasi punto all’interno di una normale finestra wpf (ad esempio un clic sul pulsante o l’inizializzazione). Si noti che non è ansible chiamare questo fino a quando la finestra non viene inizializzata, se l’HWND non è ancora stato creato (come nel costruttore), si verificherà un errore.

Rendi la finestra un appbar:

AppBarFunctions.SetAppBar( this, ABEdge.Right ); 

Ripristina la finestra in una finestra normale:

 AppBarFunctions.SetAppBar( this, ABEdge.None ); 

Ecco il codice completo del file: tieni presente che vorrai cambiare lo spazio dei nomi nella riga 7 in qualcosa di appropriato.

 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); } } } 

Molto felice di aver trovato questa domanda. Sopra la class è davvero utile, ma non copre tutte le basi dell’implementazione AppBar.

Per implementare completamente tutto il comportamento di un AppBar (far fronte alle app a tutto schermo ecc.), Vorrete leggere anche questo articolo di MSDN.

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

Scusa per il mio inglese … Ecco la soluzione di Philip Rieck con alcune correzioni. Funziona correttamente con la posizione della barra delle applicazioni e le modifiche alle dimensioni.

 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); } } } 

Lo stesso codice che puoi scrivere per i bordi sinistro e destro. Bel lavoro, Philip Rieck, grazie!

Ho modificato il codice da Philip Rieck (btw. Grazie mille) per lavorare in più impostazioni di visualizzazione. Ecco la mia soluzione.

 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); } } } 

C’è un eccellente articolo MSDN del 1996 che è aggiornato in modo divertente: Estendi la shell di Windows 95 con le barre degli strumenti di Application Desktop . Seguendo la sua guida produce una appbar basata su WPF che gestisce una serie di scenari che le altre risposte in questa pagina non offrono:

  • Consenti il ​​dock su qualsiasi lato dello schermo
  • Consenti dock ad un particolare monitor
  • Permetti il ​​ridimensionamento della appbar (se lo desideri)
  • Gestire le modifiche al layout dello schermo e monitorare le disconnessioni
  • Gestisci Win + Shift + Left e tenta di minimizzare o spostare la finestra
  • Gestire la cooperazione con altre appbar (OneNote et al.)
  • Gestire il ridimensionamento DPI per monitor

Ho sia un’app demo che l’implementazione di AppBarWindow su GitHub .

Esempio di utilizzo:

         

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); } } 

Modifica della posizione ancorata:

AppBar ancorata ai bordi

Ridimensionamento con il pollice:

Ridimensiona

Cooperazione con altri appbar:

Coordinazione

Clona da GitHub se vuoi usarlo. La libreria stessa ha solo tre file e può essere facilmente rilasciata in un progetto.

Spiacenti, l’ultimo codice che ho postato non funzionava quando la barra delle applicazioni viene ridimensionata. Il seguente codice di modifica sembra funzionare meglio:

  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); 

Come alternativa commerciale, vedi il componente ShellAppBar pronto per l’uso per WPF che supporta tutti i casi e i secnari come la barra delle applicazioni ancorata a sinistra, destra, alto, bordo inferiore, supporto per più monitor, drag-docking, autohide, ecc. Ecc. Potrebbe farti risparmiare tempo e denaro nel tentare di gestire tutti questi casi da solo.

DISCLAIMER : Lavoro per LogicNP Software, lo sviluppatore di ShellAppBar.

Ho trascorso alcune settimane ad esplorare questa sfida e finalmente ho creato un pacchetto NuGet molto solido che offre questa funzionalità in modo molto amichevole. Crea semplicemente una nuova app WPF e modifica la class della finestra principale da Window a DockWindow (in XAML) e il gioco è fatto!

Ottieni il pacchetto qui e guarda il repository Git per un’applicazione dimostrativa.