WPF 自定义控件的使用和访问

WPF Custom Control Usage and Access

提问人:Michael Riley 提问时间:10/11/2023 更新时间:10/14/2023 访问量:107

问:

大家下午好。我意识到这个主题可能在其他地方有涉及。

我在 C# 方面非常有经验,但大部分经验是使用控制台应用程序、Win Forms 而不是 WPF for GUI 和 MVVM。我正在编写一个程序来检测 USB-RS422 设备,然后为每个发现的设备提供用户配置选项。系统将有 1->8 个这样的设备,最初是 1->4,所以我为每个设备手动创建了布局。现在这个数字已经增加,我尝试创建一个自定义的 UserControl,但我正在为一些概念而苦苦挣扎。

下面是我的自定义控件的代码:rs422DeviceControlGroup.xaml

<UserControl
x:Class="EngineeringLabUi.rs422DeviceControlGroup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:internal="clr-namespace:rs422DeviceControlGroup"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
    <GroupBox
        Grid.Row="1"
        Grid.Column="0"
        Header="Test"
        Style="{StaticResource GroupBoxStyle}">
        <StackPanel x:Name="rs422DevStackPnl" Orientation="Vertical">
            <Border BorderThickness="1">
                <StackPanel Orientation="Horizontal">
                    <CheckBox x:Name="rs422DevEditCkBx" Content="Edit" />
                    <CheckBox x:Name="rs422DevUseCkBx" Content="Use" />
                    <TextBlock Text="Read Type" />
                    <ComboBox x:Name="rs422DevReadTypeComboBox" SelectedIndex="0" />
                    <TextBlock Text="Write Type" />
                    <ComboBox x:Name="rs422DevWriteTypeComboBox" SelectedIndex="0" />
                </StackPanel>
            </Border>
            <Border BorderThickness="1">
                <StackPanel Orientation="Horizontal">
                    <Border BorderThickness="1">
                        <StackPanel Orientation="Vertical">
                            <TextBlock Text="Baud" />
                            <ComboBox x:Name="rs422DevBoadRatesComboBox" SelectedIndex="0" />
                        </StackPanel>
                    </Border>
                    <Border BorderThickness="1">
                        <StackPanel Orientation="Vertical">
                            <TextBlock Text="Parity" />
                            <ComboBox x:Name="rs422DevParityComboBox" SelectedIndex="0" />
                        </StackPanel>
                    </Border>
                    <Border BorderThickness="1">
                        <StackPanel Orientation="Vertical">
                            <TextBlock Text="Data Bits" />
                            <ComboBox x:Name="rs422DevDataBitsComboBox" SelectedIndex="0" />
                        </StackPanel>
                    </Border>
                    <Border BorderThickness="1">
                        <StackPanel Orientation="Vertical">
                            <TextBlock Text="Stop Bits" />
                            <!--  stop bits value of None is not currently supported  -->
                            <ComboBox x:Name="rs422DevStopBitsComboBox" SelectedIndex="1" />
                        </StackPanel>
                    </Border>
                    <Border BorderThickness="1">
                        <StackPanel Orientation="Vertical">
                            <TextBlock Text="FCC Position" />
                            <ComboBox x:Name="rs422DevFccPositionComboBox" SelectedIndex="0" />
                        </StackPanel>
                    </Border>
                    <Border BorderThickness="1">
                        <StackPanel Orientation="Vertical">
                            <TextBlock Text="Status" />
                            <Ellipse x:Name="rs422DevStatusEllipse" />
                            <Button x:Name="rs422DevCheckBtn" Content="Check" />
                        </StackPanel>
                    </Border>
                </StackPanel>
            </Border>
        </StackPanel>
    </GroupBox>
</Grid>

以下是它在设计视图中的呈现方式enter image description here

  1. 在定义这样的自定义控件时,我是否应该为控件中的对象设置名称,如下所示?<Button x:Name="rs422DevCheckBtn" Content="Check" />

  2. 当我的应用程序检测到设备时,我想将其中一个控件添加到 TabItem,但我不确定如何深入了解每个复选框、组合框、椭圆和按钮。

我还在 App.xaml 文件中创建了样式,如下所示:

<Application
x:Class="EngineeringLabUi.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:EngineeringLabUi"
StartupUri="MainWindow.xaml">

