194 lines
7.7 KiB
C#
194 lines
7.7 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.ComponentModel;
|
||
using System.Linq;
|
||
using System.Runtime.InteropServices;
|
||
using System.Security;
|
||
using System.Windows;
|
||
using System.Windows.Interop;
|
||
using HandyControl.Tools.Extension;
|
||
using HandyControl.Tools.Interop;
|
||
|
||
namespace HandyControl.Tools
|
||
{
|
||
public static class WindowHelper
|
||
{
|
||
/// <summary>
|
||
/// 获取当前应用中处于激活的一个窗口
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public static Window GetActiveWindow() => Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
|
||
|
||
private static readonly BitArray _cacheValid = new BitArray((int) InteropValues.CacheSlot.NumSlots);
|
||
|
||
private static bool _setDpiX = true;
|
||
|
||
private static bool _dpiInitialized;
|
||
|
||
private static readonly object _dpiLock = new object();
|
||
|
||
private static int _dpi;
|
||
|
||
internal static int Dpi
|
||
{
|
||
[SecurityCritical, SecuritySafeCritical]
|
||
get
|
||
{
|
||
if (!_dpiInitialized)
|
||
{
|
||
lock (_dpiLock)
|
||
{
|
||
if (!_dpiInitialized)
|
||
{
|
||
var desktopWnd = new HandleRef(null, IntPtr.Zero);
|
||
|
||
// Win32Exception will get the Win32 error code so we don't have to
|
||
var dc = InteropMethods.GetDC(desktopWnd);
|
||
|
||
// Detecting error case from unmanaged call, required by PREsharp to throw a Win32Exception
|
||
if (dc == IntPtr.Zero)
|
||
{
|
||
throw new Win32Exception();
|
||
}
|
||
|
||
try
|
||
{
|
||
_dpi = InteropMethods.GetDeviceCaps(new HandleRef(null, dc), InteropValues.LOGPIXELSY);
|
||
_dpiInitialized = true;
|
||
}
|
||
finally
|
||
{
|
||
InteropMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return _dpi;
|
||
}
|
||
}
|
||
|
||
private static int _dpiX;
|
||
|
||
internal static int DpiX
|
||
{
|
||
[SecurityCritical, SecuritySafeCritical]
|
||
get
|
||
{
|
||
if (_setDpiX)
|
||
{
|
||
lock (_cacheValid)
|
||
{
|
||
if (_setDpiX)
|
||
{
|
||
_setDpiX = false;
|
||
var desktopWnd = new HandleRef(null, IntPtr.Zero);
|
||
var dc = InteropMethods.GetDC(desktopWnd);
|
||
if (dc == IntPtr.Zero)
|
||
{
|
||
throw new Win32Exception();
|
||
}
|
||
try
|
||
{
|
||
_dpiX = InteropMethods.GetDeviceCaps(new HandleRef(null, dc), InteropValues.LOGPIXELSX);
|
||
_cacheValid[(int) InteropValues.CacheSlot.DpiX] = true;
|
||
}
|
||
finally
|
||
{
|
||
InteropMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return _dpiX;
|
||
}
|
||
}
|
||
|
||
private static Thickness _windowResizeBorderThickness;
|
||
|
||
internal static Thickness WindowResizeBorderThickness
|
||
{
|
||
[SecurityCritical]
|
||
get
|
||
{
|
||
lock (_cacheValid)
|
||
{
|
||
while (!_cacheValid[(int) InteropValues.CacheSlot.WindowResizeBorderThickness])
|
||
{
|
||
_cacheValid[(int) InteropValues.CacheSlot.WindowResizeBorderThickness] = true;
|
||
|
||
var frameSize = new Size(InteropMethods.GetSystemMetrics(InteropValues.SM.CXSIZEFRAME), InteropMethods.GetSystemMetrics(InteropValues.SM.CYSIZEFRAME));
|
||
var frameSizeInDips = DpiHelper.DeviceSizeToLogical(frameSize, DpiX / 96.0, Dpi / 96.0);
|
||
|
||
_windowResizeBorderThickness = new Thickness(frameSizeInDips.Width, frameSizeInDips.Height, frameSizeInDips.Width, frameSizeInDips.Height);
|
||
}
|
||
}
|
||
|
||
return _windowResizeBorderThickness;
|
||
}
|
||
}
|
||
|
||
public static Thickness WindowMaximizedPadding
|
||
{
|
||
get
|
||
{
|
||
InteropValues.APPBARDATA appBarData = default;
|
||
var autoHide = InteropMethods.SHAppBarMessage(4, ref appBarData) != 0;
|
||
#if NET40
|
||
return WindowResizeBorderThickness.Add(new Thickness(autoHide ? -8 : 0));
|
||
#elif Core
|
||
var hdc = InteropMethods.GetDC(IntPtr.Zero);
|
||
var scale = InteropMethods.GetDeviceCaps(hdc, InteropValues.DESKTOPVERTRES) / (float)InteropMethods.GetDeviceCaps(hdc, InteropValues.VERTRES);
|
||
InteropMethods.ReleaseDC(IntPtr.Zero, hdc);
|
||
return WindowResizeBorderThickness.Add(new Thickness((autoHide ? - 4 : 4) * scale));
|
||
#else
|
||
return WindowResizeBorderThickness.Add(new Thickness(autoHide ? -4 : 4));
|
||
#endif
|
||
}
|
||
}
|
||
|
||
public static IntPtr CreateHandle() => new WindowInteropHelper(new Window()).EnsureHandle();
|
||
|
||
public static IntPtr GetHandle(this Window window) => new WindowInteropHelper(window).EnsureHandle();
|
||
|
||
public static HwndSource GetHwndSource(this Window window) => HwndSource.FromHwnd(window.GetHandle());
|
||
|
||
/// <summary>
|
||
/// 让窗口激活作为前台最上层窗口
|
||
/// </summary>
|
||
/// <param name="window"></param>
|
||
public static void SetWindowToForeground(Window window)
|
||
{
|
||
// [WPF 让窗口激活作为前台最上层窗口的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/12749671.html)
|
||
var interopHelper = new WindowInteropHelper(window);
|
||
var thisWindowThreadId = InteropMethods.GetWindowThreadProcessId(interopHelper.Handle, out _);
|
||
var currentForegroundWindow = InteropMethods.GetForegroundWindow();
|
||
var currentForegroundWindowThreadId = InteropMethods.GetWindowThreadProcessId(currentForegroundWindow, out _);
|
||
|
||
// [c# - Bring a window to the front in WPF - Stack Overflow](https://stackoverflow.com/questions/257587/bring-a-window-to-the-front-in-wpf )
|
||
// [SetForegroundWindow的正确用法 - 子坞 - 博客园](https://www.cnblogs.com/ziwuge/archive/2012/01/06/2315342.html )
|
||
/*
|
||
1.得到窗口句柄FindWindow
|
||
2.切换键盘输入焦点AttachThreadInput
|
||
3.显示窗口ShowWindow(有些窗口被最小化/隐藏了)
|
||
4.更改窗口的Z Order,SetWindowPos使之最上,为了不影响后续窗口的Z Order,改完之后,再还原
|
||
5.最后SetForegroundWindow
|
||
*/
|
||
|
||
InteropMethods.AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, true);
|
||
|
||
window.Show();
|
||
window.Activate();
|
||
// 去掉和其他线程的输入链接
|
||
InteropMethods.AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, false);
|
||
|
||
// 用于踢掉其他的在上层的窗口
|
||
if (window.Topmost != true)
|
||
{
|
||
window.Topmost = true;
|
||
window.Topmost = false;
|
||
}
|
||
}
|
||
}
|
||
}
|