2017-02-11 1 views
0

J'ai besoin de modifier DataGrid de sorte que chaque en-tête de colonne ait une zone de texte, une liste déroulante et une case à cocher, et tous doivent être liés à une propriété. J'ai passé beaucoup de temps à essayer de l'implémenter avec un DataGrid par défaut, mais je ne pense pas que ce soit possible, j'ai donc décidé de créer un DataGrid personnalisé.Comment lier des colonnes DataGrid personnalisées

Jusqu'à présent, j'ai une propriété de liaison BindableColumns qui stocke DataTable, donc j'ai les données que j'ai besoin d'afficher. Le problème est que je ne sais pas comment passer ces données à OnAutoGeneratedColumns, donc je peux ajouter des colonnes à la propriété Columns de DataGrid.

public class BindableGrid : DataGrid 
{ 
    public DataTable BindableColumns 
    { 
     get { return (DataTable)GetValue(BindableColumnsProperty); } 
     set { SetValue(BindableColumnsProperty, value); } 
    } 

    public static readonly DependencyProperty BindableColumnsProperty = 
     DependencyProperty.Register("BindableColumns", typeof(DataTable), typeof(BindableGrid), new PropertyMetadata(null, BindableColumnsPropertyChanged)); 

    private static void BindableColumnsPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) 
    { 
     // This is where I get DataTable after binding 
    } 


    protected override void OnAutoGeneratedColumns(EventArgs e) 
    { 
     // This is where I need the DataTable to generate columns. 
     // I don't know how to invoke this method myself. 
     Columns.Add(new DataGridTemplateColumn 
     { 
      Header = "Test1", 
      HeaderTemplate = new DataTemplate() 
     }); 
    } 
} 

Et XAML:

<controls:BindableGrid ItemsSource="{Binding Data}" BindableColumns="{Binding Data}" AutoGenerateColumns="True"> 
</controls:BindableGrid> 

EDIT:

Merci à @Ramin je colonnes de travail. J'ai eu quelques problèmes avec l'ajout dynamique de lignes, car DataGrid attend des lignes en tant que classe avec exactement les mêmes noms de variables que les liaisons de la colonne. Pour toute personne ayant des problèmes, voici comment je l'ai résolu:

for (var rowIndex = 0; rowIndex < data.Rows.Count; rowIndex++) 
{ 
    dynamic row = new ExpandoObject(); 

    for (var i = 0; i < data.Columns.Count; i++) 
    // Create variables named after bindings, and assign values 
     ((IDictionary<string, object>)row)[data.Columns[i].ColumnName.Replace(' ', '_')] = data.Rows[rowIndex].ItemArray[i]; 

    // Add row to DataGrid 
    dg.Items.Add(row); 
} 

Répondre

1

En ce qui concerne la source du problème, voici un DataGrid avec un en-tête basé sur un modèle qui contient une zone de texte, ComboBox et CheckBox:

<DataGrid > 
     <DataGrid.Columns> 
      <DataGridTextColumn Binding="{Binding}" > 
       <DataGridTextColumn.HeaderTemplate> 
        <DataTemplate> 
         <StackPanel Orientation="Horizontal"> 
          <TextBox Text="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=DataGrid}}" /> 
          <CheckBox IsChecked="{Binding DataContext.Value , RelativeSource={RelativeSource AncestorType=DataGrid}}"/> 
          <ComboBox ItemsSource="{Binding DataContext.Names , RelativeSource={RelativeSource AncestorType=DataGrid}}" SelectedIndex="0"/> 
         </StackPanel> 
        </DataTemplate> 
       </DataGridTextColumn.HeaderTemplate> 
      </DataGridTextColumn> 
     </DataGrid.Columns> 
    </DataGrid> 

le DataContext utilise une classe personnalisée:

public MainWindow() 
{ 
    InitializeComponent(); 
    DataContext = new MyClass() { Name = "Name0", Value = true, Names = new string[2] { "Name1", "Name2" } }; 
} 

public class MyClass 
{ 
    public string Name { get; set; } 
    public bool Value { get; set; } 
    public string[] Names { get; set; } 
} 

EDIT

vous pouvez ajouter dynamiquement des colonnes:

public void addNewColumn(Header h, string bindcol) 
    { 
     DataGridColumn col = new DataGridTextColumn(){Binding=new Binding(bindcol)}; 
     col.Header = h; 
     col.HeaderTemplate = (DataTemplate)FindResource("dgh") as DataTemplate; 
     dg.Columns.Add(col); 
    } 

dans App.xaml:

<Application.Resources> 
    <DataTemplate x:Key="dgh"> 
     <StackPanel Orientation="Horizontal"> 
      <TextBox Text="{Binding Name}" /> 
      <CheckBox IsChecked="{Binding Value}"/> 
      <ComboBox ItemsSource="{Binding Names}" SelectedIndex="0"/> 
     </StackPanel> 
    </DataTemplate> 
</Application.Resources> 

à tester (en supposant qu'il existe un DataGrid avec le nom dg):

public MainWindow() 
    { 
     InitializeComponent(); 
     var h1 = new Header() 
     { 
      Name = "Name0", 
      Value = true, 
      Names = new string[2] { "Name1", "Name2" } 
     }; 
     var h2 = new Header() 
     { 
      Name = "Name1", 
      Value = true, 
      Names = new string[2] { "Name12", "Name22" } 
     }; 

     addNewColumn(h1, "col1"); 
     addNewColumn(h2, "col2"); 

    } 

    public class Header 
    { 
     public string Name { get; set; } 
     public bool Value { get; set; } 
     public string[] Names { get; set; } 
    } 

Notez que "col1" et "col2" font référence à ItemsSource de la grille de données.

+0

Est-ce que cela fonctionnera pour un tableau de colonnes? Parce que je ne sais pas combien de colonnes je vais avoir dans mon 'DataTable'. Je laisse l'utilisateur charger les fichiers csv qui sont convertis en 'DataTable', donc le nombre de colonnes est différent pour chaque fichier. – FCin

+0

voir la partie modifiée. – Ron

+0

Je l'ai presque réussi à travailler, mais je ne peux pas ajouter dynamiquement des lignes, à cause de la reliure. 'dg.Items.Add();' nécessite un objet avec des propriétés nommées comme les bindings, mais en C# je ne peux pas créer dynamiquement des classes. – FCin