我希望当值更改时数据网格的前景色为蓝色。因此,用户应该明白该值已更改并且应激活保存功能。
为此,我在 MainVM 类中创建了一个布尔属性名称 CellInEdit。当用户更改单元格中的值时,CellInEdit 属性值将变为 True。为了实现这一点,我创建了一个 ValueConverter 类,它将布尔类型转换为颜色。但它不起作用。
这些是 XAML 代码:
<Window x:Class="TestAppDataGrid.View.MainView"
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:TestAppDataGrid.View"
xmlns:converters="clr-namespace:TestAppDataGrid.ViewModel.ValueConverters"
xmlns:vm="clr-namespace:TestAppDataGrid.ViewModel"
mc:Ignorable="d"
Title="MainView" Height="450" Width="300">
<Window.DataContext>
<vm:MainVM/>
</Window.DataContext>
<Window.Resources>
<converters:BoolenToGridForegroundConverter x:Key="bg"/>
<Style TargetType="DataGridCell">
<Setter Property="Foreground" Value="{Binding CellInEdit, Converter={StaticResource bg}}"/>
</Style>
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding Persons}"
AutoGenerateColumns="False"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn IsReadOnly="True" Binding="{Binding Id}" Header="Id" Width="30"/>
<DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="80" />
<DataGridTextColumn Binding="{Binding SurName}" Header="Surname" Width="180"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
这是模型类:
public class Person:INotifyPropertyChanged
{
MainVM VM;
public Person(MainVM vm)
{
VM=vm;
}
public int Id { get; set; }
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Name");
VM.CellInEdit = true;
}
}
private string surName;
public string SurName
{
get { return surName; }
set
{
surName = value;
OnPropertyChanged("SurName");
VM.CellInEdit = true;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
这是 MainVM 类:
public class MainVM : INotifyPropertyChanged
{
public ObservableCollection<Person> Persons { get; set; }
public MainVM()
{
PopulatePersonList();
}
private void PopulatePersonList()
{
Persons= new ObservableCollection<Person>
{
new Person(this)
{
Id = 1,
Name = "Alex",
SurName = "Demon",
},
new Person(this)
{
Id = 2,
Name = "Jack",
SurName = "Further",
},
new Person(this)
{
Id = 3,
Name = "Lisa",
SurName = "Kyle",
}
};
}
private bool cellInEdit;
public bool CellInEdit
{
get { return cellInEdit; }
set
{
cellInEdit = value;
OnPropertyChanged("CellInEdit");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
这是 IValueConverter 类:
class BoolenToGridForegroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool CellInEdit= (bool)value;
if(CellInEdit)
return Brushes.Blue;
else
return Brushes.Black;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
编辑:我为那些有类似问题的人编辑了帖子。
根据@Sinatr的建议,仅在值发生变化的行中更改颜色的想法似乎更合理。
我将 CellInEdit 属性移至 Person 类,如评论中所述。而且已经固定了。
您不应该用这种纯粹的视图相关状态信息污染您的数据模型或视图模型类。这可以被视为违反 MVVM。
相反,您应该完全在视图中处理突出显示。
例如,只有在视图模型中的单元格编辑状态与数据或数据处理相关时,才可以处理该状态。
为了正确封装,您应该考虑扩展
DataGrid
并在扩展类型中实现高亮逻辑。
以下示例展示了如何使用 clean MVVM 在单元格值已更改的情况下突出显示视图中已编辑的单元格和已编辑的行。
如果您扩展
DataGrid
,您只需将以下代码移动到这个新类型即可。
MainWindow.xaml
<DataGrid PreparingCellForEdit="DataGrid_PreparingCellForEdit"
CellEditEnding="DataGrid_CellEditEnding"
RowEditEnding="DataGrid_RowEditEnding" />
MainWindow.xaml.cs
partial class MainWindow : Window
{
private string OldEditingCellValue { get; set; }
private bool HasEditedCellValueChanged { get; set; }
// Get and store the current value of the cell before editing
// to enable the identification of a change.
// When this handler is called, we know that the editing operation
// was not cancelled.
private void DataGrid_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
{
this.HasEditedCellValueChanged = false;
var cellContent = e.Column.GetCellContent(e.Row);
if (e.Column is DataGridTextColumn)
{
var cellEditingElement = (TextBox)cellContent;
this.OldEditingCellValue = cellEditingElement.Text;
}
}
// Get the current value of the cell after editing
// and identify a possible change
private void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if (e.EditAction is DataGridEditAction.Cancel)
{
return;
}
if (e.Column is DataGridTextColumn textColumn)
{
var cellContent = (TextBox)textColumn.GetCellContent(e.Row);
string postEditValue = cellContent.Text;
this.HasEditedCellValueChanged = !this.OldEditingCellValue.Equals(postEditValue, StringComparison.Ordinal);
}
}
// When the currently edited cell's value has changed
// then we can apply the highlighting of the edited row and cell
private void DataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
{
if (!this.HasEditedCellValueChanged)
{
return;
}
var dataGrid = (DataGrid)sender;
if (dataGrid.CurrentColumn is DataGridTextColumn textColumn)
{
FrameworkElement cellContent = textColumn.GetCellContent(dataGrid.CurrentItem);
var editedCell = (DataGridCell)cellContent.Parent;
/* Apply the highlighting to row and cell individually */
e.Row.Background = Brushes.Orange;
editedCell.Background = Brushes.Red;
}
}
}