Dominic Jonas
Dominic Jonas

Reputation: 5015

WinUI Removing the overwritten resources is not considered

At runtime I would like to activate / deactivate a highlighting effect.

For this I created an AttachedProperty Control.Highlight. This adds new Resources to the FrameworkElement and thus overwrites the default values for e.g. ButtonBackground (https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.button?view=winrt-22000).

This problem does not exist with ButtonBackgroundPointerOver and ButtonBackgroundPressed. Access is new here each time the corresponding Storyboard has been triggered: https://github.com/microsoft/microsoft-ui-xaml/blob/3b9d847e59094543a0a3e07a0779b6634c581bcf/dev/CommonStyles/Button_themeresources.xaml#L183

Unfortunately, the Background is not set again once the control has been rendered. FrameworkElement.UpdateLayout() does not bring the desired effect here either.

So I decided to reapply the default Style (DefaultButtonStyle) to eventually disable the highlighting effect. However, this does not work and the background remains unchanged.

https://github.com/microsoft/microsoft-ui-xaml/blob/3b9d847e59094543a0a3e07a0779b6634c581bcf/dev/CommonStyles/Button_themeresources.xaml#L136

AttachedProperty

public class Control
{
    public static readonly DependencyProperty HightlightProperty = DependencyProperty.RegisterAttached("Hightlight", typeof(bool), typeof(Control), new PropertyMetadata(null, HightlightChangedCallback));

    public static bool GetHightlight(DependencyObject target) => (bool)target.GetValue(HightlightProperty);

    public static void SetHightlight(DependencyObject target, bool value) => target.SetValue(HightlightProperty, value);

    private static void HightlightChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is FrameworkElement frameworkElement)
        {
            Hightlight(frameworkElement, (bool)e.NewValue);
        }
    }

    private static void Hightlight(FrameworkElement frameworkElement, bool highlight)
    {
        try
        {
            var resourceDictionary = Application.Current.Resources.MergedDictionaries.First(dictionary => dictionary.Source.OriginalString.EndsWith("Highlight.xaml"));

            switch (frameworkElement)
            {
                case Button button:
                    resourceDictionary = (ResourceDictionary)resourceDictionary["ButtonHighlightLight"]; // https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.button?view=winrt-22000
                    break;
                default:
                    _ = App.ServiceProvider.GetRequiredService<IDialogService>().ShowAsync(new ContentDialog
                    {
                        Title = "undefined FrameworkElement for Highlight",
                        Content = $"'{frameworkElement.GetType().Name}' case missing",
                        CloseButtonText = "Ok!"
                    });
                    return;
            }

            foreach (var kvp in resourceDictionary)
            {
                if (highlight)
                {
                    frameworkElement.Resources.Add(kvp);
                }
                else
                {
                    frameworkElement.Resources.Remove(kvp.Key);
                }
            }

            var styles = Application.Current.Resources.MergedDictionaries[0].Where(pair => pair.Value is Style).ToList();
            var buttonStyles = styles.Where(pair => ((Style)pair.Value).TargetType == typeof(Button)).ToList();
            var style = buttonStyles.First(); // DefaultButtonStyle

            frameworkElement.Style = (Style)style.Value;
        }
        catch (Exception e)
        {
            _ = App.ServiceProvider.GetRequiredService<IDialogService>().ShowExceptionAsync(e, Translations.DialogTitle_Error);
        }
    }
}

XAML

<ToggleButton IsChecked="{x:Bind ViewModel.Highlight, Mode=TwoWay}"/>
<Button attachedProperties:Control.Hightlight="{x:Bind ViewModel.Highlight, Mode=OneWay}" MinWidth="120" />

Highlight.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <ResourceDictionary x:Key="ButtonHighlightLight"> <!--https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.button?view=winrt-22000-->
    <SolidColorBrush x:Key="ButtonBackground" Color="#ff6900"/>
    <SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="#ff6900"/>
    <SolidColorBrush x:Key="ButtonBackgroundPressed" Color="Red"/>
  </ResourceDictionary>
</ResourceDictionary>

Highlighting

The Background should revert back to its original Color.

Upvotes: 0

Views: 366

Answers (1)

Dominic Jonas
Dominic Jonas

Reputation: 5015

Changing the Theme leads to the goal. I'm not sure if this is the right way, but this will reload the Resources.

// force resource reload
if (frameworkElement.ActualTheme == ElementTheme.Light)
{
    frameworkElement.RequestedTheme = ElementTheme.Dark;
    frameworkElement.RequestedTheme = ElementTheme.Light;
}
else if (frameworkElement.ActualTheme == ElementTheme.Dark)
{
    frameworkElement.RequestedTheme = ElementTheme.Light;
    frameworkElement.RequestedTheme = ElementTheme.Dark;
}
else
{
    frameworkElement.RequestedTheme = ElementTheme.Light;
    frameworkElement.RequestedTheme = ElementTheme.Dark;
    frameworkElement.RequestedTheme = ElementTheme.Default;
}

enter image description here


