你如何做AppBar的对接(屏幕边缘,如WinAmp)在WPF?

是否有任何WPF中的AppBar停靠(如locking屏幕边缘)的完整指导? 我知道有InterOp调用需要做,但我正在寻找基于简单的WPFforms的概念certificate,或者可以消费的组件化版本。

相关资源:

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

请注意:这个问题收集了大量的反馈意见,下面的一些人已经做出了很好的观点或修复。 因此,虽然我将这里的代码(可能更新它),我也创build了一个WpfAppBar项目在github上 。 随意发送拉请求。

同样的项目也构build到WpfAppBar nuget包


我从问题中提供的第一个链接( http://www.codeproject.com/KB/dotnet/AppBar.aspx )获取了代码,并将其修改为做两件事:

  1. 使用WPF
  2. 是“独立的” – 如果你把这个单独的文件放在你的项目中,你可以调用AppBarFunctions.SetAppBar(…)而不需要对窗口做进一步的修改。

这种方法不会创build一个基类。

要使用,只需从一个正常的WPF窗口中的任何地方调用此代码(例如button单击或初始化)。 请注意,在窗口初始化之后,如果HWND尚未创build(如构造函数中),则不能调用此函数,否则将发生错误。

使窗口成为appbar:

AppBarFunctions.SetAppBar( this, ABEdge.Right ); 

将窗口恢复到正常窗口:

 AppBarFunctions.SetAppBar( this, ABEdge.None ); 

以下是该文件的完整代码 – 请注意,您需要将第7行的命名空间更改为适当的。

 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<Window, RegisterInfo> s_RegisteredWindowInfo = new Dictionary<Window, RegisterInfo>(); 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); } } } 

很高兴发现这个问题。 上面的类是非常有用的,但并不完全覆盖AppBar实现的所有基础。

为了全面实现AppBar的所有行为(应付全屏应用等),你也要阅读这篇MSDN文章。

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

对不起,我的英语…这里是菲利普里克的解决scheme,一些更正。 它正确地与任务栏位置和大小更改一起使用。

 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<Window, RegisterInfo> s_RegisteredWindowInfo = new Dictionary<Window, RegisterInfo>(); 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); } } } 

您可以为左侧和右侧边缘编写相同的代码。 菲利普·里克,很好,谢谢!

我修改了Philip Rieck的代码(顺便说一句,非常感谢)在多个显示设置中工作。 这是我的解决scheme。

 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<Window, RegisterInfo> s_RegisteredWindowInfo = new Dictionary<Window, RegisterInfo>(); 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); } } } 

有一个很好的MSDN文章从1996年是有趣的最新: 扩展与应用程序桌面工具栏的Windows 95shell 。 遵循其指导将生成一个基于WPF的应用程序,它可以处理一些本页面上其他答案的情况:

  • 允许停靠在屏幕的任何一侧
  • 允许停靠到特定的监视器
  • 允许resize的appbar(如果需要的话)
  • 处理屏幕布局更改并监视断开连接
  • 处理Win + Shift + Left并尝试最小化或移动窗口
  • 处理与其他应用程序的合作(OneNote等)
  • 处理每个监视器的DPI缩放

我有一个演示应用程序和GitHub上的AppBarWindow的实现 。

使用示例:

 <apb:AppBarWindow x:Class="WpfAppBarDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:apb="clr-namespace:WpfAppBar;assembly=WpfAppBar" DataContext="{Binding RelativeSource={RelativeSource Self}}" Title="MainWindow" DockedWidthOrHeight="200" MinHeight="100" MinWidth="100"> <Grid> <Button x:Name="btClose" Content="Close" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="23" Margin="10,10,0,0" Click="btClose_Click"/> <ComboBox x:Name="cbMonitor" SelectedItem="{Binding Path=Monitor, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Margin="10,38,0,0"/> <ComboBox x:Name="cbEdge" SelectedItem="{Binding Path=DockMode, Mode=TwoWay}" HorizontalAlignment="Left" Margin="10,65,0,0" VerticalAlignment="Top" Width="120"/> <Thumb Width="5" HorizontalAlignment="Right" Background="Gray" x:Name="rzThumb" Cursor="SizeWE" DragCompleted="rzThumb_DragCompleted" /> </Grid> </apb:AppBarWindow> 

代码隐藏:

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

改变停靠位置:

AppBar停靠到边缘

用拇指resize:

调整

与其他appbars合作:

协调

如果你想使用它, 从GitHub克隆。 该库本身只有三个文件,并可以很容易地在一个项目中删除。

对不起,当任务栏被resize时,我发布的最后一个代码不起作用。 下面的代码更改似乎更好:

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

作为一个商业的替代scheme,请参阅WPF 即时可用的ShellAppBar组件,它支持所有的案例和任务栏,例如任务栏停靠在左侧,右侧,顶部,底部边缘,支持多个显示器,拖放,自动隐藏等等。它可以节省您的时间和金钱,试图自己处理所有这些情况。

免责声明 :我为ShellAppBar的开发者LogicNP Software工作。

我已经花了几周的时间来探索这个挑战,并最终创build了一个非常可靠的NuGet包,以非常友好的方式提供这个function。 简单地创build一个新的WPF应用程序,然后将主窗口的类从Window更改为DockWindow(在XAML中),就是这样!

在这里获取包,并看到一个示范应用程序的Git回购。

Interesting Posts