Reputation: 897
I'm creating a WPF project which uses the windows 10 Accent color as background for my WPF Main Window. I was able to get the windows Accent Color using GetImmersiveUserColorSetPreference()
, GetImmersiveColorTypeFromName()
& GetImmersiveColorFromColorSetEx()
and I was able to use it as my window background. But the problem is that i was not able to change the Background automatically when the Accent Color is Changed (I must restart to change the background).
Here is the code that I use:
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
The InitializeBrushes()
function is called from WndProc
WM_DWMCOLORIZATIONCOLORCHANGED
which helps me to set SystemAccentBrush
to the current system Accent Color and it works perfectly.But When i'm setting the SystemAccentBrush
as background for a control it doesn't change based on the Accent Color Change (But the Brush Color is changing).
Here is the code that i used to set SystemAccentBrush as the background for a Grid:
<Grid x:Name="container" Background="{x:Static common:AccentColors.SystemAccentBrush}">
</Grid>
I think the problem is related to this :
{x:Static common:AccentColors.SystemAccentBrush}
So i tried setting it as Dynamic source like this:
{DynamicSource {x:Static common:AccentColors.SystemAccentBrush}}
Then the background disappears.
Is there any way to overcome this problem?
Upvotes: 1
Views: 1124
Reputation: 22089
The x:Static
markup extension is for static references and does not pick up any changes at runtime.
References any static by-value code entity that is defined in a Common Language Specification (CLS)–compliant way.
If you create static properties that change at runtime, you have to use a binding and implement a static equivalent of the PropertyChanged
event similar to INotifyPropertyChanged
. This feature is supported since WPF 4.5 and there are two different ways to implement it.
Change your property as below and create an event SystemAccentBrushChanged
for your property. Raise the event in the setter of SystemAccentBrush
with null
and EventArgs.Empty
.
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;
Change your property as below and create an event StaticPropertyChanged
and raise it with the property name that was changed. This event can be used with different properties.
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));
}
In order to bind the static property to pick up the change notifications, you have to use a Binding
with Path
and parentheses to access the class member. If you omit Path
, the XAML parser and the designer will throw and error.
<Grid Background="{Binding Path=(common:AccentColors.SystemAccentBrush)}">
A final note on this: Although static bindings with property changed notifications are supported this way, you might want to think about migrating your static class to a singleton. This way, you could make SystemAccentBrush
a non-static property, implement INotifyPropertyChanged
and bind the property as usual, without any special syntax or custom static events.
Upvotes: 1