如何解决在 C# WPF 中使用依赖属性时,如何正确利用数据触发器和设置器?
我正在尝试为 WPF HMI 应用程序创建通用状态指示器显示。这些状态指示器是一个用户控件,其中两个不同半径的同心圆重叠。我希望能够根据我的 StatusIndicator
类的某些依赖属性更改路径标记上“填充”属性的颜色。在实践中,可以使用任意数量的这些指标。这些指示器的“状态”由类对象 DigitalIOAssignment
处理,该类对象从 PLC 获取有关给定 I/O 组件状态的数据(componentID、isActive、isInterlocked 等)。由于这些状态指示器的数量是任意的,我创建了一个 List <DigitalIOAssignment>
并将其传递给我的视图模型。这工作正常,我可以在我的视图模型中看到我想要正确绑定的数据。
状态指示器的编码如下:
XAML:
<UserControl x:Class="HMI.UserControls.StatusIndicator"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:HMI.UserControls"
xmlns:globals="clr-namespace:HMI.LogixPLCService.Globals;assembly=HMI.LogixPLCService"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="100">
<Viewbox x:Name="ControlViewbox" Stretch="Uniform" Height="auto" Width="auto">
<Canvas x:Name="ControlCanvas" Width="100" Height="100">
<!-- Draw Secondary Indicator Body First -->
<Path x:Name="StatusIndicator_Body" Width="100" Height="100"
Canvas.Left="0" Canvas.Top="0" StrokeThickness="1"
StrokeMiterLimit="2.75" Stroke="Black">
<Path.Data>
<EllipseGeometry Center="50,50" RadiusX="50" RadiusY="50"/>
</Path.Data>
<Path.Style>
<Style TargetType="Path">
<Setter Property="Fill" Value="LightGray"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:StatusIndicator}},Path=isInterlockedProperty}"
Value="True">
<Setter Property="Fill" Value="Yellow"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
<!-- Draw Foreground Indicator Body Second -->
<Path x:Name="StatusIndicator_Status" Width="100" Height="100"
Canvas.Left="0" Canvas.Top="0" StrokeThickness=".5"
StrokeMiterLimit="1" Stroke="Black">
<Path.Data>
<EllipseGeometry Center="50,50" RadiusX="30" RadiusY="30"/>
</Path.Data>
<Path.Style>
<Style TargetType="Path">
<Setter Property="Fill" Value="DarkGray"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:StatusIndicator}},Path=isActiveProperty}"
Value="True">
<Setter Property="Fill" Value="Lime"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</Canvas>
</Viewbox>
</UserControl>
背后的代码:
namespace HMI.UserControls
{
public partial class StatusIndicator : UserControl
{
/// <summary>
/// Interaction logic for StatusIndicator.xaml
///</summary>
public string StatusIndicatorName
{
get { return (string)GetValue(StatusIndicatorNameProperty); }
set { SetValue(StatusIndicatorNameProperty,value); }
}
public static readonly DependencyProperty StatusIndicatorNameProperty =
DependencyProperty.Register("StatusIndicatorName",typeof(string),typeof(StatusIndicator),new PropertyMetadata(null));
public string ComponentID
{
get { return (string)GetValue(ComponentIDProperty); }
set { SetValue(ComponentIDProperty,value); }
}
public static readonly DependencyProperty ComponentIDProperty =
DependencyProperty.Register("ComponentID",new PropertyMetadata(null));
public bool isActiveProperty
{
get { return (bool)GetValue(isActive); }
set { SetValue(isActive,value); }
}
public static readonly DependencyProperty isActive =
DependencyProperty.Register("isActiveProperty",typeof(bool),new PropertyMetadata(false));
public bool isInterlockedProperty
{
get { return (bool)GetValue(isInterlocked); }
set { SetValue(isInterlocked,value); }
}
public static readonly DependencyProperty isInterlocked =
DependencyProperty.Register("isInterlockedProperty",new PropertyMetadata(false));
public StatusIndicator()
{
InitializeComponent();
}
}
}
在我视图的 xaml 中,我在设计器中创建了每个状态指示器,并将 x:Name
硬编码给它并将其分配给 StatusIndicatorName
,因为我不知道如何传递这个 Name 值在运行时到代码隐藏(任何提示将不胜感激!!)。我想做的是:
- 创建一个 StatusIndicator 用户控件并为
StatusIndicatorName
属性分配一个已知字符串 -
UserControls:StatusIndicator.ComponentID
属性绑定到DigitalIOAssignment.componentID
- 我希望绑定到 List 会导致对这个列表进行迭代并使用
<DataTrigger>
,这将允许我在满足触发条件时引用相同的DigitalIOAssignment
对象,并设置以这种方式适当的标志(isActive,isInterlocked 等)。我希望这个伪代码代表了我在我的视图的 Xaml 中尝试做的事情:
<UserControls:StatusIndicator x:Name="DI_99VLV01"
StatusIndicatorName="{Binding ElementName=DI_99VLV01}"
Height="18" Width="18"
Margin="106,144,0"
HorizontalAlignment="Left" VerticalAlignment="Top"
ComponentID="{Binding privateDigitalInputAssignments/componentID}">
<DataTrigger Binding="{Binding Path=(UserControls:StatusIndicator.ComponentID)}"
Value="{Binding Path=(UserControls:StatusIndicator.StatusIndicatorName)}">
<Setter Property="UserControls:StatusIndicator.isActiveProperty"
Value="{Binding privateDigitalInputAssignments/isActive}"/>
<Setter Property="UserControls:StatusIndicator.isInterlockedProperty"
Value="{Binding privateDigitalInputAssignments/isInterlocked}"/>
</DataTrigger>
</UserControls:StatusIndicator>
显然,这个实现不起作用。我无法对数据触发器上的值使用绑定(我可能必须对我期望的组件 ID 进行硬编码,因为无论如何我都对状态指示器名称进行了硬编码),而且我似乎无法对我的依赖项属性使用 setter。我收到一个错误:
Cannot find the static member 'isActivePropertyProperty' [sic!] on the type 'StatusIndicator'.
有人可以让我了解如何解决这个问题以实现我想要实现的目标吗?即使我需要重新开始并以不同的方式处理它?谢谢!
解决方法
我不是 100% 确定我遵循您的要求。您有任意数量的 PATH
,它们保存在 VM 的列表中,并且您想在视图中为每个 DigitalIOAssignment
创建一个 StatusIndicator
?
执行此操作的常用方法是在视图中使用 ItemsControl
,使用具有单个 DataTemplate
的 StatusIndicator
。如果您将 ItemsControl.ItemsSource
绑定到您的列表,wpf 将为列表中的每个项目应用模板,模板的 DataContext
将是该项目,因此您可以进行直接绑定而无需触发器.
类似于:
<ItemsControl ItemsSource="{Binding DigitalInputAssignments}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<UserControls:StatusIndicator Height="18" Width="18"
Margin="106,144,0"
HorizontalAlignment="Left" VerticalAlignment="Top"
StatusIndicatorName="{Binding Name}"
ComponentID="{Binding ComponentID}"
IsActive="{Binding IsActive}"
IsInterlocked="{Binding IsInterlocked}">
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。