Style BasedOn的DynamicResource

Nts*_*alt 12 wpf styles

构建一个具有自定义"高对比度"主题的应用程序,供户外使用,可在运行时切换打开和关闭.通过合并和取消合并包含以下样式的资源字典,可以正常工作...

<Style x:Key="{x:Type MenuItem}" TargetType="{x:Type MenuItem}">
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    <Setter Property="Template" Value="{StaticResource Theme_MenuItemTemplate}"/>
</Style>
Run Code Online (Sandbox Code Playgroud)

当menuitem的使用未指定样式时,这非常有用.尽管在许多情况下这是不现实的,因为没有方法可以绑定没有样式的ItemsSource生成的子项.例如:

<ContextMenu.ItemContainerStyle>
    <Style TargetType="MenuItem">
        <Setter Property="Header" Value="{Binding Path=Name}"/>
        <Setter Property="IsCheckable" Value="True"/>
        <Setter Property="IsChecked" Value="{Binding Path=Checked}"/>
        <EventSetter Event="Checked" Handler="HistoryItem_Checked"/>
    </Style>
</ContextMenu.ItemContainerStyle>
Run Code Online (Sandbox Code Playgroud)

StackOverflow上的所有其他帖子都说你只需要这样做......

<Style TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}">
    <!-- Your overrides -->
</Style>
Run Code Online (Sandbox Code Playgroud)

但这对我的情况不起作用,因为我的BasedOn可以并且将在运行时更改(当然,您不能在BasedOn属性上使用DynamicResource扩展).在我的应用程序中执行此操作当前会导致控件在加载控件时覆盖其样式,而其他控件在没有重新加载的情况下正确切换.

所以我的问题......

有没有办法让DynamicResource扩展为BasedOn工作,还是有另外一种方法/ hack我可以实现它来实现它?

Nts*_*alt 7

最后想出了一个解决方案DynamicResouceStyle.BasedOn使用AttachedDependencyProperty.

这是修复ItemsControl.ItemContainerStyle(可以轻松修改以更改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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个例子......

<!-- 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>
Run Code Online (Sandbox Code Playgroud)

这是一个修改后的版本,FrameworkElement.Style在我的另一篇文章的答案中设置了一个: 设置一个不同于主题风格/替代基于LocalOn DynamicResource的本地隐式风格