WPF 窗体设置亚克力效果
框架使用大于等于.NET40
。
Visual Studio 2022
。
项目使用 MIT 开源许可协议。
WindowAcrylicBlur
设置亚克力颜色。
Opacity
设置透明度。
实现代码
1) 准备WindowAcrylicBlur.cs如下:
using System; | |
using System.Runtime.InteropServices; | |
using System.Windows; | |
using System.Windows.Interop; | |
using System.Windows.Media; | |
using Microsoft.Win32; | |
using Microsoft.Windows.Shell; | |
namespace WPFDevelopers.Controls | |
{ | |
internal enum AccentState | |
{ | |
ACCENT_DISABLED = 0, | |
ACCENT_ENABLE_GRADIENT = 1, | |
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, | |
ACCENT_ENABLE_BLURBEHIND = 3, | |
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, | |
ACCENT_INVALID_STATE = 5 | |
} | |
[ | ]|
internal struct AccentPolicy | |
{ | |
public AccentState AccentState; | |
public uint AccentFlags; | |
public uint GradientColor; | |
public uint AnimationId; | |
} | |
[ | ]|
internal struct WindowCompositionAttributeData | |
{ | |
public WindowCompositionAttribute Attribute; | |
public IntPtr Data; | |
public int SizeOfData; | |
} | |
internal enum WindowCompositionAttribute | |
{ | |
// ... | |
WCA_ACCENT_POLICY = 19 | |
// ... | |
} | |
internal class WindowOldConfig | |
{ | |
public bool AllowsTransparency; | |
public Brush Background; | |
public WindowChrome WindowChrome; | |
public WindowStyle WindowStyle = WindowStyle.SingleBorderWindow; | |
} | |
internal class WindowOSHelper | |
{ | |
public static Version GetWindowOSVersion() | |
{ | |
var regKey = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion"); | |
int major; | |
int minor; | |
int build; | |
int revision; | |
try | |
{ | |
var str = regKey.GetValue("CurrentMajorVersionNumber")?.ToString(); | |
int.TryParse(str, out major); | |
str = regKey.GetValue("CurrentMinorVersionNumber")?.ToString(); | |
int.TryParse(str, out minor); | |
str = regKey.GetValue("CurrentBuildNumber")?.ToString(); | |
int.TryParse(str, out build); | |
str = regKey.GetValue("BaseBuildRevisionNumber")?.ToString(); | |
int.TryParse(str, out revision); | |
return new Version(major, minor, build, revision); | |
} | |
catch (Exception) | |
{ | |
return new Version(0, 0, 0, 0); | |
} | |
finally | |
{ | |
regKey.Close(); | |
} | |
} | |
} | |
public class WindowAcrylicBlur : Freezable | |
{ | |
private static readonly Color _BackgtoundColor = Color.FromArgb(0x01, 0, 0, 0); //设置透明色 防止穿透 | |
[ | ]|
internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data); | |
private static bool EnableAcrylicBlur(Window window, Color color, double opacity, bool enable) | |
{ | |
if (window == null) | |
return false; | |
AccentState accentState; | |
var vOsVersion = WindowOSHelper.GetWindowOSVersion(); | |
if (vOsVersion > new Version(10, 0, 17763)) //1809 | |
accentState = enable ? AccentState.ACCENT_ENABLE_ACRYLICBLURBEHIND : AccentState.ACCENT_DISABLED; | |
else if (vOsVersion > new Version(10, 0)) | |
accentState = enable ? AccentState.ACCENT_ENABLE_BLURBEHIND : AccentState.ACCENT_DISABLED; | |
else | |
accentState = AccentState.ACCENT_DISABLED; | |
if (opacity > 1) | |
opacity = 1; | |
var windowHelper = new WindowInteropHelper(window); | |
var accent = new AccentPolicy(); | |
var opacityIn = (uint) (255 * opacity); | |
accent.AccentState = accentState; | |
if (enable) | |
{ | |
var blurColor = (uint) ((color.R << 0) | (color.G << 8) | (color.B << 16) | (color.A << 24)); | |
var blurColorIn = blurColor; | |
if (opacityIn > 0) | |
blurColorIn = (opacityIn << 24) | (blurColor & 0xFFFFFF); | |
else if (opacityIn == 0 && color.A == 0) | |
blurColorIn = (0x01 << 24) | (blurColor & 0xFFFFFF); | |
if (accent.GradientColor == blurColorIn) | |
return true; | |
accent.GradientColor = blurColorIn; | |
} | |
var accentStructSize = Marshal.SizeOf(accent); | |
var accentPtr = Marshal.AllocHGlobal(accentStructSize); | |
Marshal.StructureToPtr(accent, accentPtr, false); | |
var data = new WindowCompositionAttributeData(); | |
data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY; | |
data.SizeOfData = accentStructSize; | |
data.Data = accentPtr; | |
SetWindowCompositionAttribute(windowHelper.Handle, ref data); | |
Marshal.FreeHGlobal(accentPtr); | |
return true; | |
} | |
private static void Window_Initialized(object sender, EventArgs e) | |
{ | |
if (!(sender is Window window)) | |
return; | |
var config = new WindowOldConfig | |
{ | |
WindowStyle = window.WindowStyle, | |
AllowsTransparency = window.AllowsTransparency, | |
Background = window.Background | |
}; | |
var vWindowChrome = WindowChrome.GetWindowChrome(window); | |
if (vWindowChrome == null) | |
{ | |
window.WindowStyle = WindowStyle.None; //一定要将窗口的背景色改为透明才行 | |
window.AllowsTransparency = true; //一定要将窗口的背景色改为透明才行 | |
window.Background = new SolidColorBrush(_BackgtoundColor); //一定要将窗口的背景色改为透明才行 | |
} | |
else | |
{ | |
config.WindowChrome = new WindowChrome | |
{ | |
GlassFrameThickness = vWindowChrome.GlassFrameThickness | |
}; | |
window.Background = Brushes.Transparent; //一定要将窗口的背景色改为透明才行 | |
var vGlassFrameThickness = vWindowChrome.GlassFrameThickness; | |
vWindowChrome.GlassFrameThickness = new Thickness(0, vGlassFrameThickness.Top, 0, 0); | |
} | |
SetWindowOldConfig(window, config); | |
window.Initialized -= Window_Initialized; | |
} | |
private static void Window_Loaded(object sender, RoutedEventArgs e) | |
{ | |
if (!(sender is Window window)) | |
return; | |
var vBlur = GetWindowAcrylicBlur(window); | |
if (vBlur != null) | |
EnableAcrylicBlur(window, vBlur.BlurColor, vBlur.Opacity, true); | |
window.Loaded -= Window_Loaded; | |
} | |
protected override Freezable CreateInstanceCore() | |
{ | |
throw new NotImplementedException(); | |
} | |
protected override void OnChanged() | |
{ | |
base.OnChanged(); | |
} | |
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) | |
{ | |
base.OnPropertyChanged(e); | |
} | |
public static WindowAcrylicBlur GetWindowAcrylicBlur(DependencyObject obj) | |
{ | |
return (WindowAcrylicBlur) obj.GetValue(WindowAcrylicBlurProperty); | |
} | |
public static void SetWindowAcrylicBlur(DependencyObject obj, WindowAcrylicBlur value) | |
{ | |
obj.SetValue(WindowAcrylicBlurProperty, value); | |
} | |
public static readonly DependencyProperty WindowAcrylicBlurProperty = | |
DependencyProperty.RegisterAttached("WindowAcrylicBlur", typeof(WindowAcrylicBlur), | |
typeof(WindowAcrylicBlur), | |
new PropertyMetadata(default(WindowAcrylicBlur), OnWindowAcryBlurPropertyChangedCallBack)); | |
private static void OnWindowAcryBlurPropertyChangedCallBack(DependencyObject d, | |
DependencyPropertyChangedEventArgs e) | |
{ | |
if (!(d is Window window)) | |
return; | |
if (e.OldValue == null && e.NewValue == null) | |
return; | |
if (e.OldValue == null && e.NewValue != null) | |
{ | |
window.Initialized += Window_Initialized; | |
window.Loaded += Window_Loaded; | |
} | |
if (e.OldValue != null && e.NewValue == null) | |
{ | |
var vConfig = GetWindowOldConfig(d); | |
if (vConfig != null) | |
{ | |
window.WindowStyle = vConfig.WindowStyle; | |
window.AllowsTransparency = vConfig.AllowsTransparency; | |
window.Background = vConfig.Background; | |
if (vConfig.WindowChrome != null) | |
{ | |
var vWindowChrome = WindowChrome.GetWindowChrome(window); | |
if (vWindowChrome != null) | |
vWindowChrome.GlassFrameThickness = vConfig.WindowChrome.GlassFrameThickness; | |
} | |
} | |
} | |
if (e.OldValue == e.NewValue) | |
{ | |
if (!window.IsLoaded) | |
return; | |
var vBlur = e.NewValue as WindowAcrylicBlur; | |
if (vBlur == null) | |
return; | |
EnableAcrylicBlur(window, vBlur.BlurColor, vBlur.Opacity, true); | |
} | |
} | |
private static WindowOldConfig GetWindowOldConfig(DependencyObject obj) | |
{ | |
return (WindowOldConfig) obj.GetValue(WindowOldConfigProperty); | |
} | |
private static void SetWindowOldConfig(DependencyObject obj, WindowOldConfig value) | |
{ | |
obj.SetValue(WindowOldConfigProperty, value); | |
} | |
// Using a DependencyProperty as the backing store for WindowOldConfig. This enables animation, styling, binding, etc... | |
private static readonly DependencyProperty WindowOldConfigProperty = | |
DependencyProperty.RegisterAttached("WindowOldConfig", typeof(WindowOldConfig), typeof(WindowAcrylicBlur), | |
new PropertyMetadata(default(WindowOldConfig))); | |
public Color BlurColor | |
{ | |
get => (Color) GetValue(BlurColorProperty); | |
set => SetValue(BlurColorProperty, value); | |
} | |
// Using a DependencyProperty as the backing store for BlurColor. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty BlurColorProperty = | |
DependencyProperty.Register("BlurColor", typeof(Color), typeof(WindowAcrylicBlur), | |
new PropertyMetadata(default(Color))); | |
public double Opacity | |
{ | |
get => (double) GetValue(OpacityProperty); | |
set => SetValue(OpacityProperty, value); | |
} | |
// Using a DependencyProperty as the backing store for Opacity. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty OpacityProperty = | |
DependencyProperty.Register("Opacity", typeof(double), typeof(WindowAcrylicBlur), | |
new PropertyMetadata(default(double))); | |
} | |
} |
2) 使用AcrylicBlurWindowExample.xaml如下:
<Window x:Class="WPFDevelopers.Samples.ExampleViews.AcrylicBlurWindowExample" | |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |
xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews" | |
xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers" | |
mc:Ignorable="d" WindowStartupLocation="CenterScreen" | |
ResizeMode="CanMinimize" | |
Title="Login" Height="350" Width="400"> | |
<wpfdev:WindowChrome.WindowChrome> | |
<wpfdev:WindowChrome GlassFrameThickness="0 1 0 0"/> | |
</wpfdev:WindowChrome.WindowChrome> | |
<wpfdev:WindowAcrylicBlur.WindowAcrylicBlur> | |
<wpfdev:WindowAcrylicBlur BlurColor="AliceBlue" Opacity="0.2"/> | |
</wpfdev:WindowAcrylicBlur.WindowAcrylicBlur> | |
<Grid> | |
<Grid.RowDefinitions> | |
<RowDefinition Height="40"/> | |
<RowDefinition/> | |
</Grid.RowDefinitions> | |
<StackPanel HorizontalAlignment="Right" | |
Orientation="Horizontal" | |
Grid.Column="1" | |
wpfdev:WindowChrome.IsHitTestVisibleInChrome="True"> | |
<Button Style="{DynamicResource WindowButtonStyle}" | |
Command="{Binding CloseCommand,RelativeSource={RelativeSource AncestorType=local:AcrylicBlurWindowExample}}" Cursor="Hand"> | |
<Path Width="10" Height="10" | |
HorizontalAlignment="Center" | |
VerticalAlignment="Center" | |
Data="{DynamicResource PathMetroWindowClose}" | |
Fill="Red" | |
Stretch="Fill" /> | |
</Button> | |
</StackPanel> | |
<StackPanel Grid.Row="1" Margin="40,0,40,0" | |
wpfdev:WindowChrome.IsHitTestVisibleInChrome="True"> | |
<Image Source="/WPFDevelopers.ico" Width="80" Height="80"/> | |
<TextBox wpfdev:ElementHelper.IsWatermark="True" wpfdev:ElementHelper.Watermark="账户" Margin="0,20,0,0" Cursor="Hand"/> | |
<PasswordBox wpfdev:ElementHelper.IsWatermark="True" wpfdev:ElementHelper.Watermark="密码" Margin="0,20,0,0" Cursor="Hand"/> | |
<Button x:Name="LoginButton" | |
Content="登 录" | |
Margin="0,20,0,0" | |
Style="{StaticResource PrimaryButton}"/> | |
<Grid Margin="0 20 0 0"> | |
<TextBlock FontSize="12"> | |
<Hyperlink Foreground="Black" TextDecorations="None">忘记密码</Hyperlink> | |
</TextBlock> | |
<TextBlock FontSize="12" HorizontalAlignment="Right" Margin="0 0 -1 0"> | |
<Hyperlink Foreground="#4370F5" TextDecorations="None">注册账号</Hyperlink> | |
</TextBlock> | |
</Grid> | |
</StackPanel> | |
</Grid> | |
</Window> |
3) 使用AcrylicBlurWindowExample.xaml.cs如下:
using System.Windows; | |
using System.Windows.Input; | |
using WPFDevelopers.Samples.Helpers; | |
namespace WPFDevelopers.Samples.ExampleViews | |
{ | |
/// <summary> | |
/// AcrylicBlurWindowExample.xaml 的交互逻辑 | |
/// </summary> | |
public partial class AcrylicBlurWindowExample : Window | |
{ | |
public AcrylicBlurWindowExample() | |
{ | |
InitializeComponent(); | |
} | |
public ICommand CloseCommand => new RelayCommand(obj => | |
{ | |
Close(); | |
}); | |
} | |
} |
实现效果