Files
HandyControl/src/Shared/HandyControl_Shared/Tools/Helper/WindowHelper.cs
T

194 lines
7.7 KiB
C#
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}
}
}