public class Control
{
    public static readonly DependencyProperty HightlightProperty = DependencyProperty.RegisterAttached("Hightlight", typeof(bool), typeof(Control), new PropertyMetadata(null, HightlightChangedCallback));

    public static bool GetHightlight(DependencyObject target) => (bool)target.GetValue(HightlightProperty);

    public static void SetHightlight(DependencyObject target, bool value) => target.SetValue(HightlightProperty, value);

    private static void HightlightChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is FrameworkElement frameworkElement)
        {
            Hightlight(frameworkElement, (bool)e.NewValue);
        }
    }

    private static void Hightlight(FrameworkElement frameworkElement, bool highlight)
    {
        try
        {
            var resourceDictionary = Application.Current.Resources.MergedDictionaries.First(dictionary => dictionary.Source.OriginalString.EndsWith("Highlight.xaml"));
            resourceDictionary = (ResourceDictionary)resourceDictionary[$"Highlight{frameworkElement.ActualTheme}"]; // https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.button?view=winrt-22000

            // check if resource was defined
            if (resourceDictionary.Keys.All(o => !o.ToString().StartsWith(frameworkElement.GetType().Name)))
            {
                _ = App.ServiceProvider.GetRequiredService<IDialogService>().ShowAsync(new ContentDialog
                {
                    Title = "undefined FrameworkElement for Control.Highlight",
                    Content = $"'{frameworkElement.GetType().Name}' resource missing in Highlight.xaml",
                    CloseButtonText = "Ok!"
                });
                return;
            }

            foreach (var kvp in resourceDictionary)
            {
                if (highlight)
                {
                    frameworkElement.Resources.Add(kvp);
                }
                else
                {
                    frameworkElement.Resources.Remove(kvp.Key);
                }
            }

            // force resource reload
            if (frameworkElement.ActualTheme == ElementTheme.Light)
            {
                frameworkElement.RequestedTheme = ElementTheme.Dark;
                frameworkElement.RequestedTheme = ElementTheme.Light;
            }
            else if (frameworkElement.ActualTheme == ElementTheme.Dark)
            {
                frameworkElement.RequestedTheme = ElementTheme.Light;
                frameworkElement.RequestedTheme = ElementTheme.Dark;
            }
            else
            {
                frameworkElement.RequestedTheme = ElementTheme.Light;
                frameworkElement.RequestedTheme = ElementTheme.Dark;
                frameworkElement.RequestedTheme = ElementTheme.Default;
            }
        }
        catch (Exception e)
        {
            _ = App.ServiceProvider.GetRequiredService<IDialogService>().ShowExceptionAsync(e, Translations.DialogTitle_Error);
        }
    }
}
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <ResourceDictionary x:Key="HighlightLight">
    <!--https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.button?view=winrt-22000-->
    <SolidColorBrush x:Key="ButtonBackground" Color="#ff6900"/>
    <SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="#ff6900"/>
    <SolidColorBrush x:Key="ButtonBackgroundPressed" Color="Red"/>
    <SolidColorBrush x:Key="AppBarButtonBackground" Color="#ff6900"/>
    <SolidColorBrush x:Key="AppBarButtonBackgroundPointerOver" Color="#ff6900"/>
    <SolidColorBrush x:Key="AppBarButtonBackgroundPressed" Color="Red"/>
  </ResourceDictionary>
  <ResourceDictionary x:Key="HighlightDark">
    <!--https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.button?view=winrt-22000-->
    <SolidColorBrush x:Key="ButtonBackground" Color="#ff6900"/>
    <SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="#ff6900"/>
    <SolidColorBrush x:Key="ButtonBackgroundPressed" Color="Red"/>
    <SolidColorBrush x:Key="AppBarButtonBackground" Color="#ff6900"/>
    <SolidColorBrush x:Key="AppBarButtonBackgroundPointerOver" Color="#ff6900"/>
    <SolidColorBrush x:Key="AppBarButtonBackgroundPressed" Color="Red"/>
  </ResourceDictionary>
</ResourceDictionary>
      <StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" Spacing="5" Margin="0,0,5,0">
        <ToggleButton x:Name="ToggleButton1" IsChecked="{x:Bind ViewModel.Highlight, Mode=TwoWay}" />
        <Button attachedProperties:Control.Hightlight="{x:Bind ViewModel.Highlight, Mode=OneWay}" MinWidth="120"
                attachedProperties:Translation.Translatable="{x:Bind trans:Translations.Button_Acknowledge}" />
        <Button MinWidth="120" attachedProperties:Translation.Translatable="{x:Bind trans:Translations.Button_HornOff}" />
        <Button MinWidth="120"
                attachedProperties:Translation.Translatable="{x:Bind trans:Translations.Button_AlarmsHistory}" />
        <AppBarButton attachedProperties:Translation.Translatable="{x:Bind trans:Translations.Navigation_Alarms}" attachedProperties:Control.Hightlight="{x:Bind ViewModel.Highlight, Mode=OneWay}">
          <AppBarButton.Content>
            <svg:SvgAwesome Icon="Solid_TriangleExclamation" Height="20"/>
          </AppBarButton.Content>
        </AppBarButton>
      </StackPanel>

Upvotes: 1

Related Questions