根据绑定到 DataTable 的 WPF MVVM DataGrid 中选定的行值设置 DataGridCell 背景样式

问题描述 投票:0回答:1

我正在尝试编写一个从远程端点收集设置并在 DataGrid 中比较它们的应用程序。这些单元可以具有不同数量的设置,具体取决于它们运行的软件版本,并且设置也可以在较新的软件版本中删除,而不仅仅是添加。每个单元有约 500 个设置。 由于设置信息的差异,我无法编写正确的类并描述每个设置,因此我正在构建一个字典,其中设置名称是键,设置参数值是值。当每个单元的字典完成后,我将其添加到 ObservableCollection 中。该集合是“settingsCollection”视图和“compareSettings”视图之间的共享资源,并且在两者中都注入了依赖项。

WPF Datagrid 并不真正与字典一起使用,因此我正在构建一个可以绑定 DataGrid 的 DataTable。如果它是一个非 MVVM 应用程序,我会从 .xaml.cs 文件构建我的 DataGrid 并在此处添加所有属性。 我使可视化工作正常,但我希望单元格背景根据所选行改变颜色。如果我有两行,如下所示:

单位 设置 1 设置 2 设置 3
第 1 单元 1 2 3
第二单元 2 2 2

选择第一行,我希望“设置 1”和“设置 3”列中的 2 为红色,而“设置 2”列中的 2 应为绿色。如果我要更改选定的行,单元格中的背景应该改变颜色,具体取决于它们是否相同。

我在我的视图模型构造函数中构建了一个假的“settingsCollector”,只是让它更容易使用。

public class MainWindowViewModel : ObservableObject
{
    private DataTable _settingsdataTable = new DataTable();
    public DataTable SettingsDataTable
    {
        get => _settingsdataTable;
        set {
            if (SettingsDataTable != value) {
                _settingsdataTable = value;
                OnPropertyChanged(nameof(SettingsDataTable));
            }
        }
    }

    private Dictionary<string, string> _selectedUnit = new();
    public Dictionary<string, string> SelectedUnit
    {
        get => _selectedUnit;
        set {
            if (SelectedUnit != value) {
                _selectedUnit = value;
                OnPropertyChanged(nameof(SelectedUnit));
            }
        }
    }
    public ICommand CellSelectionChangedCommand { get; }
    private void CellSelected(object o)
    {
        DataGrid dataGrid = o as DataGrid;
        Dictionary<string, string> newSelectedUnit = new();

        for (int i = 0; i < dataGrid.SelectedCells.Count; i++) {
            DataRowView rowView = dataGrid.SelectedCells[i].Item as DataRowView;
            newSelectedUnit.Add(dataGrid.SelectedCells[i].Column.Header.ToString(), rowView.Row[i].ToString());
        };
        SelectedUnit = newSelectedUnit;
    }

