2008-09-16 37 views

回答

83

请注意:这个问题收集的反馈量好,以下一些人已经取得了很大的点或修复。因此,虽然我将代码保存在此处(并且可能会更新它),但我也在创建了一个WpfAppBar project on github。随意发送拉请求。

这同一个项目还建立一个WpfAppBar nuget package


我把从问题(http://www.codeproject.com/KB/dotnet/AppBar.aspx)提供的第一个链接的代码和修改它做两件事情:

  1. 与工作WPF
  2. “独立” - 如果您将此单个文件放入您的项目中,则可以调用AppBarFunctions.SetAppBar(...),而无需对窗口进行任何进一步的修改。

此方法不会创建基类。

要使用,只需从正常的wpf窗口中的任何位置调用此代码(例如按下按钮或初始化)。请注意,在窗口初始化之后,如果尚未创建HWND(如在构造函数中),则不能调用此函数,否则会发生错误。

使该窗口的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); 
     } 
    } 
} 
+0

这看起来很完整,感谢您花时间。我还没有尝试过,但它应该是一个很好的起点。谢谢! – paulwhit 2008-09-18 09:49:08

+1

如果你使用任何其他窗口风格而不是None,那么弄清楚为什么该死的东西会移动窗口。荣誉。 – 2008-10-28 13:41:24

+1

这是一个逻辑问题。如果您尝试使用窗口的当前宽度和高度来更改对接(例如从顶部到左侧):填充整个屏幕。我通过用IntPtr索引已注册的窗口并将RegisterInfo发送到ABSetPos来修复它。() – 2008-10-28 15:39:23

1

对不起,最后一个代码我张贴当任务栏被调整没有工作。下面的代码变化似乎更好的工作:

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

作为一个商业选择,请参见WPF准备使用的ShellAppBar组件,它支持停靠到左的所有案件,secnarios如任务栏,右边,顶部,底部边缘,支持多个显示器,拖拽对接,自动隐藏等等。它可以为您自己处理所有这些情况节省时间和金钱。

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

3

对不起,我的英语...这里是菲利普里克的解决方案,一些更正。它正确地与任务栏的位置和大小变化一起工作。

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

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

3

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

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

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

获取软件包here并查看示例应用程序的Git回购。

2

有一个很好的MSDN文章从1996年,这是有趣的最新:Extend the Windows 95 Shell with Application Desktop Toolbars。继其指导产生一个WPF基于appbar它处理了一些场景,这个页面上的其他答案并不:

  • 允许停靠到屏幕的任何一侧
  • 允许停靠到一个特定的监视器
  • 允许appbar的调整(如果需要)
  • 手柄屏幕布局的变化和监视器断开
  • 手柄 + + 并尝试最小化或移动窗口
  • 处理与其他appbars合作(的OneNote等人)
  • 处理每个监视器DPI缩放

我同时具有demo app and the implementation of AppBarWindow on GitHub

实施例使用:

<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 docked to edges

调整大小机智^ h大拇指:

Resize

与其他appbars合作:

Coordination

克隆,如果你想使用它from GitHub。库本身只有三个文件,并且可以很容易地放入项目中。