<Application.Resources>
    <!--#region buttonStyle-->
    <Style TargetType="Button">
        <Setter Property="Padding" Value="2" />
        <Setter Property="Margin" Value="1,1,1,1" />
        <Setter Property="Height" Value="auto" />
        <Setter Property="Width" Value="auto" />
        <Setter Property="IsEnabled" Value="False" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="VerticalAlignment" Value="Center" />
    </Style>
    <!--#endregion buttonStyle-->
    <!--#region borderStyle-->
    <Style TargetType="Border">
        <Setter Property="Padding" Value="2" />
        <Setter Property="Margin" Value="2" />
        <Setter Property="BorderBrush" Value="Black" />
        <Setter Property="BorderThickness" Value="2" />
        <Setter Property="CornerRadius" Value="6" />
        <Setter Property="Opacity" Value="100" />
    </Style>
    <!--#endregion borderStyle-->
    <!--#region comboBoxStyle-->
    <Style TargetType="ComboBox">
        <Setter Property="Padding" Value="5" />
        <Setter Property="Margin" Value="2" />
        <Setter Property="Height" Value="auto" />
        <Setter Property="Width" Value="auto" />
        <!--< debug />-->
        <!--<Setter Property="IsEnabled" Value="False" />-->
        <Setter Property="IsEnabled" Value="True" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="VerticalAlignment" Value="Center" />
    </Style>
    <!--#endregion comboBoxStyle-->
    <!--#region labelStyle-->
    <Style TargetType="Label">
        <Setter Property="Padding" Value="1" />
        <Setter Property="Margin" Value="1,1,1,1" />
        <Setter Property="Height" Value="auto" />
        <Setter Property="Width" Value="auto" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="FontWeight" Value="Bold" />
    </Style>
    <!--#endregion labelStyle-->
    <!--#region textBlockStyle-->
    <Style TargetType="TextBlock">
        <Setter Property="Padding" Value="1" />
        <Setter Property="Margin" Value="3,3,3,3" />
        <Setter Property="Height" Value="auto" />
        <Setter Property="Width" Value="auto" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="TextDecorations" Value="Underline" />
        <!--<Setter Property="bo" Value="Underline" />-->
    </Style>
    <!--#endregion textBlockStyle-->
    <!--#region groupBoxStyle-->
    <Style x:Key="GroupBoxStyle" TargetType="{x:Type GroupBox}">
        <Setter Property="BorderBrush" Value="#D5DFE5" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type GroupBox}">
                    <Grid SnapsToDevicePixels="true">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="auto" />
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="auto" />
                            <ColumnDefinition Width="auto" />
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="auto" />
                            <RowDefinition Height="auto" />
                        </Grid.RowDefinitions>
                        <Border
                            x:Name="Header"
                            Grid.Row="0"
                            Grid.Column="1"
                            Padding="3,1,3,0">
                            <ContentPresenter
                                ContentSource="Header"
                                RecognizesAccessKey="True"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                        </Border>
                        <ContentPresenter
                            Grid.Row="2"
                            Grid.Column="1"
                            Grid.ColumnSpan="2"
                            Margin="{TemplateBinding Padding}"
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <!--#endregion groupBoxStyle-->
    <!--#region checkBoxStyle-->
    <Style TargetType="CheckBox">
        <Setter Property="Padding" Value="0" />
        <Setter Property="Margin" Value="10,1,1,1" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <!--< debug />-->
        <!--<Setter Property="IsEnabled" Value="False" />-->
        <Setter Property="IsEnabled" Value="True" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
    </Style>
    <!--#endregion checkBoxStyle-->
    <!--#region ellipseStyle-->
    <Style TargetType="Ellipse">
        <Setter Property="Height" Value="15" />
        <Setter Property="Margin" Value="1,1,1,1" />
        <Setter Property="StrokeThickness" Value="3" />
        <Setter Property="Width" Value="15" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="IsEnabled" Value="False" />
        <Setter Property="Fill" Value="Gray" />
        <Setter Property="Stroke" Value="Black" />
    </Style>
    <!--#endregion ellipseStyle-->
    <!--#region tabControltyle-->
    <Style TargetType="TabControl">
        <Setter Property="Padding" Value="0" />
        <Setter Property="Margin" Value="1,1,1,1" />
        <Setter Property="FontSize" Value="10" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="VerticalAlignment" Value="Stretch" />
    </Style>
    <!--#endregion tabControltyle-->
    <!--#region tabItemStyle-->
    <Style TargetType="TabItem">
        <Setter Property="Padding" Value="3.5" />
        <Setter Property="Margin" Value="-2,0,-2,0" />
        <Setter Property="VerticalAlignment" Value="Top" />
        <Setter Property="Height" Value="22" />
    </Style>
    <!--#endregion tabItemStyle-->
    <!--#region menuItemStyle-->
    <Style TargetType="MenuItem">
        <!--< debug />-->
        <!--<Setter Property="Foreground" Value="#FF020202" />-->
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="Height" Value="22" />
    </Style>
    <!--#endregion menuItemStyle-->
    <!--#region flowDocumentStyle-->
    <Style TargetType="FlowDocument">
        <Setter Property="ColumnRuleBrush" Value="Black" />
        <Setter Property="ColumnRuleWidth" Value="1" />
        <Setter Property="IsColumnWidthFlexible" Value="True" />
        <Setter Property="MaxPageWidth" Value="1000" />
        <Setter Property="PageWidth" Value="auto" />
        <Setter Property="MaxPageHeight" Value="700" />
    </Style>
    <!--#endregion flowDocumentStyle-->
    <!--#region gridStyle-->
    <Style TargetType="Grid">
        <Setter Property="MaxHeight" Value="700" />
        <Setter Property="MaxWidth" Value="1400" />
        <Setter Property="Height" Value="auto" />
        <Setter Property="Width" Value="auto" />
        <Setter Property="Background" Value="#FFE5E5E5" />
    </Style>
    <!--#endregion gridStyle-->
    <!--#region richTextBoxStyle-->
    <Style TargetType="RichTextBox">
        <Setter Property="MinHeight" Value="500" />
        <Setter Property="MaxHeight" Value="700" />
        <Setter Property="MinWidth" Value="500" />
        <Setter Property="MaxWidth" Value="1000" />
        <Setter Property="HorizontalScrollBarVisibility" Value="auto" />
        <Setter Property="VerticalScrollBarVisibility" Value="auto" />
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="VerticalContentAlignment" Value="Stretch" />
    </Style>
    <!--#endregion richTextBoxStyle-->
</Application.Resources>

在我的MainWindow.xaml中,我能够使用如下所示的控件,但就像我说的,我不确定如何在自定义控件中设置对象的属性。此外,这是我第一次制作自定义控件,所以我也想知道我是否做得不正确,或者我是否应该做一些不同的事情。我的应用程序目前不是 MVVM,但我确实打算在不久的将来迁移到该架构。

    <!-- this is just a test control to organize the layout I intend to use elsewhere-->
<TabControl Margin="254,127,53,273">
    <TabItem Header="COM 4">
        <Grid Background="#FFE5E5E5">
            <local:rs422DeviceControlGroup x:Name="SomeRs422Device" >
                <!-- I'm not sure what I should do here to access the parts of the control-->
            </local:rs422DeviceControlGroup>
        </Grid>
    </TabItem>
    <TabItem Header="COM 5">
        <local:rs422DeviceControlGroup />
    </TabItem>
</TabControl>

我想一种方法是只有一个 TabControl 和 TabItems,足以容纳 8 个设备,然后在后面的代码中在运行时在那里进行实例化。

我正在寻找一些建议,因为在应用程序中的这一点之后进行更改将需要分配大量返工,我想在继续前进之前确保我的方法具体。MVVM 对我来说有点陌生,WPF 也是如此。如果这篇文章重复了其他帖子内容,我深表歉意。我已经研究了大约一个星期,需要继续前进。

提前致谢!

C# WPF 自定义控件

评论

0赞 Tarazed 10/11/2023
至于其他讨论,除了我回答的问题之外,你是否采取了一种伟大的方法,是更适合 Stack 的代码审查 codereview.stackexchange.com 将来,请尽量保持问题非常具体。每个帖子一个问题,这样可以更轻松地获得所需的答案。当你把问题结合起来时,你会阻止那些可能只知道如何回答你的一个问题的人,这对你没有帮助。

答:

1赞 Tarazed 10/11/2023 #1

对于您的第一个问题,仅当您需要引用名称时才需要它们。这可以从绑定或代码隐藏等内容中引用。

您的第二个问题已加载。有很多方法可以解决这个问题,没有意见就很难给出答案。我认为,最常见的是称为 MVVM(模型-视图-视图模型)的设计模式,在这种模式中,您可以通过绑定将控件松散地耦合到属性。MVVM 上有很多资源,这不是我在这里真正可以做的事情。

您已经多次说过“自定义控件”,实际上这是一个用户控件。这是有区别的。自定义控件(在某些基于 XAML 的 UI 中有时称为模板化控件)通常继承自 Control 并提供控件模板。

评论

0赞 Michael Riley 10/11/2023
感谢您的回复。我想您刚刚指出了我对自定义控件和用户控件之间的根本误解。您认为实现自定义控件会是更好的方法吗?
0赞 Tarazed 10/11/2023
在这种情况下,用户控件似乎没问题。用户控件是特定设计中的控件的集合,这似乎是您在此处拥有的控件。自定义控件是“无视图”控件。它定义了所有功能,然后可以应用一个或多个模板。它在模板中引用了已命名的“部件”,但并不关心它们的布局方式,也不关心命名部件之外可能存在哪些其他控件,它甚至必须假设命名部件可能不存在。自定义控件的制作可能要复杂得多。
1赞 Ferid Š. Sejdović 10/11/2023 #2

问题 1:

当您想要在代码隐藏中引用控件时(这是实现 MVVM 模式时最不想做的事情)或使用绑定时,只需将值绑定到视图模型属性中的控件时,就需要名称。例如,如果控件的类型为 textbox,并且具有 text 属性,则会将该值绑定到相应视图模型中该视图中的控件。阅读有关将值绑定到特定控件属性的信息,这是您非常需要执行的操作。

问题2:

如果希望解决方案采用“模型视图”视图模式方式,则需要了解 WPF 中的关注点分离。

此外,正如我所说,如果你有一个控件(复选框、组合框等),你可以从相应的视图模型将值绑定到该视图中的该控件。简而言之,您需要在视图模型中定义与您的视图相关的所有内容(绑定到控件值的属性、按钮的命令(如果有的话)等,当然要实现 INotify 属性更改界面,以便获得完整的实时工作解决方案)。因此,您可以从用户控件创建视图,并在视图模型中为每个控件定义所有内容,然后您将拥有所需的每个值,简而言之。我想提一下,您需要检查数据模板,当您想在 WPF 中定义这样的东西时,这是一件非常强大的事情。

最后,我将根据您在 App.xaml 中定义的所有内容制作一个资源字典,并在合并字典中的同一文件中引用它,仅此而已。

因此,如果您希望在 WPF 选项卡中控制用户控件,则如下所示:

<TabControl Name="myTabControl">
    <TabItem Header="Tab 1">
        <local:YourUserControlName />
    </TabItem>
    <TabItem Header="Tab 2">
        <local:YourUserControlName />
    </TabItem>
    <!-- Add more TabItems as needed -->
</TabControl>

其中 local 是定义用户 conrol 的命名空间。

如果你想在WPF的选项卡控件中使用视图(因为你想要在视图模型中定义所有内容 - 组合框、复选框等的属性),你可以做这样的事情:

<TabControl Name="myTabControl">
    <TabItem Header="Tab 1">
        <ContentControl>
            <ContentControl.Content>
                <local:YourViewName />
            </ContentControl.Content>
        </ContentControl>
    </TabItem>
    <TabItem Header="Tab 2">
        <ContentControl>
            <ContentControl.Content>
                <local:YourViewName />
            </ContentControl.Content>
        </ContentControl>
    </TabItem>
    <!-- Add more TabItems as needed -->
</TabControl>

内容控件将托管您的视图。最后,我最喜欢的一个是制作一个这样的数据模板:

<DataTemplate x:Key="MyTabItemContentTemplate">
    <!-- Your UI elements and data bindings go here -->
</DataTemplate>

例如,此 Data Temlate 可以位于资源字典中,但您需要在 Main 中引用它。

<TabControl Name="myTabControl">
    <TabItem Header="Tab 1" ContentTemplate="{StaticResource MyTabItemContentTemplate}">
        <!-- Data context for Tab 1 content will be bound to the DataTemplate -->
    </TabItem>
    <TabItem Header="Tab 2" ContentTemplate="{StaticResource MyTabItemContentTemplate}">
        <!-- Data context for Tab 2 content will be bound to the DataTemplate -->
    </TabItem>
    <!-- Add more TabItems as needed -->
</TabControl>

在这种情况下,每个选项卡项的内容模板属性都是使用数据模板(同样使用用户控件中的所有自定义控件)定义的,并且您需要设置数据上下文才能绑定这些控件的值和所需的所有内容。

最后,关键字是 、 、 、 ,仅此而已。ViewView ModelNotify property change interfaceData Contextbindings

评论

1赞 Michael Riley 10/12/2023
费里德,非常感谢你为我分解了这一点。在你的回应和@Tarezed的回应之间,我相信我有一条坚实的前进道路。非常感谢!
0赞 Ferid Š. Sejdović 10/12/2023
@MichaelRiley 对于一些语法错误和类似错误,我深表歉意,但我很高兴你明白了,简而言之 - 阅读模型视图、查看模型模式并检查示例,这将为您提供线索、该怎么做以及每个控制(在您的情况下的选项卡)都可以修改(以适当的方式)以获得解决方案,但您需要练习,仅此而已。干杯!
1赞 Evgeniy 10/14/2023 #3

我建议从 MVVM 开始。

  1. 您不需要使用 . 您无需为每个设备单独创建。 您将拥有您的设备。添加设备后,只需将其添加到 .X:NameUserControlModelcollection

  2. 如果您要在 Your 中使用,它将显示它。 您可以使用传递参数,例如 RS422 设备的 ID。ItemsControlViewCommand

以下是如何使用 .您需要在项目中创建文件夹:CommunityToolkit.MVVM

  • 具有 RS422Model 类的模型
  • 带有 RS422ViewModel 类的 ViewModel

主窗口

<Window x:Class="RS422.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:RS422" xmlns:viewmodel="clr-namespace:RS422.ViewModel"
        mc:Ignorable="d" xmlns:conv="clr-namespace:RS422.ValueConverters"
        Title="MainWindow" Height="450" Width="800">
    <d:Window.DataContext>
        <viewmodel:RS422ViewModel/>
    </d:Window.DataContext>
    <Window.Resources>
        <conv:BooleanToColorConverter x:Key="BooleanToColor"/>
    </Window.Resources>
    <Grid Margin="20 0 0 0">
        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBlock Text="Title" HorizontalAlignment="Center" FontSize="30"/>
        <Button Grid.Row="1" Content="Test" Width="80" HorizontalAlignment="Left" Margin="5" />
        <Grid Grid.Row="2">
            <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Background="Transparent">
                <ItemsControl ItemsSource="{Binding RS422Collection}" >
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <GroupBox Header="{Binding Id}">
                                    <StackPanel Orientation="Vertical">
                                        <Border BorderThickness="1">
                                            <StackPanel Orientation="Horizontal">
                                                <CheckBox Content="Edit" IsChecked="{Binding IsEdit}"/>
                                                <CheckBox Content="Use" IsChecked="{Binding IsUse}" />
                                                <TextBlock Text="Read Type" />
                                                <ComboBox SelectedIndex="0" ItemsSource="{Binding ReadType}" />
                                                <TextBlock Text="Write Type" />
                                                <ComboBox SelectedIndex="0" ItemsSource="{Binding WriteType}" />
                                            </StackPanel>
                                        </Border>
                                        <Border BorderThickness="1">
                                            <StackPanel Orientation="Horizontal">
                                                <Border BorderThickness="1">
                                                    <StackPanel Orientation="Vertical">
                                                        <TextBlock Text="Baud" />
                                                        <ComboBox SelectedIndex="0" ItemsSource="{Binding Baud}" />
                                                    </StackPanel>
                                                </Border>
                                                <Border BorderThickness="1">
                                                    <StackPanel Orientation="Vertical">
                                                        <TextBlock Text="Parity" />
                                                        <ComboBox SelectedIndex="0" ItemsSource="{Binding Parity}"/>
                                                    </StackPanel>
                                                </Border>
                                                <Border BorderThickness="1">
                                                    <StackPanel Orientation="Vertical">
                                                        <TextBlock Text="Data Bits" />
                                                        <ComboBox SelectedIndex="0" ItemsSource="{Binding DataBits}" />
                                                    </StackPanel>
                                                </Border>
                                                <Border BorderThickness="1">
                                                    <StackPanel Orientation="Vertical">
                                                        <TextBlock Text="Stop Bits" />
                                                        <!--  stop bits value of None is not currently supported  -->
                                                        <ComboBox SelectedIndex="1" ItemsSource="{Binding StopBits}" />
                                                    </StackPanel>
                                                </Border>
                                                <Border BorderThickness="1">
                                                    <StackPanel Orientation="Vertical">
                                                        <TextBlock Text="FCC Position" />
                                                        <ComboBox SelectedIndex="0" ItemsSource="{Binding FCCPosition}" />
                                                    </StackPanel>
                                                </Border>
                                                <Border BorderThickness="1">
                                                    <StackPanel Orientation="Vertical">
                                                        <TextBlock Text="Status" />
                                                        <Ellipse Width="10" Height="10" Fill="{Binding Status, Converter={StaticResource BooleanToColor }}" />
                                                        <Button Content="Check" Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DataContext.CheckCommand}" CommandParameter="{Binding Id}"/>
                                                    </StackPanel>
                                                </Border>
                                            </StackPanel>
                                        </Border>
                                    </StackPanel>
                                </GroupBox>
                            </Grid>

                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapPanel/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
            </ScrollViewer>
        </Grid>
    </Grid>
