通过 XAML 将 Windows Accent Color 设置为 WPF 窗口背景并监听 Accent Color Change

Posted

技术标签:

【中文标题】通过 XAML 将 Windows Accent Color 设置为 WPF 窗口背景并监听 Accent Color Change【英文标题】:Setting Windows Accent Color as WPF Window Background through XAML and listen to Accent Color Change 【发布时间】:2021-03-17 04:57:25 【问题描述】:

我正在创建一个 WPF 项目,它使用 windows 10 强调色作为我的 WPF 主窗口的背景。我能够使用GetImmersiveUserColorSetPreference()GetImmersiveColorTypeFromName()GetImmersiveColorFromColorSetEx() 获得窗口的强调色,并且可以将它用作我的窗口背景。但问题是我无法在更改强调色时自动更改背景(我必须重新启动才能更改背景)。

这是我使用的代码:

AccentColors.cs

public static class AccentColors 

    private static Brush systemAccentBrush;

    static AccentColors() 
        InitializeBrushes();
    

    public static void InitializeBrushes() 
        SystemAccentBrush = CreateBrush(GetColorByTypeName("ImmersiveSystemAccent"));
    

    public static Color GetColorByTypeName(string name) 
        var colorSet = NativeMethods.GetImmersiveUserColorSetPreference(false, false);
        var colorType = NativeMethods.GetImmersiveColorTypeFromName(name);
        var rawColor = NativeMethods.GetImmersiveColorFromColorSetEx(colorSet, colorType, false, 0);

        var bytes = BitConverter.GetBytes(rawColor);
        return Color.FromArgb(bytes[3], bytes[0], bytes[1], bytes[2]);
    

    private static Brush CreateBrush(Color color) 
        var brush = new SolidColorBrush(color);
        return brush;
    

    #region Brushes
    public static Brush SystemAccentBrush 
        get 
            return systemAccentBrush;
        
        private set 
            if (!object.Equals(systemAccentBrush, value)) 
                systemAccentBrush = value;
            
        
    

    #endregion

InitializeBrushes() 函数是从 WndProc WM_DWMCOLORIZATIONCOLORCHANGED 调用的,这有助于我将 SystemAccentBrush 设置为当前系统 Accent Color 并且它工作得很好。但是当我设置SystemAccentBrush 作为控件的背景,它不会根据 Accent Color 更改(但画笔颜色正在更改)而更改。

这是我用来将 SystemAccentBrush 设置为 Grid 的背景的代码:

<Grid x:Name="container" Background="x:Static common:AccentColors.SystemAccentBrush">
</Grid>

我认为问题与此有关:

x:Static common:AccentColors.SystemAccentBrush

所以我尝试将其设置为动态源,如下所示:

DynamicSource x:Static common:AccentColors.SystemAccentBrush

然后背景消失了。

有什么办法可以解决这个问题吗?

【问题讨论】:

【参考方案1】:

x:Static markup extension 用于静态引用,不会在运行时获取任何更改。

引用以符合公共语言规范 (CLS) 的方式定义的任何静态按值代码实体。

如果您创建在运行时更改的静态属性,则必须使用绑定并实现类似于INotifyPropertyChangedPropertyChanged 事件的静态等效项。从 WPF 4.5 开始支持此功能,并且有 two different ways 来实现它。

事件处理程序

如下更改您的属性并为您的属性创建一个事件SystemAccentBrushChanged。使用nullEventArgs.EmptySystemAccentBrush 的设置器中引发事件。

private static Brush systemAccentBrush;
public static Brush SystemAccentBrush

   get => systemAccentBrush;
   private set
   
      if (Equals(systemAccentBrush, value))
         return;

      systemAccentBrush = value;
      SystemAccentBrushChanged?.Invoke(null, EventArgs.Empty);
   


public static event EventHandler SystemAccentBrushChanged;

静态属性改变事件

如下更改您的属性并创建一个事件StaticPropertyChanged 并使用已更改的属性名称引发它。此事件可以与不同的属性一起使用。

private static Brush systemAccentBrush;

public static Brush SystemAccentBrush

   get => systemAccentBrush;
   private set
   
      if (Equals(systemAccentBrush, value))
         return;

      systemAccentBrush = value;
      OnStaticPropertyChanged();
   


public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;

private static void OnStaticPropertyChanged([CallerMemberName] string propertyName = null)

   StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));

绑定静态属性

为了绑定静态属性以获取更改通知,您必须使用BindingPath 和括号来访问类成员。如果省略Path,XAML 解析器和设计器将抛出错误。

<Grid Background="Binding Path=(common:AccentColors.SystemAccentBrush)">

最后一点说明:虽然这种方式支持带有属性更改通知的静态绑定,但您可能需要考虑将静态类迁移到单例。这样,您可以将SystemAccentBrush 设为非静态属性,实现INotifyPropertyChanged 并照常绑定该属性,无需任何特殊语法或自定义静态事件。

【讨论】:

非常感谢它运行良好,您的回答非常容易理解。你绝对值得一票。

以上是关于通过 XAML 将 Windows Accent Color 设置为 WPF 窗口背景并监听 Accent Color Change的主要内容,如果未能解决你的问题,请参考以下文章

在通用 Windows 应用 (XAML) 中启用后退/前进和刷新手势

用于设计 Windows 10 应用程序的 GUI?还是仅通过 XAML?

如何将 Windows 商店中的 Windows Phone 应用程序(8.1 XAML)迁移到 8.1 Silverlight?

在 App.xaml 中添加按钮并以编程方式将其添加到其他 xaml 页面 Windows 运行时应用

将 wpf xaml 图像绑定到 System.Windows.Controls.Image 不起作用

Blend/XAML:通过按钮绑定 ItemsSource