2014-09-03 61 views
5

我有一個DataTable與DataSource作爲ItemsSource。列數隨時間不同而不同。如果列的DataType屬於類A,我想使用DataTemplate來自定義單元格內容的外觀。WPF DataGrid - 數據綁定到DataTable單元格在CellTemplates DataTemplate

我已經在DataGrid上設置

AutoGenerateColumns="True" 

,這樣在數據表中的所有列將產生。

我更換一個DataGridTemplateColumn的DataGridColumn如果數據類型是A型

private void DataGrid_AutoGeneratingColumn(object sender, system.Windows.Controls.DataGridAutoGeneratingColumnEventArgs e) 
{ 
    if (e.PropertyType == typeof(A)) 
    { 
     e.Column = new DataGridTemplateColumn 
     { 
      CellTemplate = (DataTemplate)Resources["ATemplate"], 
      Header = e.Column.Header, 
      HeaderTemplate = e.Column.HeaderTemplate, 
      HeaderStringFormat = e.Column.HeaderStringFormat 
     }; 
    } 
} 

的DataTemplate中看起來是這樣的。

<DataTemplate x:Key="ATemplate"> 
    <RadioButton Content="{Binding Name}" GroupName="{Binding GroupName}" IsChecked="{Binding IsSelected}" /> 
</DataTemplate> 

的單選按鈕顯示,但我得到綁定錯誤的所有屬性,如

BindingExpression path error: 'IsSelected' property not found on 'object' ''DataRowView' 

A類看起來像這樣

public class A 
{ 
    public string Name { get; set; } 
    public string GroupName { get; set; } 
    public bool IsSelected { get; set; } 
} 

我怎麼能數據綁定的DataTemplate中向右細胞和財產?

(如果你有,我沒有使用DataGrid_AutoGeneratingColumn這將是一個偉大的解決方案MVVM)

編輯

我已經沒有運氣嘗試這種解決方案了。當單元格不知道如何渲染類時,只會像平常一樣在單元格中顯示類名。

<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding Items}"> 
    <DataGrid.Resources> 
     <DataTemplate DataType="{x:Type viewModel:A}"> 
     <RadioButton Content="{Binding Path=Name}" GroupName="{Binding Path=GroupName}" IsChecked="{Binding Path=IsSelected}" /> 
     </DataTemplate> 
    </DataGrid.Resources> 
</DataGrid> 
+0

對不起,我的壞...那不行。 – Sheridan 2014-09-03 13:06:17

回答

8

模板中的綁定不起作用,因爲DataContext是來自DataTable的DataRowView。

一種解決方案是更改模板以將DataContext設置爲所需的對象(類型A),然後所有綁定都可以工作(Name,GroupName,IsSelected)。要做到這一點,你需要製作一個轉換器並讓你的模板使用它。

模板中的DataContext綁定到它傳遞到轉換器的DataGridCell祖先。從單元格中,我們可以得到DataContext(DataRowView),我們可以得到單元格的列。當我們在DataGrid_AutoGeneratingColumn中創建列時,我們將列的SortMemberPath設置爲e.PropertyName(數據表中的列名)。在轉換器中,我們使用SortMemberPath作爲索引來查找DataRowView.Row中的對象。我們將其作爲模板的DataContext返回。

這裏是A類和B類的實現。我在數據表中添加了每個類的兩列,以表明它可以與多個實例一起工作。

enter image description here

MainWindow.xaml:

