WPF에서 AppBar 도킹 (WinAmp와 같은 화면 가장자리)을 어떻게 수행합니까?
WPF에서 AppBar 도킹 (예 : 화면 가장자리 잠금)에 대한 완전한 지침이 있습니까? 필요한 InterOp 호출이 있다는 것을 이해하지만 간단한 WPF 양식을 기반으로하는 개념 증명 또는 사용할 수있는 구성 요소 화 된 버전을 찾고 있습니다.
관련 자료 :
- http://www.codeproject.com/KB/dotnet/AppBar.aspx
- http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/05c73c9c-e85d-4ecd-b9b6-4c714a65e72b/
참고 : 이 질문은 많은 피드백을 수집했으며 아래의 일부 사람들이 훌륭한 포인트 나 수정을했습니다. 따라서 여기에 코드를 보관하고 업데이트 하는 동안 github 에서 WpfAppBar 프로젝트 도 만들었습니다 . 풀 리퀘스트를 보내주세요.
동일한 프로젝트도 WpfAppBar 너겟 패키지로 빌드됩니다.
질문에 제공된 첫 번째 링크 ( http://www.codeproject.com/KB/dotnet/AppBar.aspx ) 에서 코드를 가져와 두 가지 작업을 수행하도록 수정했습니다.
- WPF로 작업
- "독립형"이어야합니다.이 단일 파일을 프로젝트에 넣으면 창을 더 이상 수정하지 않고도 AppBarFunctions.SetAppBar (...)를 호출 할 수 있습니다.
이 접근 방식은 기본 클래스를 생성하지 않습니다.
사용하려면 일반 wpf 창 (예 : 버튼 클릭 또는 초기화) 내에서이 코드를 호출하기 만하면됩니다. 윈도우가 초기화 될 때까지 이것을 호출 할 수 없습니다. HWND가 아직 생성되지 않은 경우 (생성자에서와 같이) 오류가 발생합니다.
창을 앱 바로 만듭니다.
AppBarFunctions.SetAppBar( this, ABEdge.Right );
창을 일반 창으로 복원 :
AppBarFunctions.SetAppBar( this, ABEdge.None );
여기에 파일의 전체 코드는 - 음 당신이 원하는거야 뭔가 apropriate에 라인 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);
}
}
}
영어로 죄송합니다 ... 여기에 몇 가지 수정 사항이있는 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);
}
}
}
왼쪽 및 오른쪽 가장자리에 대해 작성할 수있는 동일한 코드입니다. 잘 했어, Philip Rieck, 감사합니다!
여러 디스플레이 설정에서 작동하도록 Philip Rieck (btw. 감사합니다)의 코드를 수정했습니다. 여기 내 해결책이 있습니다.
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);
}
}
}
이 질문을 발견하게되어 매우 기쁩니다. 위의 클래스는 정말 유용하지만 AppBar 구현의 모든 기반을 다루지는 않습니다.
AppBar의 모든 동작을 완전히 구현하려면 (전체 화면 앱 등을 처리)이 MSDN 문서도 읽어야합니다.
http://msdn.microsoft.com/en-us/library/bb776821.aspx
흥미롭게도 최신 인 1996 년의 훌륭한 MSDN 기사가 있습니다 . 응용 프로그램 데스크탑 도구 모음으로 Windows 95 셸 확장 . 지침에 따라이 페이지의 다른 답변이 수행하지 않는 여러 시나리오를 처리하는 WPF 기반 앱 바가 생성됩니다.
- 화면의 어느쪽에 든 도킹 허용
- 특정 모니터에 도킹 허용
- 앱바 크기 조정 허용 (원하는 경우)
- 화면 레이아웃 변경 처리 및 연결 해제 모니터링
- Win+ Shift+를 처리 Left하고 창을 최소화하거나 이동하려고합니다.
- 다른 앱 바와의 공동 작업 처리 (OneNote 등)
- 모니터 별 DPI 스케일링 처리
데모 앱과 AppBarWindow
GitHub 구현 이 모두 있습니다 .
사용 예 :
<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);
}
}
고정 위치 변경 :
엄지 손가락으로 크기 조정 :
Cooperation with other appbars:
Clone from GitHub if you want to use it. The library itself is only three files, and can easily be dropped in a project.
Sorry, the last code I posted didn't work when the Taskbar is resized. The following code change seems to work better:
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);
As a commercial alternative, see the ready-to-use ShellAppBar component for WPF which supports all cases and secnarios such as taskbar docked to left,right,top,bottom edge, support for multiple monitors, drag-docking, autohide , etc etc. It may save you time and money over trying to handle all these cases yourself.
DISCLAIMER: I work for LogicNP Software, the developer of ShellAppBar.
몇 주 동안이 도전 과제를 탐구하고 마침내이 기능을 매우 친숙한 방식으로 제공하는 매우 견고한 NuGet 패키지를 만들었습니다. 새 WPF 앱을 만든 다음 주 창의 클래스를 Window에서 DockWindow (XAML에서)로 변경하기 만하면됩니다.
여기에서 패키지 를 다운로드하고 데모 앱에 대한 Git 리포지토리를 확인 하세요 .
'development' 카테고리의 다른 글
지금은 iPhone 앱을 설치할 수 없습니다. (0) | 2020.12.08 |
---|---|
pandas에서 여러 열 삭제 (0) | 2020.12.08 |
C ++에서 벡터 요소에 대한 포인터 반환 (0) | 2020.12.08 |
PHP는 다운로드 할 파일을 생성하고 리디렉션합니다. (0) | 2020.12.08 |
HTML에 대한 컬러 Git 차이 (0) | 2020.12.08 |