C# 打开通知区域附近的WPF窗口,并支持多重监视器
我的wpf应用程序应该在通知区域附近打开一个窗口。当用户单击托盘图标时,会打开一个窗口。所以我需要窗口在同一屏幕上的通知区域。现在,我只需要获取任务栏位置: 然后我用它来定位我的窗口,如:C# 打开通知区域附近的WPF窗口,并支持多重监视器,c#,wpf,windows,tray,C#,Wpf,Windows,Tray,我的wpf应用程序应该在通知区域附近打开一个窗口。当用户单击托盘图标时,会打开一个窗口。所以我需要窗口在同一屏幕上的通知区域。现在,我只需要获取任务栏位置: 然后我用它来定位我的窗口,如: //xaml.cs var taskBarLocation = GetTaskBarLocation(); var taskBarPosition = GetTaskbarPosition(); this.Left = taskBarLocation == Location.Left
//xaml.cs
var taskBarLocation = GetTaskBarLocation();
var taskBarPosition = GetTaskbarPosition();
this.Left = taskBarLocation == Location.Left
? taskBarPosition.Width
: SystemParameters.WorkArea.Width - Width;
this.Top = taskBarLocation == Location.Top
? taskBarPosition.Height
: SystemParameters.WorkArea.Height - Height;
仅当通知区域位于主屏幕时,它才起作用。据我所知,我应该使用WinApi将我的窗口放在其他显示器上,但我甚至不知道如何找到带有通知区域的屏幕。任何帮助都将不胜感激。好的,因为
1) Wpf窗口位置不关心监视器(它在“控制面板\所有控制面板项目\显示\屏幕分辨率”坐标上工作,而不是在当前屏幕坐标上)
2) WinApi方法SHAppBarMessage(5,ref数据)也适用于全局坐标
3) 每个屏幕对象都具有表示屏幕在全局坐标处的位置的属性
解决方案非常简单:
public static class TaskBarLocationProvider
{
// P/Invoke goo:
private const int ABM_GETTASKBARPOS = 5;
[DllImport("shell32.dll")]
private static extern IntPtr SHAppBarMessage(int msg, ref AppBarData data);
/// <summary>
/// Where is task bar located (at top of the screen, at bottom (default), or at the one of sides)
/// </summary>
private enum Dock
{
Left = 0,
Top = 1,
Right = 2,
Bottom = 3
}
private struct Rect
{
public int Left, Top, Right, Bottom;
}
private struct AppBarData
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public Dock Dock;
public Rect rc;
public IntPtr lParam;
}
private static Rectangle GetTaskBarCoordinates(Rect rc)
{
return new Rectangle(rc.Left, rc.Top,
rc.Right - rc.Left, rc.Bottom - rc.Top);
}
private static AppBarData GetTaskBarLocation()
{
var data = new AppBarData();
data.cbSize = Marshal.SizeOf(data);
IntPtr retval = SHAppBarMessage(ABM_GETTASKBARPOS, ref data);
if (retval == IntPtr.Zero)
{
throw new Win32Exception("WinAPi Error: does'nt work api method SHAppBarMessage");
}
return data;
}
private static Screen FindScreenWithTaskBar(Rectangle taskBarCoordinates)
{
foreach (var screen in Screen.AllScreens)
{
if (screen.Bounds.Contains(taskBarCoordinates))
{
return screen;
}
}
return Screen.PrimaryScreen;
}
/// <summary>
/// Calculate wpf window position for place it near to taskbar area
/// </summary>
/// <param name="windowWidth">target window height</param>
/// <param name="windowHeight">target window width</param>
/// <param name="left">Result left coordinate <see cref="System.Windows.Window.Left"/></param>
/// <param name="top">Result top coordinate <see cref="System.Windows.Window.Top"/></param>
public static void CalculateWindowPositionByTaskbar(double windowWidth, double windowHeight, out double left, out double top)
{
var taskBarLocation = GetTaskBarLocation();
var taskBarRectangle = GetTaskBarCoordinates(taskBarLocation.rc);
var screen = FindScreenWithTaskBar(taskBarRectangle);
left = taskBarLocation.Dock == Dock.Left
? screen.Bounds.X + taskBarRectangle.Width
: screen.Bounds.X + screen.WorkingArea.Width - windowWidth;
top = taskBarLocation.Dock == Dock.Top
? screen.Bounds.Y + taskBarRectangle.Height
: screen.Bounds.Y + screen.WorkingArea.Height - windowHeight;
}
您是否尝试过浏览
System.Windows.Forms.Screen.AllScreens
属性?这是一个类型为Screen
的数组。您可以检查该屏幕是否为主屏幕。通知区没有连接到任务栏吗?谢谢你的回复!我不知道wpf窗口坐标指向显示坐标,我可以简单地通过将其坐标设置到主屏幕之外来移动窗口。我也不知道SHAppBarMessage返回显示坐标。
private void SetWindowPosition()
{
double left, right;
TaskBarLocationProvider.CalculateWindowPositionByTaskbar(this.Width, this.Height, out left, out right);
Left = left;
Top = right;
}