Переплет для стилей WPF - PullRequest
34 голосов
/ 04 января 2009

Я пытаюсь создать пользовательский элемент управления - кнопку, к которой будет применено несколько стилей в зависимости от значения свойства в контексте данных.

Я думал о том, чтобы использовать что-то похожее на:

<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 ]

37 голосов
/ 04 января 2009

Если вы хотите заменить весь стиль (а не только его элементы), то вы, вероятно, будете хранить эти стили в ресурсах. Вы должны быть в состоянии сделать что-то вроде:

<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();
    }
}

Это не то, что я делаю очень часто, но это должно работать по памяти:)

15 голосов
/ 04 января 2009

Похоже, вам нужно использовать 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>
8 голосов
/ 27 мая 2011

Для тех из нас, кто не может использовать конвертер нескольких значений (я смотрю на вас SL4 и WP7 :), благодаря ответу Стивена я нашел способ использовать обычный конвертер значений.

Единственным предположением является то, что значение стиля содержится в свойстве устанавливаемого стиля.

Таким образом, если вы используете шаблон 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}">

С вашим классом StyleConverter, реализующим IValueConverter:

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, но он будет работать в SL4 и WP7, если вы используете статический ресурс и добавляете немного больше работы, поскольку метод TryFindResource не есть.

Надеюсь, это кому-нибудь поможет, и еще раз спасибо, Стивен!

1 голос
/ 13 июня 2013

ViewModel

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

View

Затем установите значение DataContext и затем внедрите следующий код в вашем представлении

    <Button Style="{Binding DynamicStyle,Mode=TwoWay}"/>
...