DynamicResource for Style BasedOn

Your Styles should be in a UIElement.Resources tag. This can be dynamically cleared and repopulated.

I do something similar but not as complex like this:

MobileApp.Get().Resources.MergedDictionaries.Clear();

Uri uri = new Uri("/Resources/DayModeButton.xaml", UriKind.Relative);
ResourceDictionary resDict = Application.LoadComponent(uri) as ResourceDictionary;

resDict["SelectedColor"] = selectedColor; //change an attribute of resdict

MobileApp.Get().Resources.MergedDictionaries.Add(resDict);

Finally figured out a solution for a DynamicResouce for Style.BasedOn using an AttachedDependencyProperty.

Here is the fix for ItemsControl.ItemContainerStyle (can be easily modified to change FrameworkElement.Style)

public class DynamicContainerStyle
{
    public static Style GetBaseStyle(DependencyObject obj)
    {
        return (Style)obj.GetValue(BaseStyleProperty);
    }

    public static void SetBaseStyle(DependencyObject obj, Style value)
    {
        obj.SetValue(BaseStyleProperty, value);
    }

    // Using a DependencyProperty as the backing store for BaseStyle.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty BaseStyleProperty =
        DependencyProperty.RegisterAttached("BaseStyle", typeof(Style), typeof(DynamicContainerStyle), new UIPropertyMetadata(DynamicContainerStyle.StylesChanged));

    public static Style GetDerivedStyle(DependencyObject obj)
    {
        return (Style)obj.GetValue(DerivedStyleProperty);
    }

    public static void SetDerivedStyle(DependencyObject obj, Style value)
    {
        obj.SetValue(DerivedStyleProperty, value);
    }

    // Using a DependencyProperty as the backing store for DerivedStyle.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DerivedStyleProperty =
        DependencyProperty.RegisterAttached("DerivedStyle", typeof(Style), typeof(DynamicContainerStyle), new UIPropertyMetadata(DynamicContainerStyle.StylesChanged));

    private static void StylesChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        if (!typeof(System.Windows.Controls.ItemsControl).IsAssignableFrom(target.GetType()))
            throw new InvalidCastException("Target must be ItemsControl");

        var Element = (System.Windows.Controls.ItemsControl)target;

        var Styles = new List<Style>();

        var BaseStyle = GetBaseStyle(target);

        if (BaseStyle != null)
            Styles.Add(BaseStyle);

        var DerivedStyle = GetDerivedStyle(target);

        if (DerivedStyle != null)
            Styles.Add(DerivedStyle);

        Element.ItemContainerStyle = MergeStyles(Styles);
    }

    private static Style MergeStyles(ICollection<Style> Styles)
    {
        var NewStyle = new Style();

        foreach (var Style in Styles)
        {
            foreach (var Setter in Style.Setters)
                NewStyle.Setters.Add(Setter);

            foreach (var Trigger in Style.Triggers)
                NewStyle.Triggers.Add(Trigger);
        }

        return NewStyle;
    }
}

And here is an example...

<!-- xmlns:ap points to the namespace where DynamicContainerStyle class lives -->
<MenuItem Header="Recent" 
    ItemsSource="{Binding Path=RecentFiles}"
    IsEnabled="{Binding RelativeSource={RelativeSource Self}, Path=HasItems}"
    ap:DynamicContainerStyle.BaseStyle="{DynamicResource {x:Type MenuItem}}">
    <ap:DynamicContainerStyle.DerivedStyle>
        <Style TargetType="MenuItem">
            <EventSetter Event="Click"  Handler="RecentFile_Clicked"/>
        </Style>
    </ap:DynamicContainerStyle.DerivedStyle>
    <MenuItem.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding}"/>
        </DataTemplate>
    </MenuItem.ItemTemplate>
</MenuItem>

Here is a modified version that sets the FrameworkElement.Style instead in my answer to another post: Setting a local implicit style different from theme-style / alternative to BasedOn DynamicResource


I have gotten wiser as time has passed and I would like to offer another option to handle different appearances, if the change is mostly colors (with of borders ext. should work too).

Create the colors you want as resources, and when you make the styles for your controls, use the colors as DynamicResources, NOT StaticResource, and this is the whole trick.

When you want to change the 'theme', simply load/override the color resources. Because of the dynamic bindings, the styles will automatically update/use the nearest definition of the resource.

Similarly, if there is an area where you want things to look different, simply set/override the color resources by setting new values in the Resources of a parent control.

I hope this is useable for others who come along =0)


I have a slight improvement for NtscCobalts answer:

    #region Type-specific function (FrameworkElement)
    private static void StylesChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        var mergedStyles = GetMergedStyles<FrameworkElement>(target, GetBaseStyle(target), GetDerivedStyle(target)); // NOTE: change type on copy

        var element = (FrameworkElement)target; // NOTE: change type on copy

        element.Style = mergedStyles;
    }
    #endregion Type-specific function (FrameworkElement)


    #region Reused-function 
    public static Style GetMergedStyles<T>(DependencyObject target, Style baseStyle, Style derivedStyle) where T : DependencyObject
    {
        if (!(target is T)) throw new InvalidCastException("Target must be " + typeof(T));

        if (derivedStyle == null) return baseStyle;
        if (baseStyle == null) return derivedStyle;

        var newStyle = new Style { BasedOn = baseStyle, TargetType = derivedStyle.TargetType };
        foreach (var setter in derivedStyle.Setters) newStyle.Setters.Add(setter);
        foreach (var trigger in derivedStyle.Triggers) newStyle.Triggers.Add(trigger);
        return newStyle;

    }
    #endregion Reused-function

You see, it is possible to simply set the base style, when creating a new style. This way the base styles from the base style are automatically in the hierarchy.

Tags:

Wpf

Styles