簡體   English   中英

在 C# WPF 中使用依賴屬性時,如何正確使用數據觸發器和設置器?

[英]How do I properly utilize datatriggers and setters when using dependency properties in C# WPF?

我正在嘗試為 WPF HMI 應用程序創建通用狀態指示器顯示。 這些狀態指示器是用戶控件,其中兩個不同半徑的同心圓重疊。 我希望能夠根據我的StatusIndicator class 的一些依賴屬性來更改路徑標記上“填充”屬性的顏色。 在實踐中,可以使用任意數量的這些指標。 這些指標的“狀態”由 class object、 DigitalIOAssignment處理,它從給定 I/OZ2 組件的 Z9ED39E2EA931586B536A985A69EZ2 組件的 PLC 獲取其數據(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", 
                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();
        }
    }
}

在我的視圖的 xaml 中,我在設計器中創建每個狀態指示器並對其進行硬編碼x:Name並將其分配給StatusIndicatorName ,因為我無法弄清楚如何在運行時將此 Name 值傳遞給代碼隱藏(任何提示將不勝感激。:)。 我想做的是:

  1. 創建一個 StatusIndicator 用戶控件並為StatusIndicatorName屬性分配一個已知字符串
  2. UserControls:StatusIndicator.ComponentID屬性綁定到DigitalIOAssignment.componentID
  3. 我希望綁定到 List 會導致對該列表進行迭代並使用<DataTrigger> ,這將允許我在滿足觸發條件時引用相同的DigitalIOAssignment object,並設置適當的標志(isActive、isInterlocked 等)這樣。 我希望,這個偽代碼代表我在我的視圖中嘗試做的 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>

顯然,這個實現是行不通的。 我不能對數據觸發器上的值使用綁定(我可能必須對我期望的組件 ID 進行硬編碼,因為無論如何我都對狀態指示器名稱進行了硬編碼),而且我似乎無法對我的依賴項屬性使用 setter。 我收到一個錯誤:

Cannot find the static member 'isActivePropertyProperty' [sic.] on the type 'StatusIndicator'.

有人可以給我一些見解如何解決我想要實現的這個問題嗎? 即使我需要重新開始並以不同的方式處理它? 謝謝!

我不是 100% 肯定我遵循你所追求的。 您有任意數量的DigitalIOAssignment ,它們保存在 VM 的列表中,並且您想在視圖中為它們中的每一個創建一個StatusIndicator嗎?

執行此操作的常用方法是在視圖中使用ItemsControl ,並使用具有單個StatusIndicatorDataTemplate 如果您將ItemsControl.ItemsSource綁定到您的列表,wpf 將為列表中的每個項目應用模板,並且模板的DataContext將是該項目,因此您可以進行直接綁定而無需觸發器。

就像是:

<ItemsControl ItemsSource="{Binding DigitalInputAssignments}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>

      <UserControls:StatusIndicator Height="18" Width="18"
                                    Margin="106,144,0,0"
                                    HorizontalAlignment="Left" VerticalAlignment="Top"
                                    StatusIndicatorName="{Binding Name}"
                                    ComponentID="{Binding ComponentID}"
                                    IsActive="{Binding IsActive}"
                                    IsInterlocked="{Binding IsInterlocked}">
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM