none
Implement equivalent to BasedOn StaticResource, but for DynamicResources.

    Question

  • I am trying to provide "skinning" capabilities for my application. In a combo box that contains the available themes, I execute the following code:

     

            private void ApplySelectedThemeButton_Click(object sender, RoutedEventArgs e)

            {

                try

                {

                    ResourceDictionary theme = new ResourceDictionary();

                    theme.Source = this.ThemeList.SelectedValue as Uri;

                    Application.Current.Resources.MergedDictionaries.Clear();

                    Application.Current.Resources.MergedDictionaries.Add(theme);

                }

                catch (Exception exception)

                {

                    MessageBox.Show(String.Format("{0}\n{1}", "The theme could not be applied.", exception.Message), "Theme change failed");

                }

            }


     

    With this, all controls whose styles are based on a "DynamicResource" do change as expected. However, when I have a style that redefines a basic control, this does not work because the requirement for BasedOn is that the value is a StaticResource. So, if I have a style like this:

        <Style x:Key="SmallSquareButton" BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">

            <Setter Property="Margin" Value="1"/>

            <Setter Property="MinHeight" Value="24"/>

            <Setter Property="MinWidth" Value="24"/>

            <Setter Property="MaxHeight" Value="24"/>

            <Setter Property="MaxWidth" Value="24"/>

        </Style>


    ... then the controls that use that style will not change when the dictionaries get re-merged, because the style is based on a StaticResource that does not get re-evaluated after the merging of the new dictionary.

    I am running out of options other than removing all my "BasedOn" styles and manually updating all controls that use these types of styles. I am looking for ways to add a resource to the dynamic resources programatically, but I am kind of stuck there.

    Any hints or ideas would be greatly appreciated.

    Best Regards,

    JUAN M. MEDINA

     

    Tuesday, September 28, 2010 9:26 PM

Answers

  • Hi Yiling Lai!

    Neither of the following accomplishes what I seek when I remerge the dictionaries at the application, window or control level.

      <Style x:Key="SmallSquareButton" TargetType="{x:Type Button}">
        <Setter Property="Margin" Value="1"/>
        <Setter Property="MinHeight" Value="24"/>
        <Setter Property="MinWidth" Value="24"/>
        <Setter Property="MaxHeight" Value="24"/>
        <Setter Property="MaxWidth" Value="24"/>
      </Style>
    
    

    or 

      <Style x:Key="SmallSquareButton" BasedOn="{x:Null}" TargetType="{x:Type Button}">
        <Setter Property="Margin" Value="1"/>
        <Setter Property="MinHeight" Value="24"/>
        <Setter Property="MinWidth" Value="24"/>
        <Setter Property="MaxHeight" Value="24"/>
        <Setter Property="MaxWidth" Value="24"/>
      </Style>
    

    However, perusing through the "WPF Themes" xaml files I found out a workaround.

      <ControlTemplate x:Key="SmallSquareButtonTemplate" TargetType="{x:Type Button}">
        <Button Style="{DynamicResource {x:Type Button}}"
            Margin="1"
            MinHeight="24"
            MinWidth="24"
            MaxHeight="24"
            MaxWidth="24"
            Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}">
          <ContentPresenter />
        </Button>
      </ControlTemplate>
    
      <Style x:Key="SmallSquareButton" BasedOn="{x:Null}" TargetType="{x:Type Button}">
         <Setter Property="Template" Value="{DynamicResource SmallSquareButtonTemplate}" />
      </Style>
    
    

    This, of course, is redefining the Button control instead of basing it directly on the stock Button control. In this particular case, and for most of my needs it would do.

    Again, this does not address the fact that it would be great to have a way to base a control using the BasedOn and have it be a DynamicResource, but I think in most cases it does what it is supposed to do.

    Thanks for the hints, they were really helpful.

    Best Regards,

     

    JUAN M. MEDINA

     

    • Marked as answer by Juan M. Medina Wednesday, September 29, 2010 2:33 PM
    Wednesday, September 29, 2010 2:32 PM

