设置与主题样式不同的本地隐式样式/替代基于动态资源 [英] Setting a local implicit style different from theme-style / alternative to BasedOn DynamicResource

查看:26
本文介绍了设置与主题样式不同的本地隐式样式/替代基于动态资源的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一个 wpf 应用程序,我可以在其中动态更改主题.我通过在应用程序资源级别换出 ResourceDictionaries 来做到这一点.主题资源字典具有为 TextBox 等定义的隐式样式.

imagine a wpf-application where I can dynamically change the theme. I do this by swapping out ResourceDictionaries at the Application-resource level. The theme-resourcedictionaries have implicit styles defined for TextBox and the like.

现在我在我的应用程序中有一个部分,其中文本框应该具有这种特定样式NonDefaultTextBoxStyle",而不是应用程序范围内的隐式样式.

Now I have a part in my application where textboxes should have this specific style "NonDefaultTextBoxStyle" and not the application wide implicit one.

我很乐意这样做(使用 DynamicResource 因为主题可以在运行时更改):

I would love to do this (using DynamicResource because the theme can be changed during runtime):

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="TextBox" BasedOn="{DynamicResource NonDefaultTextBoxStyle}"/>
    </StackPanel.Resources>
    <TextBox .../>
    <TextBox .../>
    <TextBox .../>
</StackPanel>

而不必这样做:

<StackPanel>
    <TextBox Style="{DynamicResource NonDefaultTextBoxStyle}" .../>
    <TextBox Style="{DynamicResource NonDefaultTextBoxStyle}" .../>
    <TextBox Style="{DynamicResource NonDefaultTextBoxStyle}" .../>
</StackPanel>

现在为了简化这一点,我想在 StackPanel 上设置一个可继承的附加属性,该属性将为每个后代文本框设置指定的样式.

Now to simplify this, I had this idea of setting an inheritable attached property on the StackPanel that would set a specified style on every descendent textbox.

这是个好主意吗?有没有更简单的方法?我错过了什么吗?

Is this a good idea? Are there simpler ways? Am I missing something?

这几乎可以归结为:在样式中,有什么可以替代 BasedOn="{DynamicResource ...}?

推荐答案

我遇到了完全相同的问题,但对于 ItemsContainerStyle...所以我几乎按照你说的做了,并编写了一个 AttachedProperty,它允许为 BasedOn 提供动态资源.

I had the exact same problem but for ItemsContainerStyle... so I did pretty much what you said and wrote an AttachedProperty that would allow a dynamic resource for the BasedOn.

Style BasedOn 的动态资源

以下是针对您的情况修改的解决方案:

Here is the solution modified for your situation:

public class DynamicStyle
{
    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(DynamicStyle), new UIPropertyMetadata(DynamicStyle.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(DynamicStyle), new UIPropertyMetadata(DynamicStyle.StylesChanged));

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

        var Element = (FrameworkElement)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.Style = 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;
    }
}

这是它的使用示例:

<!-- xmlns:ap points to the namespace where DynamicStyle class lives -->
<Button ap:DynamicStyle.BaseStyle="{DynamicResource {x:Type Button}}">
    <ap:DynamicStyle.DerivedStyle>
        <Style TargetType="Button">
            <Style.Triggers>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=FirstButtonWarning}" Value="True"/>
                        <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled}" Value="True"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.Setters>
                        <Setter Property="Background" Value="{DynamicResource WarningBackground}"/>
                        <Setter Property="Foreground" Value="{DynamicResource WarningForeground}"/>
                    </MultiDataTrigger.Setters>
                </MultiDataTrigger>
            </Style.Triggers>
        </Style>
    </ap:DynamicStyle.DerivedStyle>
    <TextBlock Text="Button that changes background and foreground when warning is active"/>
</Button>

这篇关于设置与主题样式不同的本地隐式样式/替代基于动态资源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