XAML组合样式

A.R*_*.R. 43 wpf xaml styles

我对此并不是很有希望,但有没有办法在XAML中组合多种样式来制作具有所有所需设置的新样式?

例如(伪代码);

<Style x:key="A">
 ...
</Style>

<Style x:key="B">
 ...
</Style>

<Style x:key="Combined">
 <IncludeStyle Name="A"/>
 <IncludeStyle Name="B"/>
 ... other properties.
</Style>
Run Code Online (Sandbox Code Playgroud)

我知道样式有一个'BasedOn'属性,但是这个功能只会带你到目前为止.我真的只是在寻找一种简单的方法(在XAML中)来创建这些"组合"样式.但就像我之前说过的那样,我怀疑它是否存在,除非有人听说过这样的事情?

Ala*_*ain 54

您可以创建一个标记扩展,将样式属性和触发器合并为一个样式.

从谷歌搜索结果来看,最受欢迎的是来自这个博客:http://bea.stollnitz.com/blog/?p = 384

这将允许您使用类似CSS的语法合并样式.

例如:

[MarkupExtensionReturnType(typeof(Style))]
public class MultiStyleExtension : MarkupExtension
{
    private string[] resourceKeys;

    /// <summary>
    /// Public constructor.
    /// </summary>
    /// <param name="inputResourceKeys">The constructor input should be a string consisting of one or more style names separated by spaces.</param>
    public MultiStyleExtension(string inputResourceKeys)
    {
        if (inputResourceKeys == null)
            throw new ArgumentNullException("inputResourceKeys");
        this.resourceKeys = inputResourceKeys.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
        if (this.resourceKeys.Length == 0)
            throw new ArgumentException("No input resource keys specified.");
    }

    /// <summary>
    /// Returns a style that merges all styles with the keys specified in the constructor.
    /// </summary>
    /// <param name="serviceProvider">The service provider for this markup extension.</param>
    /// <returns>A style that merges all styles with the keys specified in the constructor.</returns>
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        Style resultStyle = new Style();
        foreach (string currentResourceKey in resourceKeys)
        {
            object key = currentResourceKey;
            if (currentResourceKey == ".")
            {
                IProvideValueTarget service = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
                key = service.TargetObject.GetType();
            }
            Style currentStyle = new StaticResourceExtension(key).ProvideValue(serviceProvider) as Style;
            if (currentStyle == null)
                throw new InvalidOperationException("Could not find style with resource key " + currentResourceKey + ".");
            resultStyle.Merge(currentStyle);
        }
        return resultStyle;
    }
}

public static class MultiStyleMethods
{
    /// <summary>
    /// Merges the two styles passed as parameters. The first style will be modified to include any 
    /// information present in the second. If there are collisions, the second style takes priority.
    /// </summary>
    /// <param name="style1">First style to merge, which will be modified to include information from the second one.</param>
    /// <param name="style2">Second style to merge.</param>
    public static void Merge(this Style style1, Style style2)
    {
        if(style1 == null)
            throw new ArgumentNullException("style1");
        if(style2 == null)
            throw new ArgumentNullException("style2");
        if(style1.TargetType.IsAssignableFrom(style2.TargetType))
            style1.TargetType = style2.TargetType;
        if(style2.BasedOn != null)
            Merge(style1, style2.BasedOn);
        foreach(SetterBase currentSetter in style2.Setters)
            style1.Setters.Add(currentSetter);
        foreach(TriggerBase currentTrigger in style2.Triggers)
            style1.Triggers.Add(currentTrigger);
        // This code is only needed when using DynamicResources.
        foreach(object key in style2.Resources.Keys)
            style1.Resources[key] = style2.Resources[key];
    }
}
Run Code Online (Sandbox Code Playgroud)

您还可以将新样式定义为合并样式.

你的例子将通过以下方式解决:

<Style x:key="Combined" BasedOn="{local:MultiStyle A B}">
      ... other properties.
</Style>
Run Code Online (Sandbox Code Playgroud)

你需要做的就是添加这个类做你的命名空间,然后你就开始运行了:

<Window.Resources>
    <Style TargetType="Button" x:Key="ButtonStyle">
        <Setter Property="Width" Value="120" />
        <Setter Property="Height" Value="25" />
        <Setter Property="FontSize" Value="12" />
    </Style>
    <Style TargetType="Button" x:Key="GreenButtonStyle">
        <Setter Property="Foreground" Value="Green" />
    </Style>
    <Style TargetType="Button" x:Key="RedButtonStyle">
        <Setter Property="Foreground" Value="Red" />
    </Style>
    <Style TargetType="Button" x:Key="BoldButtonStyle">
        <Setter Property="FontWeight" Value="Bold" />
    </Style>
</Window.Resources>

<Button Style="{local:MultiStyle ButtonStyle GreenButtonStyle}" Content="Green Button" />
<Button Style="{local:MultiStyle ButtonStyle RedButtonStyle}" Content="Red Button" />
<Button Style="{local:MultiStyle ButtonStyle GreenButtonStyle BoldButtonStyle}" Content="green, bold button" />
<Button Style="{local:MultiStyle ButtonStyle RedButtonStyle BoldButtonStyle}" Content="red, bold button" />
Run Code Online (Sandbox Code Playgroud)

如果您使用上面的代码(稍微修改了博客上的原始代码),您可以另外使用当前的默认样式,可以使用"."合并类型.句法:

<Button Style="{local:MultiStyle . GreenButtonStyle BoldButtonStyle}"/>
Run Code Online (Sandbox Code Playgroud)

以上将合并默认样式MarkupExtension与两个补充样式.

  • 我成功地用它来组合2种风格; 但是,我遇到了一个小故障.VS 2010 WPF Designer在使用此方法时遇到问题.我可以组合样式并使用完全详细的MultiStyle,并且没有问题地构建/运行代码.但是WPF设计者抱怨在DataTemplate中使用这种方法.有没有人遇到/解决了这个问题? (4认同)
  • 在资源字典中为点表示法定义的样式中使用``BasedOn``时有一个错误.这是修复:``IProvideValueTarget service =(IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget)); if(service.TargetObject是Style){key =((Style)service.TargetObject).TargetType; } else {key = service.TargetObject.GetType(); }`` (4认同)
  • @JoeK我遇到了完全相同的问题,并在此发布了一个问题:http://stackoverflow.com/questions/8731547/markup-extension-staticresourceextension-requires-ixamlschemacontextprovider.到目前为止,我唯一的解决方案是在设计模式下禁用扩展,这不太理想. (2认同)
  • 我修改了你的代码,所以现在它可以接受最多10个Style对象作为参数,而不是使用样式键进行重构目的+在构造函数中进行合并:https://dotnetfiddle.net/r464VS (2认同)
  • 如果有人感兴趣,我将此 Epsiloner.Wpf.Extensions.MultiStyleExtension 放入我的 WPF 相关库中:https://github.com/Epsil0neR/Epsiloner.Wpf.Core 或 https://www.nuget.org/packages/Epsiloner。 Wpf.核心/ (2认同)