我正在尝试编写一个从远程端点收集设置并在 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 事件,但这并没有真正改变单元格背景。
我可以根据所选行值以某种方式更改单元格背景颜色吗?如果列中其他单元格的值与所选单元格的值不同,则该单元格应为红色,否则为绿色。
我不确定我是否理解您的逻辑,但如果您想根据当前选择的项目设置单元格的属性,您可以使用
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
,每次选择一个项目时都应该为所有单元格调用转换器。