I am trying to create a generic status indicator display for a WPF HMI application. These status indicators are a user control wherein two concentric circles of different radius overlap. I want to be able to change the colour of the "fill" property on the path tag depending on some dependency properties of my StatusIndicator
class. In practice, there are an arbitrary number of these indicators that may be used. The 'state' of these indicators is handled by a class object, DigitalIOAssignment
, which gets its data (componentID, isActive, isInterlocked, etc.) from a PLC concerning the state of a given I/O component. Since the number of these status indicators is arbitrary, I create a List <DigitalIOAssignment>
and pass this to my viewmodel. This is working correctly and I can see the data I want to bind correctly in my viewmodel.
The status indicator is coded as follows:
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>
Code Behind:
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",
typeof(string), typeof(StatusIndicator), 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), typeof(StatusIndicator), new PropertyMetadata(false));
public bool isInterlockedProperty
{
get { return (bool)GetValue(isInterlocked); }
set { SetValue(isInterlocked, value); }
}
public static readonly DependencyProperty isInterlocked =
DependencyProperty.Register("isInterlockedProperty",
typeof(bool), typeof(StatusIndicator), new PropertyMetadata(false));
public StatusIndicator()
{
InitializeComponent();
}
}
}
In my view's xaml, I create each status indicator in the designer and hard-code a x:Name
to it and assign this to StatusIndicatorName
since I can't figure out how to pass this Name value at runtime to the code-behind (any hints would be appreciated!!). What I want to do is this:
- Create a StatusIndicator user control and assign the
StatusIndicatorName
property a known string UserControls:StatusIndicator.ComponentID
property is bound toDigitalIOAssignment.componentID
- It is my hope that binding to the List causes an iteration over this list and to engage a
<DataTrigger>
that will allow me to reference the sameDigitalIOAssignment
object when the trigger condition is met, and set the appropriate flags (isActive, isInterlocked etc) in this way. This pseudocode represents, I hope, what I am trying to do in my view's Xaml:
<UserControls:StatusIndicator x:Name="DI_99VLV01"
StatusIndicatorName="{Binding ElementName=DI_99VLV01}"
Height="18" Width="18"
Margin="106,144,0,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>
Obviously, this implementation does not work. I cannot use a binding for a value on a data trigger (I may have to hard-code the component ID I am expecting since I hard-code the status indicator name anyway), and I cannot seem to use setters for my dependency properties. I get an error:
Cannot find the static member 'isActivePropertyProperty' [sic!] on the type 'StatusIndicator'.
Can someone please give me some insight how to approach this problem for what I am trying to achieve? Even if I need to start over and approach it a different way? Thank you!