All replies

  • Hello JUAN M. MEDIA

    This thread has the similar issue with you .

    http://social.msdn.microsoft.com/Forums/en/wpf/thread/44e80e86-9c62-4b77-87bc-00997a4796f4

    Reference Bob Bao: All application level resources are automatically frozen if they are freezable and SolidColorBrush is. So you could add the ResourceDictionary on the lower level like Window.Resources.

     

    Hope this helps.


    Yiling Lai. MVP (Visual C++ and Client App Dev)
    Wednesday, September 29, 2010 1:46 AM
  • Hi Yiling Lai!

    That was an interesting article, but that does not solve the issue. I actually start by doing something like:

     

        private void ThemeList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
          try
          {
            ResourceDictionary theme = new ResourceDictionary();
            theme.Source = this.ThemeList.SelectedValue as Uri;
            ThemedControl.Resources.MergedDictionaries.Clear();
            ThemedControl.Resources.MergedDictionaries.Add(theme);
          }
          catch (Exception exception)
          {
            MessageBox.Show(String.Format("{0}\n{1}", "The theme could not be applied.", exception.Message), "Theme change failed");
          }
        }
    
    

    As you see, this is at the lowest possible level, which is the control level. I tried too doing this at the window level. 

    The reason I was not expecting that solution to work is because the StaticResources, by definition, get resolved the first time they get used. Since my "BasedOn" is using a StaticResource, it should not be included in the "Merge" of the new dictionaries, and thus does not get "skinned".

    In any case, thanks for the pointer.

    Best Regards,

    JUAN M. MEDINA


    Wednesday, September 29, 2010 2:15 AM
  • I agree with you that, What's the reason that you wrote the sytle with BasedOn="{StaticResource {x:Type Button}}"? Generally, we can just removed this property and don't need to update all the control styel by yourself.


    Yiling Lai. MVP (Visual C++ and Client App Dev)
    Wednesday, September 29, 2010 2:27 AM
  • Hi Yiling Lai!

    Neither of the following accomplishes what I seek when I remerge the dictionaries at the application, window or control level.

      <Style x:Key="SmallSquareButton" TargetType="{x:Type Button}">
        <Setter Property="Margin" Value="1"/>
        <Setter Property="MinHeight" Value="24"/>
        <Setter Property="MinWidth" Value="24"/>
        <Setter Property="MaxHeight" Value="24"/>
        <Setter Property="MaxWidth" Value="24"/>
      </Style>
    
    

    or 

      <Style x:Key="SmallSquareButton" BasedOn="{x:Null}" TargetType="{x:Type Button}">
        <Setter Property="Margin" Value="1"/>
        <Setter Property="MinHeight" Value="24"/>
        <Setter Property="MinWidth" Value="24"/>
        <Setter Property="MaxHeight" Value="24"/>
        <Setter Property="MaxWidth" Value="24"/>
      </Style>
    

    However, perusing through the "WPF Themes" xaml files I found out a workaround.

      <ControlTemplate x:Key="SmallSquareButtonTemplate" TargetType="{x:Type Button}">
        <Button Style="{DynamicResource {x:Type Button}}"
            Margin="1"
            MinHeight="24"
            MinWidth="24"
            MaxHeight="24"
            MaxWidth="24"
            Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}">
          <ContentPresenter />
        </Button>
      </ControlTemplate>
    
      <Style x:Key="SmallSquareButton" BasedOn="{x:Null}" TargetType="{x:Type Button}">
         <Setter Property="Template" Value="{DynamicResource SmallSquareButtonTemplate}" />
      </Style>
    
    

    This, of course, is redefining the Button control instead of basing it directly on the stock Button control. In this particular case, and for most of my needs it would do.

    Again, this does not address the fact that it would be great to have a way to base a control using the BasedOn and have it be a DynamicResource, but I think in most cases it does what it is supposed to do.

    Thanks for the hints, they were really helpful.

    Best Regards,

     

    JUAN M. MEDINA

     

    • Marked as answer by Juan M. Medina Wednesday, September 29, 2010 2:33 PM
    Wednesday, September 29, 2010 2:32 PM