Связывание для стилей 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.
Это не совсем идеально, но пока я не смогу найти лучшее решение, похоже, это то, что мне нужно будет использовать.
Ответы
Ответ 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}"/>