From becfe3ed286385d7f3282df86ff95112fef0cce3 Mon Sep 17 00:00:00 2001 From: Tienyu Yang <53754724+lingrottin@users.noreply.github.com> Date: Sun, 1 Oct 2023 17:09:03 -0700 Subject: [PATCH] beautify & fix Windows11 acrylic --- MapTP.App/AboutWindow.xaml.cs | 1 - MapTP.App/App.config | 12 +- MapTP.App/BlurManager.cs | 261 +++++++++++++++++++++++++++ MapTP.App/CalibrateWindow.xaml.cs | 2 - MapTP.App/MainWindow.xaml | 6 +- MapTP.App/MainWindow.xaml.cs | 47 +++-- MapTP.App/MapTP.App.csproj | 136 ++++++++++++-- MapTP.App/Properties/AssemblyInfo.cs | 55 ++++++ MapTP.App/ScreenManager.cs | 9 +- MapTP.sln | 11 +- 10 files changed, 498 insertions(+), 42 deletions(-) create mode 100644 MapTP.App/BlurManager.cs create mode 100644 MapTP.App/Properties/AssemblyInfo.cs diff --git a/MapTP.App/AboutWindow.xaml.cs b/MapTP.App/AboutWindow.xaml.cs index d1533fd..4cf4aa8 100644 --- a/MapTP.App/AboutWindow.xaml.cs +++ b/MapTP.App/AboutWindow.xaml.cs @@ -3,7 +3,6 @@ using System.Windows; using System.Windows.Documents; using System.Windows.Media; -using Walterlv.Windows.Effects; namespace MapTP.App { diff --git a/MapTP.App/App.config b/MapTP.App/App.config index 4bfa005..bcb2ae2 100644 --- a/MapTP.App/App.config +++ b/MapTP.App/App.config @@ -1,6 +1,14 @@ - + - + + + + + + + + + diff --git a/MapTP.App/BlurManager.cs b/MapTP.App/BlurManager.cs new file mode 100644 index 0000000..56e6588 --- /dev/null +++ b/MapTP.App/BlurManager.cs @@ -0,0 +1,261 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Interop; +using System.Windows.Media; + +namespace MapTP.App +{ + internal class BlurManager + { + // Completely from Walterlv + + private readonly Window _window; + private bool _isEnabled; + private int _blurColor; + + /// + /// 创建 的一个新实例。 + /// + /// 要创建模糊特效的窗口实例。 + public BlurManager(Window window) => _window = window ?? throw new ArgumentNullException(nameof(window)); + + /// + /// 获取或设置此窗口模糊特效是否生效的一个状态。 + /// 默认为 false,即不生效。 + /// + [DefaultValue(false)] + public bool IsEnabled + { + get => _isEnabled; + set + { + _isEnabled = value; + OnIsEnabledChanged(value); + } + } + + /// + /// 获取或设置此窗口模糊特效叠加的颜色。 + /// + public Color Color + { + get => Color.FromArgb( + // 取出红色分量。 + (byte)((_blurColor & 0x000000ff) >> 0), + // 取出绿色分量。 + (byte)((_blurColor & 0x0000ff00) >> 8), + // 取出蓝色分量。 + (byte)((_blurColor & 0x00ff0000) >> 16), + // 取出透明分量。 + (byte)((_blurColor & 0xff000000) >> 24)); + set => _blurColor = + // 组装红色分量。 + value.R << 0 | + // 组装绿色分量。 + value.G << 8 | + // 组装蓝色分量。 + value.B << 16 | + // 组装透明分量。 + value.A << 24; + } + + private void OnIsEnabledChanged(bool isEnabled) + { + Window window = _window; + var handle = new WindowInteropHelper(window).EnsureHandle(); + Composite(handle, isEnabled); + } + + private void Composite(IntPtr handle, bool isEnabled) + { + // 操作系统版本判定。 + var osVersion = Environment.OSVersion.Version; + var windows10_1809 = new Version(10, 0, 17763); + var windows10 = new Version(10, 0); + var windows11_buildver = 22000; + + // 创建 AccentPolicy 对象。 + var accent = new AccentPolicy(); + + // 设置特效。 + if (!isEnabled) + { + accent.AccentState = AccentState.ACCENT_DISABLED; + } + else if(Environment.OSVersion.Version.Build >= windows11_buildver) + { + accent.AccentState = AccentState.ACCENT_INVALID_STATE; // disable Acrylic and do nothing + } + else if (osVersion > windows10_1809) + { + // 如果系统在 Windows 10 (1809) 以上,则启用亚克力效果,并组合已设置的叠加颜色和透明度。 + // 请参见《在 WPF 程序中应用 Windows 10 真•亚克力效果》 + // https://blog.walterlv.com/post/using-acrylic-in-wpf-application.html + accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLICBLURBEHIND; + accent.GradientColor = _blurColor; + } + else if (osVersion > windows10) + { + // 如果系统在 Windows 10 以上,则启用 Windows 10 早期的模糊特效。 + // 请参见《在 Windows 10 上为 WPF 窗口添加模糊特效》 + // https://blog.walterlv.com/post/win10/2017/10/02/wpf-transparent-blur-in-windows-10.html + accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND; + } + else + { + // 暂时不处理其他操作系统: + // - Windows 8/8.1 不支持任何模糊特效 + // - Windows Vista/7 支持 Aero 毛玻璃效果 + return; + } + + // 将托管结构转换为非托管对象。 + var accentPolicySize = Marshal.SizeOf(accent); + var accentPtr = Marshal.AllocHGlobal(accentPolicySize); + Marshal.StructureToPtr(accent, accentPtr, false); + + // 设置窗口组合特性。 + try + { + // 设置模糊特效。 + var data = new WindowCompositionAttributeData + { + Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY, + SizeOfData = accentPolicySize, + Data = accentPtr, + }; + SetWindowCompositionAttribute(handle, ref data); + } + finally + { + // 释放非托管对象。 + Marshal.FreeHGlobal(accentPtr); + } + } + + [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] + private static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data); + + private enum AccentState + { + /// + /// 完全禁用 DWM 的叠加特效。 + /// + ACCENT_DISABLED = 0, + + /// + /// + /// + ACCENT_ENABLE_GRADIENT = 1, + ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, + ACCENT_ENABLE_BLURBEHIND = 3, + ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, + ACCENT_INVALID_STATE = 5, + } + + [StructLayout(LayoutKind.Sequential)] + private struct AccentPolicy + { + public AccentState AccentState; + public int AccentFlags; + public int GradientColor; + public int AnimationId; + } + + [StructLayout(LayoutKind.Sequential)] + private struct WindowCompositionAttributeData + { + public WindowCompositionAttribute Attribute; + public IntPtr Data; + public int SizeOfData; + } + + private enum WindowCompositionAttribute + { + // 省略其他未使用的字段 + WCA_ACCENT_POLICY = 19, + // 省略其他未使用的字段 + } + + /// + /// 当前操作系统支持的透明模糊特效级别。 + /// + public enum BlurSupportedLevel + { + /// + /// + /// + NotSupported, + Aero, + Blur, + Acrylic, + } + } + + /// + /// enables Mica for Windows 11 users + /// + internal class Mica + { + //this is from Difegue/Mica-WPF-Sample + #region Win32 + [DllImport("dwmapi.dll")] + public static extern int DwmSetWindowAttribute(IntPtr hwnd, DwmWindowAttribute dwAttribute, ref int pvAttribute, int cbAttribute); + + [Flags] + public enum DwmWindowAttribute : uint + { + DWMWA_USE_IMMERSIVE_DARK_MODE = 20, + DWMWA_MICA_EFFECT = 1029, + DWMA_SYSTEMBACKDROP_TYPE=38, + } + #endregion + + // Enable Mica on the given HWND. + public static void EnableMica(HwndSource source, bool darkThemeEnabled) + { + int trueValue = 0x01; + int falseValue = 0x00; + int systemBackdropType_Main = 3; + + // Set dark mode before applying the material, otherwise you'll get an ugly flash when displaying the window. + if (darkThemeEnabled) + DwmSetWindowAttribute(source.Handle, DwmWindowAttribute.DWMWA_USE_IMMERSIVE_DARK_MODE, ref trueValue, Marshal.SizeOf(typeof(int))); + else + DwmSetWindowAttribute(source.Handle, DwmWindowAttribute.DWMWA_USE_IMMERSIVE_DARK_MODE, ref falseValue, Marshal.SizeOf(typeof(int))); + + DwmSetWindowAttribute(source.Handle, DwmWindowAttribute.DWMWA_MICA_EFFECT, ref trueValue, Marshal.SizeOf(typeof(int))); + DwmSetWindowAttribute(source.Handle, DwmWindowAttribute.DWMA_SYSTEMBACKDROP_TYPE, ref systemBackdropType_Main, Marshal.SizeOf(typeof(int))); + } + + private HwndSource hwndSource; + + private bool _enable; + public bool enable + { + get => _enable; + set + { + _enable = value; + if (enable) + { + EnableMica(hwndSource, false); + } + } + } + + public Mica(HwndSource _hwndSource) + { + this.hwndSource = _hwndSource; + } + } +} + diff --git a/MapTP.App/CalibrateWindow.xaml.cs b/MapTP.App/CalibrateWindow.xaml.cs index e01877c..057132f 100644 --- a/MapTP.App/CalibrateWindow.xaml.cs +++ b/MapTP.App/CalibrateWindow.xaml.cs @@ -2,8 +2,6 @@ using System; using System.Windows; using System.Windows.Interop; -using System.Windows.Media; -using Walterlv.Windows.Effects; namespace MapTP.App { diff --git a/MapTP.App/MainWindow.xaml b/MapTP.App/MainWindow.xaml index 0081d84..9a32e0a 100644 --- a/MapTP.App/MainWindow.xaml +++ b/MapTP.App/MainWindow.xaml @@ -7,9 +7,11 @@ xmlns:local="clr-namespace:MapTP.App" mc:Ignorable="d" Title="MapTP" Height="500" Width="800" - Background="{x:Null}"> + Foreground="{DynamicResource SystemControlPageTextBaseHighBrush}" + WindowStyle="None" + Background="#33aaaaff"> - + diff --git a/MapTP.App/MainWindow.xaml.cs b/MapTP.App/MainWindow.xaml.cs index 1bc3a0c..81e994c 100644 --- a/MapTP.App/MainWindow.xaml.cs +++ b/MapTP.App/MainWindow.xaml.cs @@ -8,7 +8,6 @@ using System.Windows.Media; using System.Xml; using System.Xml.Serialization; -using Walterlv.Windows.Effects; namespace MapTP.App { @@ -98,9 +97,12 @@ public void ReceiveTouchpadSize(int X, int Y) { this.TouchpadSizeX = X; this.TouchpadSizeY = Y; - TpAreaRect.Width = CalculateRectangle(TouchpadSizeX, TouchpadSizeY); - TpRectGrid.Width = CalculateRectangle(TouchpadSizeX, TouchpadSizeY); - TouchpadSizeTB.Text = $"Touchpad size: {TouchpadSizeX}x{TouchpadSizeY}"; + if (TouchpadSizeX != 0 && TouchpadSizeY != 0) + { + TpAreaRect.Width = CalculateRectangle(TouchpadSizeX, TouchpadSizeY); + TpRectGrid.Width = CalculateRectangle(TouchpadSizeX, TouchpadSizeY); + TouchpadSizeTB.Text = $"Touchpad size: {TouchpadSizeX}x{TouchpadSizeY}"; + } return; } @@ -190,19 +192,42 @@ protected override void OnSourceInitialized(EventArgs e) private void OnLoaded(object sender, RoutedEventArgs e) { - var WalterlvCompositor = new WindowAccentCompositor(this) + + if (!(Environment.OSVersion.Version > new Version(10, 0, 17763))) + { + this.Background = Brushes.White; + } + else if (Environment.OSVersion.Version.Build >= 22000) // Mica { - Color = Color.FromArgb(0x33, 0x87, 0xce, 0xfa) - }; - WalterlvCompositor.IsEnabled = true; - if (!(Environment.OSVersion.Version > new Version(10, 0, 17763))) + // Get PresentationSource + PresentationSource presentationSource = PresentationSource.FromVisual((Visual)sender); + + // Subscribe to PresentationSource's ContentRendered event + presentationSource.ContentRendered += (s,ev)=>OnRendered(PresentationSource.FromVisual((Visual)sender) as HwndSource); + + } + else { - this.Background = Brushes.Aqua; - WalterlvCompositor.IsEnabled = false; + + var WalterlvCompositor = new BlurManager(this) + { + Color = Color.FromArgb(0x33, 0x87, 0xce, 0xfa) + }; + WalterlvCompositor.IsEnabled = true; } } + /// + /// Enable Mica when the window is rendered + /// + /// + /// + private void OnRendered(HwndSource src) + { + var mica = new Mica(src); + mica.enable = true; + } /// /// This method is for limiting TextBoxes only to accept numbers diff --git a/MapTP.App/MapTP.App.csproj b/MapTP.App/MapTP.App.csproj index a2673f4..ac3b388 100644 --- a/MapTP.App/MapTP.App.csproj +++ b/MapTP.App/MapTP.App.csproj @@ -1,20 +1,134 @@ - + + + - net7.0-windows10.0.18362.0 + Debug + AnyCPU + {B640177C-8B8A-4E97-931A-8C13EF816C12} WinExe - false - true - true + MapTP.App + MapTP.App + v4.8 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + true + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 app.manifest - 10.0.18362.0 - - - - - + + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + AboutWindow.xaml + + + App.xaml + Code + + + + CalibrateWindow.xaml + + + MainWindow.xaml + Code + + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + 3.4.0 + + + 7.0.0 + + \ No newline at end of file diff --git a/MapTP.App/Properties/AssemblyInfo.cs b/MapTP.App/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..51cc4c0 --- /dev/null +++ b/MapTP.App/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("MapTP.App")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MapTP.App")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +//若要开始生成可本地化的应用程序,请设置 +//.csproj 文件中的 CultureYouAreCodingWith +//在 中。例如,如果你使用的是美国英语。 +//使用的是美国英语,请将 设置为 en-US。 然后取消 +//对以下 NeutralResourceLanguage 特性的注释。 更新 +//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。 + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //主题特定资源词典所处位置 + //(未在页面中找到资源时使用, + //或应用程序资源字典中找到时使用) + ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置 + //(未在页面中找到资源时使用, + //、应用程序或任何主题专用资源字典中找到时使用) +)] + + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MapTP.App/ScreenManager.cs b/MapTP.App/ScreenManager.cs index b10d97a..c1f397a 100644 --- a/MapTP.App/ScreenManager.cs +++ b/MapTP.App/ScreenManager.cs @@ -1,11 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Interop; +using System.Drawing; using System.Windows; -using System.Drawing; +using System.Windows.Interop; namespace MapTP.App diff --git a/MapTP.sln b/MapTP.sln index efd7f33..6b54fdb 100644 --- a/MapTP.sln +++ b/MapTP.sln @@ -1,9 +1,8 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.7.34031.279 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapTP.App", "MapTP.App\MapTP.App.csproj", "{F5217DE5-48E8-404D-A4F0-15B88C21B5E4}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapTP.App", "MapTP.App\MapTP.App.csproj", "{B640177C-8B8A-4E97-931A-8C13EF816C12}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,10 +10,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F5217DE5-48E8-404D-A4F0-15B88C21B5E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5217DE5-48E8-404D-A4F0-15B88C21B5E4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F5217DE5-48E8-404D-A4F0-15B88C21B5E4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F5217DE5-48E8-404D-A4F0-15B88C21B5E4}.Release|Any CPU.Build.0 = Release|Any CPU + {B640177C-8B8A-4E97-931A-8C13EF816C12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B640177C-8B8A-4E97-931A-8C13EF816C12}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B640177C-8B8A-4E97-931A-8C13EF816C12}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B640177C-8B8A-4E97-931A-8C13EF816C12}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE