2008-09-16 17 views
56

Có hướng dẫn hoàn chỉnh nào về cách thực hiện việc lắp ghép AppBar (chẳng hạn như khóa vào cạnh màn hình) trong WPF không? Tôi hiểu rằng có những cuộc gọi InterOp cần phải được thực hiện, nhưng tôi đang tìm kiếm một bằng chứng về khái niệm dựa trên một hình thức WPF đơn giản, hoặc một phiên bản thành phần có thể được tiêu thụ.Làm thế nào để bạn thực hiện kết nối AppBar (với cạnh màn hình, như WinAmp) trong WPF?

nguồn lực liên quan:

Trả lời

83

Xin lưu ý: Câu hỏi này thu thập được một số lượng tốt của thông tin phản hồi, và một số người bên dưới đã thực hiện điểm lớn hoặc bản sửa lỗi. Vì vậy, trong khi tôi sẽ giữ mã ở đây (và có thể cập nhật nó), tôi cũng đã tạo một WpfAppBar project on github. Vui lòng gửi yêu cầu kéo.

Cùng dự án cũng xây dựng một WpfAppBar nuget package


tôi lấy mã từ liên kết đầu tiên được cung cấp trong câu hỏi (http://www.codeproject.com/KB/dotnet/AppBar.aspx) và sửa đổi nó để làm hai việc:

  1. Làm việc với WPF
  2. Hãy là "độc lập" - nếu bạn đặt tệp đơn này vào dự án của mình, bạn có thể gọi cho AppBarFunctions.SetAppBar (...) mà không cần sửa đổi thêm nữa vào cửa sổ.

Cách tiếp cận này không tạo lớp cơ sở.

Để sử dụng, chỉ cần gọi mã này từ bất kỳ đâu trong cửa sổ wpf bình thường (nói một nút bấm hoặc khởi tạo). Lưu ý rằng bạn không thể gọi điều này cho đến khi SAU KHI cửa sổ được khởi tạo, nếu HWND chưa được tạo (như trong hàm dựng), một lỗi sẽ xảy ra.

Làm cho cửa sổ một AppBar:

AppBarFunctions.SetAppBar(this, ABEdge.Right); 

Khôi phục các cửa sổ để một cửa sổ bình thường:

AppBarFunctions.SetAppBar(this, ABEdge.None); 

Dưới đây là toàn bộ mã để các tập tin - lưu ý bạn sẽ muốn thay đổi không gian tên trên dòng 7 đến một cái gì đó thích hợp.

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

Điều này có vẻ khá hoàn chỉnh, cảm ơn bạn đã dành thời gian. Tôi chưa thử nó, nhưng nó phải là một điểm khởi đầu tốt. Cảm ơn! – paulwhit

+1

Thực hiện tốt việc tìm ra lý do tại sao thứ chết tiệt này di chuyển cửa sổ nếu bạn sử dụng bất kỳ kiểu cửa sổ nào khác hơn là Không. Thanh danh. –

+1

Đây là một vấn đề nhỏ với logic. Nếu bạn cố gắng thay đổi docking (từ trên xuống bên trái ví dụ) mã của bạn bằng cách sử dụng chiều rộng và chiều cao hiện tại của cửa sổ: nó lấp đầy toàn bộ màn hình. Tôi cố định nó bằng cách lập chỉ mục các cửa sổ đã đăng ký với một IntPtr và gửi RegisterInfo đến ABSetPos() –

3

Rất vui khi tìm thấy câu hỏi này. Trên lớp là thực sự hữu ích, nhưng doesnt khá bao gồm tất cả các căn cứ của AppBar thực hiện.

Để thực hiện đầy đủ tất cả các hành vi của một AppBar (đối phó với các ứng dụng toàn màn hình vv), bạn sẽ muốn đọc bài viết MSDN này.

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

+0

Cảm ơn bạn đã tham khảo. Tôi đã nhận được đủ ý kiến ​​về mã này mà tôi chỉ cần đặt nó lên trên github. Nếu bạn muốn, cảm thấy tự do để ngã ba nó hoặc gửi yêu cầu kéo. –

+0

Làm cách nào để móc chức năng AppBarCallback? Tôi không thể tìm ra cách thích hợp để gọi nó. –

1

Xin lỗi, mã cuối cùng tôi gửi đã không làm việc khi Taskbar được thay đổi kích cỡ.Sự thay đổi mã sau đây dường như làm việc tốt hơn:

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

Là một thay thế thương mại, thấy sẵn sàng để sử dụng ShellAppBar thành phần cho WPF hỗ trợ tất cả các trường hợp và secnarios như thanh tác vụ cập cảng sang trái, phải , trên, cạnh dưới, hỗ trợ cho nhiều màn hình, kéo-docking, autohide, vv vv Nó có thể giúp bạn tiết kiệm thời gian và tiền bạc trên cố gắng để xử lý tất cả những trường hợp này cho mình.

DISCLAIMER: Tôi làm việc cho phần mềm LogicNP, nhà phát triển của ShellAppBar.

3

Xin lỗi vì tiếng Anh của tôi ... Đây là giải pháp của Philip Rieck với một số sửa chữa. Nó hoạt động đúng với vị trí và kích thước của thanh tác vụ.

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

Cùng mã bạn có thể viết cho cạnh Trái và Phải. Làm tốt lắm, Philip Rieck, cảm ơn!

+0

Phillip thực sự hoạt động. Bạn thậm chí không biên dịch. – nathanchere

+0

Nó biên dịch. –

3

Tôi đã sửa đổi mã từ Philip Rieck (btw. Cảm ơn rất nhiều) để làm việc trong nhiều cài đặt hiển thị. Đây là giải pháp của tôi.

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

Tôi đã dành một vài tuần khám phá thử thách này và cuối cùng đã tạo một gói NuGet rất vững chắc cung cấp chức năng này theo cách rất thân thiện. Đơn giản chỉ cần tạo một ứng dụng WPF mới sau đó thay đổi lớp cửa sổ chính từ Window sang DockWindow (trong XAML) và đó là nó!

Nhận gói here và xem repo Git cho một ứng dụng trình diễn.

+0

Trong khi liên kết này có thể trả lời câu hỏi, tốt hơn nên bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở thành không hợp lệ nếu trang được liên kết thay đổi. –

+1

@JamesKPolk: Các liên kết đến một công cụ hoặc thư viện nên bao gồm giải thích cụ thể về cách tài nguyên được liên kết có thể áp dụng cho vấn đề và lý tưởng [cũng kèm theo ghi chú sử dụng hoặc một số mã mẫu] (http://meta.stackoverflow.com/a/251605). Điều này dường như làm tất cả những điều đó. –

2

Có một bài viết MSDN xuất sắc từ năm 1996 được giải trí cập nhật: Extend the Windows 95 Shell with Application Desktop Toolbars. Sau hướng dẫn của nó tạo ra một WPF dựa AppBar mà xử lý một số tình huống mà các câu trả lời khác trên trang này không:

  • phép dock để bất kỳ bên của màn hình
  • phép dock để một màn hình đặc biệt
  • cho phép thay đổi kích thước của AppBar (nếu muốn)
  • thay đổi Xử lý bố cục màn hình và màn hình disconnections
  • Xử lý Win + phím Shift +Left và nỗ lực để giảm thiểu hoặc di chuyển cửa sổ
  • Xử lý hợp tác với appbars khác (OneNote et al.)
  • Xử lý mỗi màn DPI rộng

Tôi có cả một demo app and the implementation of AppBarWindow on GitHub.

Ví dụ sử dụng:

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

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

thay đổi cập cảng số các mục:

AppBar docked to edges

Thay đổi kích thước wit h ngón tay cái:

Resize

Hợp tác với appbars khác:

Coordination

Clone from GitHub nếu bạn muốn sử dụng nó. Thư viện chính nó chỉ là ba tập tin, và có thể dễ dàng bị bỏ trong một dự án.

+0

Tôi chạy dự án mẫu trên một thiết lập màn hình kép và nó khá là lỗi. Tôi chỉ có thể thay đổi kích thước khi được gắn vào trái hoặc phải và nó đã bị lỗi trong vòng 30 giây do giá trị độ cao âm. Tôi không có thời gian để gỡ lỗi hoặc tôi sẽ cung cấp cho bạn thêm thông tin. – Dan

+0

@Dan, tôi rất tiếc khi biết bạn có vấn đề như vậy. Mã này được triển khai rộng rãi và tôi không tin rằng đó là vấn đề với 'AppBarWindow' - nhưng với dự án mẫu. Vui lòng xem [cam kết 84a3720] (https://github.com/mgaffigan/WpfAppBar/commit/84a3720fd3e7ce4023a9a98e143763f15f3fff0a) để cải thiện ứng dụng demo giải thích các vấn đề bạn gặp phải. – Mitch