    public MainWindowViewModel()
    {
        ObservableCollection<Dictionary<string, string>> DictionaryCollection = new();

        // Make up some settings from multiple diffrent endpoints
        for (int i = 0; i < 2; i++) {
            Dictionary<string, string> incomingSettings = new();
            incomingSettings.Add($"Setting {i}", $"{i}");
            incomingSettings.Add($"Setting {i + 1}", $"{i}");
            incomingSettings.Add($"Setting {i + 2}", $"{i}");
            incomingSettings.Add($"Setting {i + 3}", $"{i}");
            incomingSettings.Add($"Setting {i + 4}", $"{i}");
            incomingSettings.Add($"Setting {i + 6}", $"{i + 3}");
            //... Could continue forever
            DictionaryCollection.Add(incomingSettings);
        };
        SelectedUnit = DictionaryCollection.First();

        // Go through the settings parameters in the incomming units
        // and add them to a list of all possible existing settings parameters
        List<string> AllDitionaryKeys = new();
        foreach (var item in DictionaryCollection)
        {
            foreach (var key in item.Keys)
            {
                if (!AllDitionaryKeys.Contains(key))
                    AllDitionaryKeys.Add(key);
            }
        }

        // Order keys to get all settings alphabetically.
        AllDitionaryKeys = AllDitionaryKeys.OrderBy(x => x.Length).ThenBy(x => x).ToList();

        // Create the full dataTable
        // Start with adding the headers in the table
        DataTable newDataTable = new();
        foreach (var header in AllDitionaryKeys)
        {
            newDataTable.Columns.Add(header, typeof(string));
        }

        // Add values in the table depending on the header keys
        // If the setting does not exists in the unit, add a "-" instead
        foreach (var item in DictionaryCollection)
        {
            DataRow row = newDataTable.NewRow();
            foreach (var key in AllDitionaryKeys)
            {
                if (!item.ContainsKey(key))
                    row[key] = "-";
                else
                    row[key] = item[key];
            }
            newDataTable.Rows.Add(row);
        }

        // Update the dataTable bound to the view
        SettingsDataTable = newDataTable;

        // RelayCommands from MVVM Toolkit
        CellSelectionChangedCommand = new RelayCommand<object>(o =>
        {
            Debug.WriteLine("CellSelectionChanged");
            CellSelected(o);
        });
    }
}
<UserControl x:Class="WpfApp1.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:viewmodel="clr-namespace:WpfApp1"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        xmlns:local="clr-namespace:WpfApp1" 
        d:DataContext="{d:DesignInstance Type=viewmodel:MainWindowViewModel}"
        mc:Ignorable="d"
        Background="AntiqueWhite">
    <UserControl.Resources>
        <local:CellBackgroundConverter x:Key="CellBackgroundConverter"/>
        <CollectionViewSource x:Key="MyDataViewSource" Source="{Binding SettingsDataTable.DefaultView}" />
    </UserControl.Resources>
    
    <Grid
        Margin="10"
    >
        <DataGrid 
            x:Name="MyDataGrid"
            CanUserAddRows="True"
            AutoGenerateColumns="True" 
            ItemsSource="{Binding Source={StaticResource MyDataViewSource}}" 
            Margin="5,90,5,5"
            SelectionUnit="FullRow"
            SelectionMode="Single"
            IsReadOnly="True">

            <!--Add interaction triggers-->
            <i:Interaction.Triggers>
                <!--Add event trigger-->
                <i:EventTrigger 
                    EventName="SelectedCellsChanged">
                    <i:InvokeCommandAction 
                        Command="{Binding CellSelectionChangedCommand}"
                        CommandParameter="{Binding ElementName=MyDataGrid}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>

            <DataGrid.CellStyle>
                <Style TargetType="DataGridCell">
                    <!-- Set the default background color -->
                    <Setter Property="Background" Value="Blue" />
                    <Style.Triggers>
                        <!-- Set the background color for unselected cells in the selected column -->
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridRow}}, Path=IsSelected}" Value="False">
                            <Setter Property="Background" Value="Red" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </DataGrid.CellStyle>
        </DataGrid>
    </Grid>
</UserControl>

我尝试添加多重绑定并构建转换器,但它被触发了太多次。我还尝试了 aoutoGenerateColumn 事件,但这并没有真正改变单元格背景。

我可以根据所选行值以某种方式更改单元格背景颜色吗?如果列中其他单元格的值与所选单元格的值不同,则该单元格应为红色,否则为绿色。

c# wpf mvvm datagrid datatrigger
1个回答
0
投票

我不确定我是否理解您的逻辑,但如果您想根据当前选择的项目设置单元格的属性,您可以使用

MultiBinding
绑定到
SelectedItem
DataGrid
属性和当前项目,然后根据您的要求实现转换器以返回适当的
Brush
,例如:

public class MultiConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        object selectedItem = values[0];
        object currentItem = values[1];

        // your logic here...

        return Brushes.Green;
    }

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

XAML:

<DataGrid.CellStyle>
    <Style TargetType="DataGridCell">
        <Setter Property="Background">
            <Setter.Value>
                <MultiBinding Converter="{StaticResource multiConverter}">
                    <Binding Path="SelectedItem" RelativeSource="{RelativeSource AncestorType=DataGrid}" />
                    <Binding Path="." />
                </MultiBinding>
            </Setter.Value>
        </Setter>
    </Style>
</DataGrid.CellStyle>

由于您绑定到

SelectedItem
,每次选择一个项目时都应该为所有单元格调用转换器。

© www.soinside.com 2019 - 2024. All rights reserved.