36

我正在尝试创建一个自定义控件 - 一个按钮 - 根据数据上下文中属性的值,它将应用多种样式。

我在想的是使用类似的东西:

<Button Style="{Binding Path=ButtonStyleProperty, Converter={StaticResource styleConverter}}" Text="{Binding Path=TextProp}" />

在代码中......实现一个 IValueConverter,它在方法中执行类似于以下代码的操作ConvertTo

switch(value as ValueEnums)
{
    case ValueEnums.Enum1:
        FindResource("Enum1ButtonStyle") as Style;
    break;

    ... and so on.
} 

但是,我不完全确定如何拉出样式对象,即使这是可能的......

同时我正在做的是处理DataContextChanged事件,然后将处理程序附加到PropertyChanged绑定到按钮的对象的事件 - 然后在那里运行 switch 语句。

它不是很完美,但在我找到更好的解决方案之前,这似乎是我必须使用的。

4

4 回答 4

40

如果您想替换整个样式(而不仅仅是其中的元素),那么您可能会将这些样式存储在资源中。您应该能够按照以下方式做一些事情:

<Button>
    <Button.Style>
        <MultiBinding Converter="{StaticResource StyleConverter}">
            <MultiBinding.Bindings>
                <Binding RelativeSource="{RelativeSource Self}"/>
                <Binding Path="MyStyleString"/>
            </MultiBinding.Bindings>
        </MultiBinding>
    </Button.Style>
</Button>

通过使用 MultiBinding 并使用 Self 作为第一个绑定,我们可以在转换器中查找资源。转换器需要实现 IMultiValueConverter(而不是 IValueConverter)并且看起来像这样:

class StyleConverter : IMultiValueConverter 
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        FrameworkElement targetElement = values[0] as FrameworkElement; 
        string styleName = values[1] as string;

        if (styleName == null)
            return null;

        Style newStyle = (Style)targetElement.TryFindResource(styleName);

        if (newStyle == null)
            newStyle = (Style)targetElement.TryFindResource("MyDefaultStyleName");

        return newStyle;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

这不是我经常做的事情,但这应该从记忆中起作用:)

于 2009-01-04T08:45:18.570 回答
18

看来您需要使用DataTrigger类。它允许您根据按钮的内容将不同的样式应用于按钮。

例如以下样式将根据数据上下文对象的属性值将按钮的背景属性更改为红色

<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path="Some property"}" 
                     Value="some property value">
            <Setter Property="Background" Value="Red"/>
        </DataTrigger>
    </Style.Triggers>
</Style>
于 2009-01-04T07:16:50.293 回答
8

对于我们这些不能使用多值转换器的人(我在看你 SL4 和 WP7 :),感谢 Steven 的回答,我找到了一种使用普通值转换器的方法。

唯一的假设是样式值包含在所设置样式的属性中。

因此,如果您使用 MVVM 模式,那么样式值(例如 TextSmall、TextMedium、TextLarge)被假定为视图模型的一部分,您所要做的就是传递定义样式名称的转换器参数。

例如,假设您的视图模型具有属性:

public string ProjectNameStyle
{
    get { return string.Format("ProjectNameStyle{0}", _displaySize.ToString()); }
}

申请风格:

<Application.Resources>
    <Style x:Key="ProjectNameStyleSmall" TargetType="TextBlock">
        <Setter Property="FontSize" Value="40" />
    </Style>
    <Style x:Key="ProjectNameStyleMedium" TargetType="TextBlock">
        <Setter Property="FontSize" Value="64" />
    </Style>
    <Style x:Key="ProjectNameStyleLarge" TargetType="TextBlock">
        <Setter Property="FontSize" Value="90" />
    </Style>

XAML 视图:

   <TextBlock 
        Text="{Binding Name}"
        Style="{Binding ., Mode=OneWay, Converter={cv:StyleConverter}, ConverterParameter=ProjectNameStyle}">

使用实现 IValueConverter 的 StyleConverter 类:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    if (targetType != typeof(Style))
    {
        throw new InvalidOperationException("The target must be a Style");
    }

    var styleProperty = parameter as string;
    if (value == null || styleProperty == null)
    {
        return null;
    }

    string styleValue = value.GetType()
        .GetProperty(styleProperty)
        .GetValue(value, null)
        .ToString();
    if (styleValue == null)
    {
        return null;
    }

    Style newStyle = (Style)Application.Current.TryFindResource(styleValue);
    return newStyle;
}

请注意,这是 WPF 代码,因为转换器是从 MarkupExtension 和 IValueConverter 派生的,但如果您使用静态资源并添加更多的腿部工作,因为 TryFindResource 方法不存在,它将在 SL4 和 WP7 中工作。

希望对某人有所帮助,再次感谢史蒂文!

于 2011-05-27T08:04:03.090 回答
2

视图模型

private Style _dynamicStyle = (Style)Application.Current.FindResource("Style1");
        public Style DynamicStyle
        {
            get { return _dynamicStyle; }
            set
            {
                _dynamicStyle = value;
                OnPropertyChanged("DynamicStyle");
            }

        }

public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

在您的 ViewModel 中实现一个属性,然后在您想要的任何位置动态更改样式,如下所示。

DynamicStyle=(Style)Application.Current.FindResource("Style2");// you can place this code where the action get fired

看法

然后设置DataContext值,然后在您的视图中实现以下代码

    <Button Style="{Binding DynamicStyle,Mode=TwoWay}"/>
于 2013-06-13T13:24:34.733 回答