如何在用户控件中动态设置元素的样式
How to dynamically set style of an element in a User Control
在 WPF 项目中,我有一个定义多边形形状的用户控件 (Valve.xaml)。
<Grid>
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Style="{StaticResource Valve_Open}"/>
</Grid>
我在 window xaml (FFG.xaml) 文件中显示 Valve 用户控件,如下所示:
<Window
<!-- removed other namespaces for brevity -->
xmlns:cl="clr-namespace:FFG.Controls;assembly=PID.Controls">
<Grid>
<cl:Valve x:Name="valve201A"></cl:Valve>
</Grid>
</Window>
我正在将 FFG.xaml 的 DataContext 设置为 class FFG_ViewModel.cs,它包含 Valve_Model class 的一个实例。 Valve_Model 本质上代表在 FFG.xaml.
中绘制在 window 上的阀门
public class FFG_ViewModel : ViewModelBase {
public Valve_Model Valve201A { get; set; }
// There are other properties and methods, but I've removed them for brevity also
}
这里是 Valve_Model class:
public class Valve_Model INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private bool _isValveOpen { get; set; }
public bool IsValveOpen {
get {
return _isValveOpen;
}
set {
_isValveOpen = value;
OnPropertyChanged("IsValveOpen");
}
}
#region INotifyPropertyChanged
protected virtual void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null) {
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion
}
问题:
我想要的是 Valve.xaml 中的样式 属性 在 IsValveOpen 属性 更改时更改。
因此,如果阀门打开,则它将是:
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Style="{StaticResource Valve_Open}"/>
并且当 属性 更改为 false 时,我需要将多边形的样式更改为:
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Style="{StaticResource Valve_Closed}"/>
我该如何准确地实施它?
您可以(据我所知)在 ControlTemplate
中使用 DataTrigger
。假设这两个是你的风格:
<Window.Resources>
<Style TargetType="Polygon" x:Key="Valve_Open">
<Setter Property="Fill" Value="Red"/>
</Style>
<Style TargetType="Polygon" x:Key="Valve_Close">
<Setter Property="Fill" Value="Green"/>
</Style>
</Window.Resources>
您应该将此样式添加到资源中:
<Style x:Key="changeStyle" TargetType="Control">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Control">
<Grid>
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Style="{StaticResource Valve_Open}" />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Valve201A.IsValveOpen}" Value="true">
<Setter TargetName="pValve" Property="Style" Value="{StaticResource Valve_Close}" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
并在您的观点中使用它们:
<Control DataContext="{Binding}" Style="{StaticResource changeStyle}" />
你可以使用 IMultiValueConverter
.
首先,让我们简化用例。基本上你想根据给定的状态对象交换 Styles
,我将用 ToggleButton
表示。事实上,您将所有内容都包装在 UserControl
中也不会影响基本概念。
演示:
- 开始新项目
- 声明我们的
Resources
- 将
Window
和状态提供给 Converter
。
MainWindow.xaml
<Window
...
>
<Window.Resources>
<local:ToStyleConverter x:Key="ToStyleConverter"/>
<Style x:Key="Valve_Open" TargetType="{x:Type Polygon}">
<Setter Property="Fill" Value="Red"/>
</Style>
<Style x:Key="Valve_Closed" TargetType="{x:Type Polygon}">
<Setter Property="Fill" Value="Green"/>
</Style>
</Window.Resources>
<DockPanel>
<ToggleButton x:Name="butt" DockPanel.Dock="Bottom">Switch</ToggleButton>
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Stretch="Uniform">
<Polygon.Style>
<MultiBinding Converter="{StaticResource ToStyleConverter}">
<Binding RelativeSource="{RelativeSource FindAncestor,
AncestorType={x:Type Window}}"/>
<Binding ElementName="butt" Path="IsChecked"/>
</MultiBinding>
</Polygon.Style>
</Polygon>
</DockPanel>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class ToStyleConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if (values[0] is Window)
{
Window win = (Window)values[0];
if ((bool)values[1])
return win.FindResource("Valve_Open");
if (!(bool)values[1])
return win.FindResource("Valve_Closed");
}
return DependencyProperty.UnsetValue;
}
public object[] ConvertBack(object values, Type[] targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
更改为任何特定用例意味着:
- 将相对源绑定指向包含
Resources
(样式) 的 Control
- 使用第二个绑定将状态添加到
Converter
(DP/INPC)
- 实现
Converter
逻辑
而不是将实际的 Style
属性 设置为新值,您可以将 DataTrigger
添加到样式本身,以更改基于 Polygon
的属性关于 IsValveOpen
源 属性.
的值
Valve.xaml:
<Grid>
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20">
<Polygon.Style>
<Style TargetType="Polygon">
<!-- Copy the setters from the Valve_Closed style here -->
<Setter Property="Fill" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsValveOpen}" Value="True">
<!-- Copy the setters from the Valve_Open style here -->
<Setter Property="Fill" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Polygon.Style>
</Polygon>
</Grid>
FFG.xaml:
<Grid>
<cl:Valve x:Name="valve201A" DataContext="{Binding Valve201A}" />
</Grid>
在 WPF 项目中,我有一个定义多边形形状的用户控件 (Valve.xaml)。
<Grid>
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Style="{StaticResource Valve_Open}"/>
</Grid>
我在 window xaml (FFG.xaml) 文件中显示 Valve 用户控件,如下所示:
<Window
<!-- removed other namespaces for brevity -->
xmlns:cl="clr-namespace:FFG.Controls;assembly=PID.Controls">
<Grid>
<cl:Valve x:Name="valve201A"></cl:Valve>
</Grid>
</Window>
我正在将 FFG.xaml 的 DataContext 设置为 class FFG_ViewModel.cs,它包含 Valve_Model class 的一个实例。 Valve_Model 本质上代表在 FFG.xaml.
中绘制在 window 上的阀门public class FFG_ViewModel : ViewModelBase {
public Valve_Model Valve201A { get; set; }
// There are other properties and methods, but I've removed them for brevity also
}
这里是 Valve_Model class:
public class Valve_Model INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private bool _isValveOpen { get; set; }
public bool IsValveOpen {
get {
return _isValveOpen;
}
set {
_isValveOpen = value;
OnPropertyChanged("IsValveOpen");
}
}
#region INotifyPropertyChanged
protected virtual void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null) {
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion
}
问题: 我想要的是 Valve.xaml 中的样式 属性 在 IsValveOpen 属性 更改时更改。
因此,如果阀门打开,则它将是:
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Style="{StaticResource Valve_Open}"/>
并且当 属性 更改为 false 时,我需要将多边形的样式更改为:
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Style="{StaticResource Valve_Closed}"/>
我该如何准确地实施它?
您可以(据我所知)在 ControlTemplate
中使用 DataTrigger
。假设这两个是你的风格:
<Window.Resources>
<Style TargetType="Polygon" x:Key="Valve_Open">
<Setter Property="Fill" Value="Red"/>
</Style>
<Style TargetType="Polygon" x:Key="Valve_Close">
<Setter Property="Fill" Value="Green"/>
</Style>
</Window.Resources>
您应该将此样式添加到资源中:
<Style x:Key="changeStyle" TargetType="Control">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Control">
<Grid>
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Style="{StaticResource Valve_Open}" />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Valve201A.IsValveOpen}" Value="true">
<Setter TargetName="pValve" Property="Style" Value="{StaticResource Valve_Close}" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
并在您的观点中使用它们:
<Control DataContext="{Binding}" Style="{StaticResource changeStyle}" />
你可以使用 IMultiValueConverter
.
首先,让我们简化用例。基本上你想根据给定的状态对象交换 Styles
,我将用 ToggleButton
表示。事实上,您将所有内容都包装在 UserControl
中也不会影响基本概念。
演示:
- 开始新项目
- 声明我们的
Resources
- 将
Window
和状态提供给Converter
。
MainWindow.xaml
<Window
...
>
<Window.Resources>
<local:ToStyleConverter x:Key="ToStyleConverter"/>
<Style x:Key="Valve_Open" TargetType="{x:Type Polygon}">
<Setter Property="Fill" Value="Red"/>
</Style>
<Style x:Key="Valve_Closed" TargetType="{x:Type Polygon}">
<Setter Property="Fill" Value="Green"/>
</Style>
</Window.Resources>
<DockPanel>
<ToggleButton x:Name="butt" DockPanel.Dock="Bottom">Switch</ToggleButton>
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Stretch="Uniform">
<Polygon.Style>
<MultiBinding Converter="{StaticResource ToStyleConverter}">
<Binding RelativeSource="{RelativeSource FindAncestor,
AncestorType={x:Type Window}}"/>
<Binding ElementName="butt" Path="IsChecked"/>
</MultiBinding>
</Polygon.Style>
</Polygon>
</DockPanel>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class ToStyleConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if (values[0] is Window)
{
Window win = (Window)values[0];
if ((bool)values[1])
return win.FindResource("Valve_Open");
if (!(bool)values[1])
return win.FindResource("Valve_Closed");
}
return DependencyProperty.UnsetValue;
}
public object[] ConvertBack(object values, Type[] targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
更改为任何特定用例意味着:
- 将相对源绑定指向包含
Resources
(样式) 的 - 使用第二个绑定将状态添加到
Converter
(DP/INPC) - 实现
Converter
逻辑
Control
而不是将实际的 Style
属性 设置为新值,您可以将 DataTrigger
添加到样式本身,以更改基于 Polygon
的属性关于 IsValveOpen
源 属性.
Valve.xaml:
<Grid>
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20">
<Polygon.Style>
<Style TargetType="Polygon">
<!-- Copy the setters from the Valve_Closed style here -->
<Setter Property="Fill" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsValveOpen}" Value="True">
<!-- Copy the setters from the Valve_Open style here -->
<Setter Property="Fill" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Polygon.Style>
</Polygon>
</Grid>
FFG.xaml:
<Grid>
<cl:Valve x:Name="valve201A" DataContext="{Binding Valve201A}" />
</Grid>