在 C# WPF 中使用依赖属性时,如何正确利用数据触发器和设置器?

如何解决在 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 值在运行时到代码隐藏(任何提示将不胜感激!!)。我想做的是:

  1. 创建一个 StatusIndicator 用户控件并为 StatusIndicatorName 属性分配一个已知字符串
  2. UserControls:StatusIndicator.ComponentID 属性绑定到 DigitalIOAssignment.componentID
  3. 我希望绑定到 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,使用具有单个 DataTemplateStatusIndicator。如果您将 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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-