<Window x:Class="WpfApplication17.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:viewModel="clr-namespace:WpfApplication17" 
     Title="MainWindow" Height="350" Width="525"> 
    <Window.Resources> 
     <viewModel:DataRowViewConverter x:Key="drvc" /> 
     <DataTemplate x:Key="ATemplate"> 
      <RadioButton DataContext="{Binding RelativeSource={RelativeSource AncestorType=DataGridCell}, Converter={StaticResource drvc}}" Content="{Binding Path=Name}" GroupName="{Binding Path=GroupName}" IsChecked="{Binding Path=IsSelected}" /> 
     </DataTemplate> 
     <DataTemplate x:Key="BTemplate"> 
      <CheckBox DataContext="{Binding RelativeSource={RelativeSource AncestorType=DataGridCell}, Converter={StaticResource drvc}}" Content="{Binding Path=FullName}" IsChecked="{Binding Path=IsChecked}" /> 
     </DataTemplate> 
    </Window.Resources> 
    <Grid> 
     <DataGrid AutoGenerateColumns="True" ItemsSource="{Binding Items}" AutoGeneratingColumn="DataGrid_AutoGeneratingColumn" CanUserAddRows="False"> 
     </DataGrid> 
    </Grid> 
</Window> 

MainWindow.xaml。cs:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Navigation; 
using System.Windows.Shapes; 

namespace WpfApplication17 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public System.Data.DataTable Items { get; set; } 

     public MainWindow() 
     { 
      InitializeComponent(); 

      System.Data.DataTable dt = new System.Data.DataTable(); 
      dt.Columns.Add("StringColumn", typeof(string)); 
      dt.Columns.Add("IntColumn", typeof(int)); 
      dt.Columns.Add("AColumn1", typeof(A)); 
      dt.Columns.Add("AColumn2", typeof(A)); 
      dt.Columns.Add("BColumn1", typeof(B)); 
      dt.Columns.Add("BColumn2", typeof(B)); 

      dt.Rows.Add(
       "TestString", 
       123, 
       new A() { Name = "A1", GroupName = "GroupName", IsSelected = true }, 
       new A() { Name = "A2", GroupName = "GroupName", IsSelected = false }, 
       new B() { FullName = "B1", IsChecked=true }, 
       new B() { FullName = "B2", IsChecked=false } 
      ); 

      Items = dt; 
      this.DataContext = this; 
     } 

     private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) 
     { 
      DataTemplate dt = null; 
      if (e.PropertyType == typeof(A)) 
       dt = (DataTemplate)Resources["ATemplate"]; 
      else if (e.PropertyType == typeof(B)) 
       dt = (DataTemplate)Resources["BTemplate"]; 

      if (dt != null) 
      { 
       DataGridTemplateColumn c = new DataGridTemplateColumn() 
       { 
        CellTemplate = dt, 
        Header = e.Column.Header, 
        HeaderTemplate = e.Column.HeaderTemplate, 
        HeaderStringFormat = e.Column.HeaderStringFormat, 
        SortMemberPath = e.PropertyName // this is used to index into the DataRowView so it MUST be the property's name (for this implementation anyways) 
       }; 
       e.Column = c; 
      } 
     } 
    } 

    public class A 
    { 
     public string Name { get; set; } 
     public string GroupName { get; set; } 
     public bool IsSelected { get; set; } 
    } 

    public class B 
    { 
     public string FullName { get; set; } 
     public bool IsChecked { get; set; } 
    } 

    public class DataRowViewConverter : IValueConverter 
    { 
     #region IValueConverter Members 

     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      DataGridCell cell = value as DataGridCell; 
      if (cell == null) 
       return null; 

      System.Data.DataRowView drv = cell.DataContext as System.Data.DataRowView; 
      if (drv == null) 
       return null; 

      return drv.Row[cell.Column.SortMemberPath]; 
     } 

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

     #endregion 
    } 
} 
+2

我曾嘗試過每一次編寫,但DataTemplate中的DataContext綁定。謝謝你太多了! – AxdorphCoder 2014-09-04 08:12:24

+0

我認爲綁定到DataGridCell將導致內存泄漏(由於靜態PropertyChangedTracker)。綁定到非INotifyPropertyChanged源將導致泄漏。 – ThumbGen 2016-11-29 12:28:32

+0

我該如何去用C#編程生成一個DataTemplate? – mpsyp 2017-09-07 18:46:24

相關問題