Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce the frequency of AccentColor change event #144

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

krlvm
Copy link

@krlvm krlvm commented Oct 12, 2021

Hello.

I'm developing a small application that resembles Windows 10 taskbar flyouts and I need to react to accent color change when "Show accent color on the following surfaces: Start, taskbar, and action center" setting is enabled. After subscribing to accent color change event (AccentColors.StaticPropertyChanged += Theme_AccentColorChanged, Theme_AccentColorChanged method is querying registry, modifies AcrylicWindow properties and refreshes the tray icon) and changing the accent color my application freezes and crashes.

After some investigations, I've noticed that changing accent color on Windows 10 (at least, since 1903, I'm using 21H1, which is based on 20H1) is way slower than on Windows 8, 7, Vista or Windows 10 1507 (did not test on other versions < 1903). That's because the latest Windows 10 versions contains a bug which foces system to issue multiple (seems to be at least 10) WM_DWMCOLORIZATIONCOLORCHANGED messages.

I've modified FluentWPF code to issue 14 times less event calls, because in the current implementation changing of each property fires event handlers immediately, when it calls it once after Initialize() in this patch.

Thank you for creating this project again.

@selastingeorge
Copy link

selastingeorge commented Oct 12, 2021

For windows 10 Build 10240 and above instead of listening to the WM_DWMCOLORIZATIONCOLORCHANGED message, using UISettings class from Windows.UI.Composition Seems to be the best Solution. The UISettings Class have ColorValuesChanged
event which only fires once when the accent color is changed. But you need to add Microsoft.Windows.SDK.Contracts nuget package or CSWinRT package to get access to this Class. Here is a Demo:

    public class AccentColors
    {
        private static readonly UISettings settings;

        private static SolidColorBrush systemAccent;
        private static SolidColorBrush systemAccentDark1;
        private static SolidColorBrush systemAccentDark2;
        private static SolidColorBrush systemAccentDark3;
        private static SolidColorBrush systemAccentLight1;
        private static SolidColorBrush systemAccentLight2;
        private static SolidColorBrush systemAccentLight3;

        public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;

        static AccentColors()
        {
            settings = new UISettings();
            settings.ColorValuesChanged += OnClolorValuesChanged;
            Initialize();
        }

        private static void Initialize()
        {
            SystemAccent = GetBrush(settings.GetColorValue(UIColorType.Accent));
            SystemAccentDark1 = GetBrush(settings.GetColorValue(UIColorType.AccentDark1));
            SystemAccentDark2 = GetBrush(settings.GetColorValue(UIColorType.AccentDark2));
            SystemAccentDark3 = GetBrush(settings.GetColorValue(UIColorType.AccentDark3));
            SystemAccentLight1 = GetBrush(settings.GetColorValue(UIColorType.AccentLight1));
            SystemAccentLight2 = GetBrush(settings.GetColorValue(UIColorType.AccentLight2));
            SystemAccentLight3 = GetBrush(settings.GetColorValue(UIColorType.AccentLight3));
        }

        private static SolidColorBrush GetBrush(Windows.UI.Color color)
        {
            return new SolidColorBrush(Color.FromArgb(color.A, color.R, color.G, color.B));
        }

        #region Event Handlers

        private static void OnClolorValuesChanged(UISettings sender, object args)
        {
            Application.Current.Dispatcher.Invoke(() =>
            {
                Initialize();
            });
        }

        private static void OnStaticPropertyChanged([CallerMemberName] string propertyName = null)
        {
            StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
        }

        #endregion

        #region Brushes

        public static SolidColorBrush SystemAccent
        {
            get => systemAccent;
            set
            {
                systemAccent = value;
                OnStaticPropertyChanged();
            }
        }

        public static SolidColorBrush SystemAccentDark1
        {
            get => systemAccentDark1;
            set
            {
                systemAccentDark1 = value;
                OnStaticPropertyChanged();
            }
        }

        public static SolidColorBrush SystemAccentDark2
        {
            get => systemAccentDark2;
            set
            {
                systemAccentDark2 = value;
                OnStaticPropertyChanged();
            }
        }

        public static SolidColorBrush SystemAccentDark3
        {
            get => systemAccentDark3;
            set
            {
                systemAccentDark3 = value;
                OnStaticPropertyChanged();
            }
        }

        public static SolidColorBrush SystemAccentLight1
        {
            get => systemAccentLight1;
            set
            {
                systemAccentLight1 = value;
                OnStaticPropertyChanged();
            }
        }

        public static SolidColorBrush SystemAccentLight2
        {
            get => systemAccentLight2;
            set
            {
                systemAccentLight2 = value;
                OnStaticPropertyChanged();
            }
        }

        public static SolidColorBrush SystemAccentLight3
        {
            get => systemAccentLight3;
            set
            {
                systemAccentLight3 = value;
                OnStaticPropertyChanged();
            }
        }

        #endregion
    }

@krlvm
Copy link
Author

krlvm commented Oct 12, 2021

@Extrimis thank you for this important suggestion, it is even more important because my proposed method did not work at all due to the fact that I did not notice that OnStaticPropertyChanged takes a property name as an argument.

If someone also ran into an issue where the accent color is updated repeatedly in a short amount of time, add a propertyName check to the EventArgs, it should be equal to ImmersiveSystemAccentLight3Brush (the last property to change).

@pa-0
Copy link

pa-0 commented Oct 7, 2024

@krlvm Does this pull request still apply then?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants