WPF MVVM 如何从单选按钮组更改事件时更改 DataGridRow 的 bg 颜色?

WPF MVVM How change bg color of DataGridRow on change event from group of radiobuttons?

提问人:John 提问时间:11/30/2022 最后编辑:EldHaspJohn 更新时间:12/1/2022 访问量:58

问:

我有 DataGrid,列名为“Source”和“Change source”,其中包含 3 个单选按钮。

Exampl

如果列中的值不相等,则在更改单选按钮的值时,我正在尝试填充行的背景。 即我需要跟踪更改,如果源已更改,则突出显示该行,如果源返回到“源”列中指示的值,则删除突出显示。

我该如何实现,请帮忙

XAML

<DataGrid.RowStyle>
    <Style TargetType="DataGridRow">        
        <Style.Triggers>
            <DataTrigger Binding="{Binding StateRowChanged}" Value="True">
                <Setter Property="Background" Value="Red"></Setter>
            </DataTrigger>
            <DataTrigger Binding="{Binding StateRowChanged}" Value="False">
                <Setter Property="Background" Value="White"></Setter>
            </DataTrigger>             
        </Style.Triggers>
    </Style>
</DataGrid.RowStyle>

........

<DataGridTextColumn Header="Source" Binding="{Binding Path='Source'}" IsReadOnly="True" />
<DataGridTemplateColumn Header="Change source" >
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" >
                <RadioButton Cursor="Hand" Padding="5 0" Content="c" GroupName="{Binding id}"   
                    IsChecked="{Binding selectedSrcOption,UpdateSourceTrigger=PropertyChanged,
                    Converter={StaticResource RadioButtonCheckedConverter}, 
                    ConverterParameter={x:Static local:SrcOptions.C}}"/>
                 <RadioButton Cursor="Hand" Padding="5 0" Content="i" GroupName="{Binding id}"   
                        IsChecked="{Binding selectedSrcOption,UpdateSourceTrigger=PropertyChanged,
                        Converter={StaticResource RadioButtonCheckedConverter}, 
                        ConverterParameter={x:Static local:SrcOptions.I}}"/>
                <RadioButton  Cursor="Hand" Padding="5 0" Content="m" GroupName="{Binding id}" 
                        IsChecked="{Binding selectedSrcOption,UpdateSourceTrigger=PropertyChanged,
                        Converter={StaticResource RadioButtonCheckedConverter}, 
                        ConverterParameter={x:Static local:SrcOptions.M}}"/>
            </StackPanel>
         </DataTemplate>
</DataGridTemplateColumn.CellTemplate>

...some props...

private bool _stateRowChanged;
public bool StateRowChanged
{
    get{return _stateRowChanged;}
    set{
        if (value == _stateRowChanged) return;
        _stateRowChanged = value;
        OnPropertyChanged("StateRowChanged");
        }
}

MainWindowView模型


new MyModel { id=0,Source="c",selectedSrcOption="c"};
new MyModel { id=1,Source="i",selectedSrcOption="m"}; 
new MyModel { id=2,Source="m",selectedSrcOption="c"};

..

foreach (MyModel m in Data.AllItems)
{
    m.PropertyChanged += EntryOnPropertyChanged;
}
...

private void EntryOnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
    if (args.PropertyName == nameof(MyModel.selectedSrcOption))
    {
    //Gets radio button click event!
    MessageBox.Show(args.PropertyName.ToString());
    //How to check if Source != selectedSrcOption 
    //and change StateRowChanged prop?

    }
}
C# WPF 事件 MVVM

评论

0赞 Smolakian 12/1/2022
很难说出你想实现什么。如果某一行与所有其他行单选按钮不同,您是否正在尝试突出显示该行?如果它匹配一个、两个等怎么办?无论如何,如果您有权访问数据,则可以循环访问每个项,并在 PropertyChanged 处理程序中比较源。
0赞 John 12/1/2022
不,我正在尝试逐行比较值。我不明白如何在EntryOnPropertyChanged中获取列值

答:

1赞 EldHasp 12/1/2022 #1

有必要添加一种样式,其中有一个触发器,用于为不同的属性值设置行的背景色。
此外,如果有效值的常量列表有限,那么我建议您将其实现为枚举。
若要实现按钮列表,最好使用 ListBox。

using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Windows.Data;

namespace Core2022.SO.John.ChangeSource
{
    public enum SourceEnum
    {
        c, i, m
    }

    public class SourceModel
    {
        public int Id { get; set; }
        public SourceEnum Source { get; set; }

        public SourceEnum SelectedSource { get; set; }
    }
    public class SourcesViewModel
    {
        public static ReadOnlyCollection<SourceEnum> Sources { get; } = Array.AsReadOnly(Enum.GetValues<SourceEnum>());
        public SourceModel[] SourceModels { get; } =
        {
            new SourceModel { Id=0, Source=SourceEnum.c, SelectedSource=SourceEnum.c},
            new SourceModel { Id=1, Source=SourceEnum.i, SelectedSource=SourceEnum.m},
            new SourceModel { Id=2, Source=SourceEnum.m, SelectedSource=SourceEnum.c}
        };
    }
    public class EqualsConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return Equals(values[0], values[1]);
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        private EqualsConverter() { }
        public static EqualsConverter Insatance { get; } = new();
    }
}
<Window x:Class="Core2022.SO.John.ChangeSource.SourcesWindow"
        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:Core2022.SO.John.ChangeSource"
        mc:Ignorable="d"
        Title="SourcesWindow" Height="450" Width="800"
        DataContext="{DynamicResource vm}">
    <Window.Resources>
        <local:SourcesViewModel x:Key="vm"/>
        <DataTemplate x:Key="SelectedSource.Template"
                      DataType="local:SourceModel">
            <ListBox ItemsSource="{x:Static local:SourcesViewModel.Sources}"
                     SelectedItem="{Binding SelectedSource, UpdateSourceTrigger=PropertyChanged}"
                     Background="Transparent">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <RadioButton Content="{Binding}"
                                     IsChecked="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ListBox>
        </DataTemplate>
        <Style TargetType="DataGridRow"
               x:Key="SourceModel.RowStyle">
            <Style.Triggers>
                <DataTrigger Value="False">
                    <DataTrigger.Binding>
                        <MultiBinding Converter="{x:Static local:EqualsConverter.Insatance}">
                            <Binding Path="Source"/>
                            <Binding Path="SelectedSource"/>
                        </MultiBinding>
                    </DataTrigger.Binding>
                    <Setter Property="Background" Value="Coral"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <DataGrid ItemsSource="{Binding SourceModels}" AutoGenerateColumns="False" ItemContainerStyle="{DynamicResource SourceModel.RowStyle}">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Id}" Header="Id" IsReadOnly="True"/>
                <DataGridTextColumn Header="Source" Binding="{Binding Source}" IsReadOnly="True" />
                <DataGridTemplateColumn Header="Change source"  CellTemplate="{StaticResource SelectedSource.Template}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

enter image description here

评论

0赞 John 12/1/2022
感谢您提供如此详细的解决方案!它对我很好用。我在转换器类中对我的 c#7.3 进行了一些更改,并将 cast添加到 value[1] 的 String 中。多谢!