2015-10-18 19 views
0

如何以紅色文本顯示DataTable中單元格的值,當它與另一個DataTable中的單元格不同時?在最終的應用程序中,表格將通過更改csv文件生成。所以我必須替換自動生成的列。我知道你需要一個DataGridTemplateColumn,將CellTemplate設置爲資源。然而,這個替換的列然後不是視覺樹的一部分,因此綁定不起作用。在紅色的數據網格上顯示數據表中的紅色已更改的文本WPF

文章How to bind to data when the DataContext is not inherited顯示實現Freezable對象的轉換器應解決此問題。試圖找出這個解決方案,我做了下一個簡化的例子。但它顯示在發生變化的列的所有單元的單元的最後的值,並且都在紅:

enter image description here

視圖模型:

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; 
       } 
      } 
     } 
    } 
} 

的ObjectToForegroundConverter:

using System; 
using System.Globalization; 
using System.Windows; 
using System.Windows.Data; 
using System.Windows.Media; 

namespace WpfApplication1 
{ 
    [ValueConversion(typeof(object), typeof(SolidColorBrush))] 
    public class ObjectToForegroundConverter : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      SolidColorBrush b = new SolidColorBrush(Colors.Black); 
      try 
      { 
       bool changedValue = (bool)value; 
       if (changedValue) 
       { 
        b = Brushes.Red; 
       } 
      } 
      catch (Exception e) 
      { 
       MessageBox.Show(string.Format("Error: {0}", e));//instance not set to a etc. 
      } 

      return b; 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      throw new NotImplementedException(); 
     } 
    } 
} 

The BindingProxy converter:

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; 
        } 
      } 
     } 
    } 
} 

回答

0

這裏的問題是每個單元需要一個布爾值,指示改變的狀態。但是你的視圖模型中只有1個屬性。這意味着同一列中的所有單元格將具有相同的狀態,具體取決於該屬性。這就解釋了爲什麼列B中的所有單元格都是紅色的原因,因爲屬性bB設置爲true,並綁定到列B中的所有單元格。

您需要的物品類(而不是僅僅簡單的字符串值),以保持狀態,這樣的事情:

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(); 
    } 
} 

現在你DataTable應該這樣創建:

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; 
} 

The X AML也應修改如下:

<DataTemplate x:Key="changedBColumn" > 
     <TextBlock Text="{Binding [B], Mode=OneWay}" 
     Foreground="{Binding [B],Converter={StaticResource MyObjectToForegroundConverter}, 
        Mode=OneWay}" 
     /> 
</DataTemplate> 

注:除非要在DataTemplate中的控件綁定到主視圖模型,您不需要使用代理。但實際上這是錯誤的。實際上你需要將控件綁定到這裏的是每個數據行。在每個單元格模板中,隱含的DataContext實際上是一個DataRowView。所以你不需要明確地設置Source。通常綁定它。在上面的XAML中,我使用路徑[B]表示索引器[]與字符串鍵B直接傳入 - 它相當於調用someDataRowView["B"]。此外,轉換器的傳入值爲Item(不是bool)。每次更改單元格的值時,都需要將其設置爲新的Item,不要簡單地更改Item.Value屬性,因爲它不支持INotifyPropertyChanged。雖然在這種情況下看起來像你只是想測試它。

如果要測試代理技術,請嘗試創建另一個示例。實際上,該技術確實在您的原始代碼中工作時,它將所有單元格成功綁定到通過代理傳入的主視圖模型的bB屬性。

+0

感謝您的理解。它工作得很好! –

相關問題