有没有关于在 WPF 中进行 AppBar 停靠(例如锁定到屏幕边缘)的完整指南?我知道需要进行 InterOp 调用,但我正在寻找基于简单 WPF 表单的概念证明,或者可以使用的组件化版本。
相关资源:
最佳答案
请注意:这个问题收集了大量的反馈,下面的一些人提出了很好的观点或修正。因此,虽然我会将代码保留在这里(并可能对其进行更新),但我还创建了一个 WpfAppBar project on github 强>。随时发送请求请求。
同一个项目也构建为 WpfAppBar nuget package
我从问题中提供的第一个链接 (http://www.codeproject.com/KB/dotnet/AppBar.aspx) 中获取代码并修改它以执行两件事:
- 使用 WPF
- “独立”- 如果将这个单个文件放入项目中,则无需对窗口进行任何进一步修改即可调用 AppBarFunctions.SetAppBar(...)。
此方法不创建基类。
要使用,只需从普通 wpf 窗口中的任何位置调用此代码(比如单击按钮或初始化)。请注意,在窗口初始化之后才能调用它,如果尚未创建 HWND(如在构造函数中),则会发生错误。
使窗口成为应用栏:
AppBarFunctions.SetAppBar( this, ABEdge.Right );
将窗口恢复为普通窗口:
AppBarFunctions.SetAppBar( this, ABEdge.None );
这是文件的完整代码 - 注意您需要将第 7 行的命名空间更改为适当的名称。
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;
namespace AppBarApplication
{
public enum ABEdge : int
{
Left = 0,
Top,
Right,
Bottom,
None
}
internal static class AppBarFunctions
{
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
private enum ABMsg : int
{
ABM_NEW = 0,
ABM_REMOVE,
ABM_QUERYPOS,
ABM_SETPOS,
ABM_GETSTATE,
ABM_GETTASKBARPOS,
ABM_ACTIVATE,
ABM_GETAUTOHIDEBAR,
ABM_SETAUTOHIDEBAR,
ABM_WINDOWPOSCHANGED,
ABM_SETSTATE
}
private enum ABNotify : int
{
ABN_STATECHANGE = 0,
ABN_POSCHANGED,
ABN_FULLSCREENAPP,
ABN_WINDOWARRANGE
}
[DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)]
private static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern int RegisterWindowMessage(string msg);
private class RegisterInfo
{
public int CallbackId { get; set; }
public bool IsRegistered { get; set; }
public Window Window { get; set; }
public ABEdge Edge { get; set; }
public WindowStyle OriginalStyle { get; set; }
public Point OriginalPosition { get; set; }
public Size OriginalSize { get; set; }
public ResizeMode OriginalResizeMode { get; set; }
public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam,
IntPtr lParam, ref bool handled)
{
if (msg == CallbackId)
{
if (wParam.ToInt32() == (int)ABNotify.ABN_POSCHANGED)
{
ABSetPos(Edge, Window);
handled = true;
}
}
return IntPtr.Zero;
}
}
private static Dictionary<Window, RegisterInfo> s_RegisteredWindowInfo
= new Dictionary<Window, RegisterInfo>();
private static RegisterInfo GetRegisterInfo(Window appbarWindow)
{
RegisterInfo reg;
if( s_RegisteredWindowInfo.ContainsKey(appbarWindow))
{
reg = s_RegisteredWindowInfo[appbarWindow];
}
else
{
reg = new RegisterInfo()
{
CallbackId = 0,
Window = appbarWindow,
IsRegistered = false,
Edge = ABEdge.Top,
OriginalStyle = appbarWindow.WindowStyle,
OriginalPosition =new Point( appbarWindow.Left, appbarWindow.Top),
OriginalSize =
new Size( appbarWindow.ActualWidth, appbarWindow.ActualHeight),
OriginalResizeMode = appbarWindow.ResizeMode,
};
s_RegisteredWindowInfo.Add(appbarWindow, reg);
}
return reg;
}
private static void RestoreWindow(Window appbarWindow)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
appbarWindow.WindowStyle = info.OriginalStyle;
appbarWindow.ResizeMode = info.OriginalResizeMode;
appbarWindow.Topmost = false;
Rect rect = new Rect(info.OriginalPosition.X, info.OriginalPosition.Y,
info.OriginalSize.Width, info.OriginalSize.Height);
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
public static void SetAppBar(Window appbarWindow, ABEdge edge)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
info.Edge = edge;
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = new WindowInteropHelper(appbarWindow).Handle;
if( edge == ABEdge.None)
{
if( info.IsRegistered)
{
SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd);
info.IsRegistered = false;
}
RestoreWindow(appbarWindow);
return;
}
if (!info.IsRegistered)
{
info.IsRegistered = true;
info.CallbackId = RegisterWindowMessage("AppBarMessage");
abd.uCallbackMessage = info.CallbackId;
uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd);
HwndSource source = HwndSource.FromHwnd(abd.hWnd);
source.AddHook(new HwndSourceHook(info.WndProc));
}
appbarWindow.WindowStyle = WindowStyle.None;
appbarWindow.ResizeMode = ResizeMode.NoResize;
appbarWindow.Topmost = true;
ABSetPos(info.Edge, appbarWindow);
}
private delegate void ResizeDelegate(Window appbarWindow, Rect rect);
private static void DoResize(Window appbarWindow, Rect rect)
{
appbarWindow.Width = rect.Width;
appbarWindow.Height = rect.Height;
appbarWindow.Top = rect.Top;
appbarWindow.Left = rect.Left;
}
private static void ABSetPos(ABEdge edge, Window appbarWindow)
{
APPBARDATA barData = new APPBARDATA();
barData.cbSize = Marshal.SizeOf(barData);
barData.hWnd = new WindowInteropHelper(appbarWindow).Handle;
barData.uEdge = (int)edge;
if (barData.uEdge == (int)ABEdge.Left || barData.uEdge == (int)ABEdge.Right)
{
barData.rc.top = 0;
barData.rc.bottom = (int)SystemParameters.PrimaryScreenHeight;
if (barData.uEdge == (int)ABEdge.Left)
{
barData.rc.left = 0;
barData.rc.right = (int)Math.Round(appbarWindow.ActualWidth);
}
else
{
barData.rc.right = (int)SystemParameters.PrimaryScreenWidth;
barData.rc.left = barData.rc.right - (int)Math.Round(appbarWindow.ActualWidth);
}
}
else
{
barData.rc.left = 0;
barData.rc.right = (int)SystemParameters.PrimaryScreenWidth;
if (barData.uEdge == (int)ABEdge.Top)
{
barData.rc.top = 0;
barData.rc.bottom = (int)Math.Round(appbarWindow.ActualHeight);
}
else
{
barData.rc.bottom = (int)SystemParameters.PrimaryScreenHeight;
barData.rc.top = barData.rc.bottom - (int)Math.Round(appbarWindow.ActualHeight);
}
}
SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData);
SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData);
Rect rect = new Rect((double)barData.rc.left, (double)barData.rc.top,
(double)(barData.rc.right - barData.rc.left), (double)(barData.rc.bottom - barData.rc.top));
//This is done async, because WPF will send a resize after a new appbar is added.
//if we size right away, WPFs resize comes last and overrides us.
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
}
}
关于c# - 你如何在 WPF 中进行 AppBar 对接(到屏幕边缘,如 WinAmp)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75785/