首页 / 知识

关于c#:如何在WPF中将AppBar停靠到屏幕边缘,例如WinAmp?

2023-04-16 03:24:00

How do you do AppBar docking (to screen edge, like WinAmp) in WPF?

在WPF中进行AppBar停靠(例如锁定到屏幕边缘)是否有任何完整指南? 我知道需要进行InterOp调用,但是我正在寻找基于简单WPF表单的概念证明或可以使用的组件化版本。

相关资源:

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

请注意:这个问题收集了很多反馈,下面的一些人提出了要点或修正。因此,尽管我将代码保存在这里(并可能进行更新),但我还在github上创建了一个WpfAppBar项目。随时发送请求请求。

该项目还构建到WpfAppBar nuget包中

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

  • 使用WPF
  • 是"独立的"-如果将此单个文件放入项目中,则可以调用AppBarFunctions.SetAppBar(...),而无需对窗口进行任何进一步的修改。
  • 这种方法不会创建基类。

    使用时,只需在普通的wpf窗口中的任何位置调用此代码即可(例如单击按钮或初始化)。请注意,只有在初始化窗口之后才能调用此函数,如果尚未创建HWND(如在构造函数中),则会发生错误。

    将窗口设为应用栏:

    1
    AppBarFunctions.SetAppBar( this, ABEdge.Right );

    将窗口还原为普通窗口:

    1
    AppBarFunctions.SetAppBar( this, ABEdge.None );

    这是文件的完整代码-请注意,您需要将第7行的名称空间更改为适当的名称。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    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);
            }
        }
    }

    从1996年开始,有一篇出色的MSDN文章令人愉悦地更新了:使用应用程序桌面工具栏扩展Windows 95 Shell。按照其指导,将产生一个基于WPF的应用栏,该应用栏可以处理许多此页面上其他答案没有的方案:

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

    我既有演示应用程序,又有GitHub上AppBarWindow的实现。

    使用示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <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>

    代码背后:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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克隆。该库本身只有三个文件,可以很容易地放入一个项目中。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    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);
            }
        }
    }

    对不起,我的英语...这是菲利普·里克(Philip Rieck)的正确解决方案。它可以正确使用任务栏的位置和大小更改。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    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);
            }
        }
    }

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


    很高兴找到这个问题。上面的类确实有用,但是并没有涵盖AppBar实现的所有基础。

    为了完全实现AppBar的所有行为(应对全屏应用程序等),您也将要阅读此MSDN文章。

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


    我花了数周的时间研究这个挑战,最后创建了一个非常可靠的NuGet软件包,以非常友好的方式提供了此功能。只需创建一个新的WPF应用程序,然后将主窗口的类从Window更改为DockWindow(在XAML中)就可以了!

    在此处获取软件包,并查看Git仓库以获取演示应用程序。


    作为一种商业替代方案,请参阅WPF的即用型ShellAppBar组件,该组件支持所有情况和场景,例如停靠在左侧,右侧,顶部,底部边缘的任务栏,支持多个监视器,拖放,自动隐藏等。尝试自己处理所有这些案件可能会节省您的时间和金钱。

    免责声明:我为LogicNP Software(ShellAppBar的开发人员)工作。


    抱歉,调整任务栏大小时,我发布的最后一个代码不起作用。以下代码更改似乎更好地起作用:

    1
    2
    3
    4
    5
    6
    7
    8
      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);


    边缘屏幕锁定调用

    最新内容

    相关内容

    猜你喜欢