C# 在datagrid上以datatable WPF中的红色更改文本显示
当数据表中的某个单元格与另一个数据表中的同一单元格不同时,如何以红色文本显示该单元格的值?在最终的应用程序中,表格将通过更改csv文件生成。所以我必须替换自动生成的列。我知道您需要一个DataGridTemplateColumn,将CellTemplate设置为资源。然而,这个被替换的列不是可视化树的一部分,因此绑定不起作用 本文展示了一个实现Freezable对象的转换器应该解决这个问题。为了找出这个解决方案,我做了下一个简化的例子。但它会在发生更改的列的所有单元格中显示该单元格的最后一个值,并且全部显示为红色: ViewModel:C# 在datagrid上以datatable WPF中的红色更改文本显示,c#,wpf,xaml,datagrid,datatable,C#,Wpf,Xaml,Datagrid,Datatable,当数据表中的某个单元格与另一个数据表中的同一单元格不同时,如何以红色文本显示该单元格的值?在最终的应用程序中,表格将通过更改csv文件生成。所以我必须替换自动生成的列。我知道您需要一个DataGridTemplateColumn,将CellTemplate设置为资源。然而,这个被替换的列不是可视化树的一部分,因此绑定不起作用 本文展示了一个实现Freezable对象的转换器应该解决这个问题。为了找出这个解决方案,我做了下一个简化的例子。但它会在发生更改的列的所有单元格中显示该单元格的最后一个值,
using System;
using System.ComponentModel;
using System.Data;
namespace WpfApplication1
{
class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
//private Model _Model; //for clarity left out
private DataTable _propDataTable;
public DataTable propDataTable
{
get { return _propDataTable; }
set
{
_propDataTable = value;
NotifyPropertyChanged("propDataTable");
}
}
private DataTable propCopyDataTable;
private string _sB;
public string sB
{
get { return _sB; }
set
{
_sB = value;
NotifyPropertyChanged("sB");
}
}
private bool _bB = false;
public bool bB
{
get { return _bB; }
set
{
_bB = value;
NotifyPropertyChanged("bB");
}
}
public ViewModel()
{
propDataTable = new DataTable();
propDataTable.Columns.Add("A", typeof(string));
propDataTable.Columns.Add("B", typeof(string));
DataRow row0 = propDataTable.NewRow();
DataRow row1 = propDataTable.NewRow();
row0[0] = "A0";
row0[1] = "B0";
row1[0] = "A1";
row1[1] = "B1";
propDataTable.Rows.Add(row0);
propDataTable.Rows.Add(row1);
propCopyDataTable = propDataTable.Copy();
//now set a different value in propCopyDataTable
propCopyDataTable.Rows[1][1] = "Changed";
//find out which cells in column B are different
//try to show in red text which cell changed
for (int i = 0; i < propDataTable.Rows.Count; i++)
{
DataRow dr = propDataTable.Rows[i];
DataRow drc = propCopyDataTable.Rows[i];
sB = (string) dr["B"];
if (dr["B"].ToString().Equals(drc["B"].ToString()))
{
bB = true;
}
else
{
bB = false;
}
}
}
}
}
BindingProxy转换器:
using System.Windows;
namespace WpfApplication1
{
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
}
XAML控件:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myViewModel="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<myViewModel:ViewModel/>
</Window.DataContext>
<Window.Resources>
<myViewModel:ObjectToForegroundConverter x:Key="MyObjectToForegroundConverter"/>
<myViewModel:BindingProxy x:Key="proxy" Data="{Binding}" />
<DataTemplate x:Key="changedBColumn" >
<TextBlock
Text="{Binding Data.sB,Source={StaticResource proxy},Mode=OneWay}"
Foreground="{Binding Data.bB,Converter={StaticResource MyObjectToForegroundConverter},Source={StaticResource proxy},Mode=OneWay}"
/>
</DataTemplate>
</Window.Resources>
<Grid>
<DataGrid x:Name="myXAMLtable" AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"
ItemsSource="{Binding propDataTable}">
</DataGrid>
</Grid>
</Window>
背后的代码:
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
switch (e.Column.Header.ToString())
{
case "B":
{
DataGridTemplateColumn BTemplateColumn = new DataGridTemplateColumn();
BTemplateColumn.Header = "B";
BTemplateColumn.CellTemplate = (DataTemplate)Resources["changedBColumn"];
e.Column = BTemplateColumn;
break;
}
}
}
}
}
使用System.Windows;
使用System.Windows.Controls;
命名空间WpfApplication1
{
///
///MainWindow.xaml的交互逻辑
///
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
}
私有void DataGrid_AutoGeneratingColumn(对象发送方,DataGridAutoGeneratingColumnEventArgs e)
{
开关(例如Column.Header.ToString())
{
案例“B”:
{
DataGridTemplateColumn BTemplateColumn=新DataGridTemplateColumn();
BTemplateColumn.Header=“B”;
BTemplateColumn.CellTemplate=(DataTemplate)资源[“changedBColumn”];
e、 Column=BTemplateColumn;
打破
}
}
}
}
}
这里的问题是每个单元格都需要一个bool来指示变化的状态。但视图模型中的列只有一个属性。这意味着根据该属性,同一列中的所有单元格将具有相同的状态。这就解释了为什么列B
中的所有单元格都是红色的,因为属性bB
设置为true并绑定到列B
中的所有单元格
您需要一个item类(而不仅仅是简单的字符串值)来保存状态,如下所示:
public class Item {
public string Value {get;set;}
public Item(string value){
Value = value;
}
public bool IsChanged { get; private set;}
public void SetChanged(){
IsChanged = true;
}
public override string ToString(){
return Value;
}
public override bool Equals(object other){
var item = other as Item;
if(item == null) return false;
return item.Value == Value;
}
public override int GetHashCode(){
if(Value == null) return base.GetHashCode();
return Value.GetHashCode();
}
}
propDataTable = new DataTable();
propDataTable.Columns.Add("A", typeof(Item));
propDataTable.Columns.Add("B", typeof(Item));
DataRow row0 = propDataTable.NewRow();
DataRow row1 = propDataTable.NewRow();
row0[0] = new Item("A0");
row0[1] = new Item("B0");
row1[0] = new Item("A1");
row1[1] = new Item("B1");
propDataTable.Rows.Add(row0);
propDataTable.Rows.Add(row1);
propCopyDataTable = propDataTable.Copy();
//now set a different value in propCopyDataTable
propCopyDataTable.Rows[1][1] = new Item("Changed");
//find out which cells in column B are different
//try to show in red text which cell changed
for (int i = 0; i < propDataTable.Rows.Count; i++) {
DataRow dr = propDataTable.Rows[i];
DataRow drc = propCopyDataTable.Rows[i];
if (!object.Equals(dr["B"], drc["B"])) {
(dr["B"] as Item).SetChanged();
}
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
SolidColorBrush b = new SolidColorBrush(Colors.Black);
var item = (Item)value;
if (item.IsChanged) {
b = Brushes.Red;
}
return b;
}
<DataTemplate x:Key="changedBColumn" >
<TextBlock Text="{Binding [B], Mode=OneWay}"
Foreground="{Binding [B],Converter={StaticResource MyObjectToForegroundConverter},
Mode=OneWay}"
/>
</DataTemplate>
现在,您的数据表应按如下方式创建:
public class Item {
public string Value {get;set;}
public Item(string value){
Value = value;
}
public bool IsChanged { get; private set;}
public void SetChanged(){
IsChanged = true;
}
public override string ToString(){
return Value;
}
public override bool Equals(object other){
var item = other as Item;
if(item == null) return false;
return item.Value == Value;
}
public override int GetHashCode(){
if(Value == null) return base.GetHashCode();
return Value.GetHashCode();
}
}
propDataTable = new DataTable();
propDataTable.Columns.Add("A", typeof(Item));
propDataTable.Columns.Add("B", typeof(Item));
DataRow row0 = propDataTable.NewRow();
DataRow row1 = propDataTable.NewRow();
row0[0] = new Item("A0");
row0[1] = new Item("B0");
row1[0] = new Item("A1");
row1[1] = new Item("B1");
propDataTable.Rows.Add(row0);
propDataTable.Rows.Add(row1);
propCopyDataTable = propDataTable.Copy();
//now set a different value in propCopyDataTable
propCopyDataTable.Rows[1][1] = new Item("Changed");
//find out which cells in column B are different
//try to show in red text which cell changed
for (int i = 0; i < propDataTable.Rows.Count; i++) {
DataRow dr = propDataTable.Rows[i];
DataRow drc = propCopyDataTable.Rows[i];
if (!object.Equals(dr["B"], drc["B"])) {
(dr["B"] as Item).SetChanged();
}
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
SolidColorBrush b = new SolidColorBrush(Colors.Black);
var item = (Item)value;
if (item.IsChanged) {
b = Brushes.Red;
}
return b;
}
<DataTemplate x:Key="changedBColumn" >
<TextBlock Text="{Binding [B], Mode=OneWay}"
Foreground="{Binding [B],Converter={StaticResource MyObjectToForegroundConverter},
Mode=OneWay}"
/>
</DataTemplate>
XAML也应该这样修改:
public class Item {
public string Value {get;set;}
public Item(string value){
Value = value;
}
public bool IsChanged { get; private set;}
public void SetChanged(){
IsChanged = true;
}
public override string ToString(){
return Value;
}
public override bool Equals(object other){
var item = other as Item;
if(item == null) return false;
return item.Value == Value;
}
public override int GetHashCode(){
if(Value == null) return base.GetHashCode();
return Value.GetHashCode();
}
}
propDataTable = new DataTable();
propDataTable.Columns.Add("A", typeof(Item));
propDataTable.Columns.Add("B", typeof(Item));
DataRow row0 = propDataTable.NewRow();
DataRow row1 = propDataTable.NewRow();
row0[0] = new Item("A0");
row0[1] = new Item("B0");
row1[0] = new Item("A1");
row1[1] = new Item("B1");
propDataTable.Rows.Add(row0);
propDataTable.Rows.Add(row1);
propCopyDataTable = propDataTable.Copy();
//now set a different value in propCopyDataTable
propCopyDataTable.Rows[1][1] = new Item("Changed");
//find out which cells in column B are different
//try to show in red text which cell changed
for (int i = 0; i < propDataTable.Rows.Count; i++) {
DataRow dr = propDataTable.Rows[i];
DataRow drc = propCopyDataTable.Rows[i];
if (!object.Equals(dr["B"], drc["B"])) {
(dr["B"] as Item).SetChanged();
}
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
SolidColorBrush b = new SolidColorBrush(Colors.Black);
var item = (Item)value;
if (item.IsChanged) {
b = Brushes.Red;
}
return b;
}
<DataTemplate x:Key="changedBColumn" >
<TextBlock Text="{Binding [B], Mode=OneWay}"
Foreground="{Binding [B],Converter={StaticResource MyObjectToForegroundConverter},
Mode=OneWay}"
/>
</DataTemplate>
注意:除非要将DataTemplate中的控件绑定到主视图模型,否则不需要使用代理。但事实上这是错误的。实际上,您需要将控件绑定到这里的是每个数据行。在每个单元格模板中,隐式的DataContext
实际上是一个DataRowView
。因此,不需要显式设置源代码。只要把它正常地绑起来就行了。在上面的XAML中,我使用路径[B]
,这意味着直接传入带有字符串键B
的索引器[]
,这相当于调用someDataRowView[“B”]
。此外,转换器将传入值作为项
(非布尔值)。每次更改单元格的值时,都需要将其设置为新的项
,不要简单地更改项.value
属性,因为它不支持INotifyPropertyChanged
。虽然在本例中,看起来您只是想测试它
如果要测试代理技术,请尝试创建另一个示例。事实上,当它成功地将所有单元格绑定到通过代理传入的主视图模型的bB
属性时,技术确实在原始代码中起作用。感谢这一易于理解的解释。它工作得很好!