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
{
///
/// 获取当前应用中处于激活的一个窗口
///
///
public static Window GetActiveWindow() => Application.Current.Windows.OfType().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());
///
/// 让窗口激活作为前台最上层窗口
///
///
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;
}
}
}
}