web-dev-qa-db-ja.com

WPFでAppBarドッキング(WinAmpなどのスクリーンエッジへ)を行う方法を教えてください。

WPFでのAppBarドッキング(画面のEdgeへのロックなど)に関する完全なガイダンスはありますか?必要なInterOp呼び出しがあることを理解していますが、単純なWPFフォームに基づく概念実証、または消費できるコンポーネント化されたバージョンのいずれかを探しています。

関連リソース:

59
paulwhit

ご注意ください:この質問はかなりの量のフィードバックを集めており、以下の一部の人々は素晴らしいポイントや修正を行っています。したがって、ここではコードを保持し(場合によっては更新します)、githubに WpfAppBarプロジェクトを作成しました。プルリクエストを送信してください。

同じプロジェクトが WpfAppBar nugetパッケージにもビルドされます


質問で提供された最初のリンクからコードを取得し( http://www.codeproject.com/KB/dotnet/AppBar.aspx )、2つのことを行うように変更しました:

  1. WPFを使用する
  2. 「スタンドアロン」-この単一ファイルをプロジェクトに配置すると、ウィンドウをさらに変更せずにAppBarFunctions.SetAppBar(...)を呼び出すことができます。

このアプローチでは、基本クラスは作成されません。

使用するには、通常のwpfウィンドウ内のどこからでもこのコードを呼び出すだけです(ボタンのクリックや初期化など)。ウィンドウが初期化されるまでこれを呼び出せないことに注意してください。HWNDがまだ作成されていない場合(コンストラクター内など)、エラーが発生します。

ウィンドウをappbarにします。

AppBarFunctions.SetAppBar( this, ABEdge.Right );

ウィンドウを通常のウィンドウに戻します。

AppBarFunctions.SetAppBar( this, ABEdge.None );

これがファイルへの完全なコードです-note7行目の名前空間を適切なものに変更する必要があります。

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);
        }
    }
}
87
Philip Rieck

私の英語で申し訳ありません...これはいくつかの修正を加えたPhilip Rieckの解決策です。タスクバーの位置とサイズの変更で正しく動作します。

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

左端と右端に記述できる同じコード。よくできました、フィリップ・リーク、ありがとう!

4
Dmitry Andreev

複数のディスプレイ設定で動作するように、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);
        }
    }
}
4
Miky Jadro

1996年の優れたMSDNの記事があり、面白い最新情報があります。 Windows 95シェルをアプリケーションデスクトップツールバーで拡張 。そのガイダンスに従うと、このページの他の回答では対応できない多数のシナリオを処理するWPFベースのappbarが生成されます。

  • 画面の任意の側へのドッキングを許可します
  • 特定のモニターへのドッキングを許可する
  • アプリバーのサイズ変更を許可する(必要な場合)
  • 画面レイアウトの変更を処理し、切断を監視する
  • 扱う Win + Shift + Left そして、ウィンドウを最小化または移動しようとします
  • 他のアプリバーとの連携を処理します(OneNoteなど)
  • モニターごとのDPIスケーリングを処理する

デモアプリとGitHubでのAppBarWindowの実装 の両方を持っています。

使用例:

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

        <Thumb Width="5" HorizontalAlignment="Right" Background="Gray" x:Name="rzThumb" Cursor="SizeWE" DragCompleted="rzThumb_DragCompleted" />
    </Grid>
</apb:AppBarWindow>

分離コード:

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();

        this.cbEdge.ItemsSource = new[]
        {
            AppBarDockMode.Left,
            AppBarDockMode.Right,
            AppBarDockMode.Top,
            AppBarDockMode.Bottom
        };
        this.cbMonitor.ItemsSource = MonitorInfo.GetAllMonitors();
    }

    private void btClose_Click(object sender, RoutedEventArgs e)
    {
        Close();
    }

    private void rzThumb_DragCompleted(object sender, DragCompletedEventArgs e)
    {
        this.DockedWidthOrHeight += (int)(e.HorizontalChange / VisualTreeHelper.GetDpi(this).PixelsPerDip);
    }
}

ドッキング位置の変更:

AppBar docked to edges

親指でサイズ変更:

Resize

他のアプリバーとの連携:

Coordination

クローン GitHubから 使用したい場合。ライブラリ自体は3つのファイルのみで、プロジェクトに簡単にドロップできます。

3
Mitch

この質問を見つけてとても幸せです。上記のクラスは本当に便利ですが、AppBar実装のすべての基本を網羅しているわけではありません。

AppBarのすべての動作(フルスクリーンアプリなどに対応)を完全に実装するには、このMSDNの記事も読む必要があります。

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

3
Shuft

私は数週間かけてこの課題を調査し、ついにこの機能を非常にフレンドリーな方法で提供する非常に強固なNuGetパッケージを作成しました。新しいWPFアプリを作成し、メインウィンドウのクラスをWindowからDockWindow(XAML内)に変更するだけです。

パッケージ here を取得し、デモアプリのGitリポジトリを参照してください。

1
Hugh

商用の代替手段として、WPFのすぐに使える ShellAppBar コンポーネントを参照してください。これは、左、右、上、下のエッジにドッキングされたタスクバー、複数のモニターのサポート、ドラッグなど、すべてのケースとセキュリティをサポートします。ドッキング、自動非表示など。これらすべてのケースを自分で処理するよりも、時間とお金を節約できます。

[〜#〜]免責事項[〜#〜]:ShellAppBarの開発者であるLogicNP Softwareで働いています。

1
logicnp

タスクバーのサイズを変更すると、最後に投稿したコードが機能しませんでした。次のコード変更はよりうまく機能するようです:

  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
Anonymous