Подтвердить что ты не робот

Связывание для стилей WPF

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

То, что я думал, использует нечто похожее на:

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

Это не совсем идеально, но пока я не смогу найти лучшее решение, похоже, это то, что мне нужно будет использовать.

4b9b3361

Ответ 1

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

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

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

Ответ 2

Кажется, вам нужно использовать класс 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>

Ответ 3

Для тех из нас, кто не может использовать конвертер с несколькими значениями (я смотрю на вас 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 не существуют.

Надеюсь, что кто-то помог, и снова спасибо Стивену!

Ответ 4

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

Просмотр

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

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