2008-09-16 11 views
56

¿Hay alguna guía completa para realizar el acoplamiento de AppBar (como el bloqueo en el borde de la pantalla) en WPF? Entiendo que hay que hacer llamadas de InterOp, pero estoy buscando una prueba de concepto basada en un formulario WPF simple o una versión compuesta que se pueda consumir.¿Cómo se hace el acoplamiento de AppBar (al borde de la pantalla, como WinAmp) en WPF?

recursos relacionados:

Respuesta

83

Por favor Nota: Esta pregunta se reunieron una buena cantidad de información, y algunas personas a continuación han hecho grandes puntos o correcciones. Por lo tanto, aunque voy a mantener el código aquí (y posiblemente actualizarlo), también he creado un WpfAppBar project on github. Siéntase libre de enviar solicitudes de extracción.

Ese mismo proyecto también se construye a un WpfAppBar nuget package


Tomé el código desde el primer enlace que aparece en la pregunta (http://www.codeproject.com/KB/dotnet/AppBar.aspx) y lo modificó para hacer dos cosas:

  1. Trabaja con WPF
  2. Sea "independiente": si coloca este único archivo en su proyecto, puede llamar a AppBarFunctions.SetAppBar (...) sin ninguna otra modificación en la ventana.

Este enfoque no crea una clase base.

Para usar, simplemente llame a este código desde cualquier lugar dentro de una ventana wpf normal (por ejemplo, haga clic en un botón o inicialice). Tenga en cuenta que no puede invocar esto hasta DESPUÉS de que se inicialice la ventana, si el HWND aún no se ha creado (como en el constructor), se producirá un error.

hacer que la ventana de un appbar:

AppBarFunctions.SetAppBar(this, ABEdge.Right); 

restaurar la ventana a una ventana normal:

AppBarFunctions.SetAppBar(this, ABEdge.None); 

Aquí está el código completo al expediente - nota que querrá cambiar la espacio de nombres en la línea 7 a algo apropiado.

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

Esto se ve bastante completo, gracias por tomarse el tiempo. Todavía no lo probé, pero debería ser un buen punto de partida. ¡Gracias! – paulwhit

+1

Bien hecho al descubrir por qué la maldita cosa mueve la ventana si usa cualquier otro estilo de ventana que None. Prestigio. –

+1

El es un problema muy leve con la lógica. Si intenta cambiar el acoplamiento (de arriba a la izquierda, por ejemplo) su código usando el ancho y el alto actuales de la ventana: llena toda la pantalla. Lo arreglé indexando las ventanas registradas con un IntPtr y enviando el RegisterInfo a ABSetPos() –

3

Muy feliz de haber encontrado esta pregunta. La clase superior es realmente útil, pero no cubre todas las bases de la implementación de AppBar.

Para implementar completamente todo el comportamiento de un AppBar (hacer frente a las aplicaciones de pantalla completa, etc.) también querrá leer este artículo de MSDN.

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

+0

Gracias por la referencia. He recibido suficientes comentarios sobre este código que acabo de poner en Github. Si lo desea, siéntase libre de copiarlo o enviar solicitudes de extracción. –

+0

¿Cómo conecto la función AppBarCallback? No puedo encontrar la manera correcta de llamarlo. –

1

Lo sentimos, el último código que he publicado no funcionaba cuando se cambia el tamaño de la barra de tareas.El siguiente cambio de código parece funcionar mejor:

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

Como una alternativa comercial, véase el componente listo para su uso ShellAppBar para WPF que soporta todos los casos y secnarios tales como barra de tareas atracados a la izquierda, derecha , borde superior e inferior, soporte para múltiples monitores, arrastre de arrastre, ocultación automática, etc. Puede ahorrarle tiempo y dinero en tratar de manejar todos estos casos usted mismo.

DESCARGO DE RESPONSABILIDAD: Trabajo para LogicNP Software, el desarrollador de ShellAppBar.

3

Lo siento por mi inglés ... Aquí está la solución de Philip Rieck con algunas correcciones. Funciona correctamente con la posición de la barra de tareas y los cambios de tamaño.

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

El mismo código que puede escribir para los bordes izquierdo y derecho. ¡Buen trabajo, Philip Rieck, gracias!

+0

Phillip realmente funciona. El tuyo ni siquiera compila. – nathanchere

+0

Compila. –

3

He modificado el código de Philip Rieck (muchas gracias) para trabajar en múltiples configuraciones de pantalla. Aquí está mi solución.

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

He pasado algunas semanas explorando este desafío y finalmente he creado un paquete NuGet muy sólido que ofrece esta funcionalidad de manera muy amigable. Simplemente cree una nueva aplicación WPF y luego cambie la clase de la ventana principal de Window a DockWindow (en el XAML) ¡y listo!

Obtenga el paquete here y vea el repositorio de Git para una aplicación de demostración.

+0

Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace de referencia. Las respuestas de solo enlace pueden dejar de ser válidas si la página vinculada cambia. –

+1

@JamesKPolk: Los enlaces a una herramienta o biblioteca deben incluir una explicación específica de cómo el recurso vinculado es aplicable al problema, e idealmente [también ir acompañado de notas de uso o algún código de muestra] (http://meta.stackoverflow.com/a/251605). Esto parece hacer todo eso. –

2

Hay un excelente artículo de MSDN de 1996 que está entretenido al día: Extend the Windows 95 Shell with Application Desktop Toolbars. Tras su orientación produce una appbar basado WPF que se ocupa de una serie de escenarios que las otras respuestas en esta página no:

  • Permitir muelle a cualquier lado de la pantalla
  • Permitir muelle para un monitor concreto
  • Permitir el cambio de tamaño de la appbar (si se desea)
  • cambios de diseño de la pantalla del monitor de la manija y desconexiones
  • Handle Win +Shift +Izquierda y trata de minimizar o mover la ventana
  • manija cooperación con otros appbars (OneNote et al.)
  • manija per-monitor de DPI de escala

I tiene tanto un demo app and the implementation of AppBarWindow on GitHub.

Ejemplo uso:

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

AppBar docked to edges

Cambiar el tamaño de ingenio:

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

cambiar de posición acoplado h pulgar:

Resize

Cooperación con otros appbars:

Coordination

Clon from GitHub si desea utilizarlo. La biblioteca en sí misma es solo tres archivos y puede soltarse fácilmente en un proyecto.

+0

Ejecuté el proyecto de ejemplo en una configuración de monitor doble y fue un poco problemático. Solo pude cambiar el tamaño cuando estaba acoplado a la izquierda o a la derecha, y se colgó en 30 segundos debido a un valor de altura negativo. No tengo tiempo para depurarlo o te daré más información. – Dan

+0

@Dan, lamento que haya tenido tales problemas. Este código está ampliamente implementado y no creo que esos sean problemas con 'AppBarWindow', sino con el proyecto de ejemplo. Consulte [commit 84a3720] (https://github.com/mgaffigan/WpfAppBar/commit/84a3720fd3e7ce4023a9a98e143763f15f3fff0a) para obtener mejoras en la aplicación de demostración que da cuenta de los problemas que vio. – Mitch

Cuestiones relacionadas