C# 在datagrid上以datatable WPF中的红色更改文本显示

C# 在datagrid上以datatable WPF中的红色更改文本显示,c#,wpf,xaml,datagrid,datatable,C#,Wpf,Xaml,Datagrid,Datatable,当数据表中的某个单元格与另一个数据表中的同一单元格不同时,如何以红色文本显示该单元格的值?在最终的应用程序中,表格将通过更改csv文件生成。所以我必须替换自动生成的列。我知道您需要一个DataGridTemplateColumn,将CellTemplate设置为资源。然而,这个被替换的列不是可视化树的一部分,因此绑定不起作用 本文展示了一个实现Freezable对象的转换器应该解决这个问题。为了找出这个解决方案,我做了下一个简化的例子。但它会在发生更改的列的所有单元格中显示该单元格的最后一个值,

当数据表中的某个单元格与另一个数据表中的同一单元格不同时,如何以红色文本显示该单元格的值?在最终的应用程序中,表格将通过更改csv文件生成。所以我必须替换自动生成的列。我知道您需要一个DataGridTemplateColumn,将CellTemplate设置为资源。然而,这个被替换的列不是可视化树的一部分,因此绑定不起作用

本文展示了一个实现Freezable对象的转换器应该解决这个问题。为了找出这个解决方案,我做了下一个简化的例子。但它会在发生更改的列的所有单元格中显示该单元格的最后一个值,并且全部显示为红色:

ViewModel:

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
属性时,技术确实在原始代码中起作用。

感谢这一易于理解的解释。它工作得很好!