</Window>

MainWindow 代码隐藏:

public partial class MainWindow : Window
{
    RS422ViewModel viewModel;
    public MainWindow()
    {
        InitializeComponent();
        viewModel = new RS422ViewModel();
        DataContext = viewModel;
     }
}

您的型号:

public partial class RS422Model: ObservableObject
{
    [ObservableProperty]
    private string? _id;

    [ObservableProperty]
    private bool? _isEdit;

    [ObservableProperty]
    private bool? _isUse;

    [ObservableProperty]
    private string[]? _readType;

    [ObservableProperty]
    private string[]? _writeType;

    [ObservableProperty]
    private string[]? _baud;

    [ObservableProperty]
    private string[]? _parity;

    [ObservableProperty]
    private string[]? _dataBits;

    [ObservableProperty]
    private string[]? _stopBits;

    [ObservableProperty]
    private string[]? _fCCPosition;

    [ObservableProperty]
    private bool? _status;
}

您的 ViewModel:

    public partial class RS422ViewModel : ObservableObject
    {
        [ObservableProperty]
        ObservableCollection<RS422Model> _rS422Collection;

        public RS422ViewModel()
        {
            RS422Collection = new ObservableCollection<RS422Model>();

            //TEST DATA where i = number of devices
            for (int i = 0; i < 8; i++)
            {
                RS422Collection.Add(new RS422Model()
                {
                    Id = i.ToString(),
                    IsEdit = true,
                    IsUse = true,
                    ReadType = new string[] { "Type 1", "Type 2", "Type 3", "Type 4" },
                    WriteType = new string[] { "Type 1", "Type 2", "Type 3", "Type 4" },
                    Baud = new string[] { "14400", "19200", "38400", "57600" },
                    Parity = new string[] { "none", "Even", "Odd" },
                    DataBits = new string[] { "none 1", "none 2", "none 3" },
                    StopBits = new string[] { "none 1", "none 2", "none 3" },
                    FCCPosition = new string[] { "none 1", "none 2", "none 3" },
                    Status = true
                });
            }
        }

        [RelayCommand]
        public void Check(string parameter)
        {
            //TEST
            MessageBox.Show($"Test for device ID { parameter } ");
            if (parameter == "0")
            {
                //this is Device with Id 0
            }
            else if (parameter == "1")
            {
                //this is Device with Id 1
            }
            else if (parameter == "2")
            {
                //this is Device with Id 2
            }
              //and so on
        }
    }

Check按钮现在将带有 . 您可以创建操作 .您可以像处理数组一样对元素进行寻址。对于颜色,您需要使用 - 例如类。只需将文件夹添加到您的项目中并创建一个类CommandparameterRS422CollectionStatusConvertersbool to color converterValueConverters

public class BooleanToColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if ((bool)value)
            {
                {
                    return new SolidColorBrush(Colors.Red);
                }
            }
            return new SolidColorBrush(Colors.White);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return false;
        }
    }

您需要在ValueConvertersMainWindow

xmlns:conv="clr-namespace:RS422.ValueConverters"

然后,您将作为资源添加到ConverterMainWindow


<Window.Resources>
     <conv:BooleanToColorConverter x:Key="BooleanToColor"/>
</Window.Resources>

当改变 - 它会切换. 希望这会有所帮助。Statusboolellip'sfill