Voici une solution de contournement pour la liaison de colonnes dans le DataGrid. Puisque la propriété Columns est ReadOnly, comme tout le monde l'a remarqué, j'ai créé une propriété attachée appelée BindableColumns qui met à jour les colonnes dans le DataGrid chaque fois que la collection change via l'événement CollectionChanged.
Si nous avons cette collection de DataGridColumn
public ObservableCollection<DataGridColumn> ColumnCollection
{
get;
private set;
}
Ensuite, nous pouvons lier BindableColumns à ColumnCollection comme ceci
<DataGrid Name="dataGrid"
local:DataGridColumnsBehavior.BindableColumns="{Binding ColumnCollection}"
AutoGenerateColumns="False"
...>
La propriété attachée BindableColumns
public class DataGridColumnsBehavior
{
public static readonly DependencyProperty BindableColumnsProperty =
DependencyProperty.RegisterAttached("BindableColumns",
typeof(ObservableCollection<DataGridColumn>),
typeof(DataGridColumnsBehavior),
new UIPropertyMetadata(null, BindableColumnsPropertyChanged));
private static void BindableColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
DataGrid dataGrid = source as DataGrid;
ObservableCollection<DataGridColumn> columns = e.NewValue as ObservableCollection<DataGridColumn>;
dataGrid.Columns.Clear();
if (columns == null)
{
return;
}
foreach (DataGridColumn column in columns)
{
dataGrid.Columns.Add(column);
}
columns.CollectionChanged += (sender, e2) =>
{
NotifyCollectionChangedEventArgs ne = e2 as NotifyCollectionChangedEventArgs;
if (ne.Action == NotifyCollectionChangedAction.Reset)
{
dataGrid.Columns.Clear();
foreach (DataGridColumn column in ne.NewItems)
{
dataGrid.Columns.Add(column);
}
}
else if (ne.Action == NotifyCollectionChangedAction.Add)
{
foreach (DataGridColumn column in ne.NewItems)
{
dataGrid.Columns.Add(column);
}
}
else if (ne.Action == NotifyCollectionChangedAction.Move)
{
dataGrid.Columns.Move(ne.OldStartingIndex, ne.NewStartingIndex);
}
else if (ne.Action == NotifyCollectionChangedAction.Remove)
{
foreach (DataGridColumn column in ne.OldItems)
{
dataGrid.Columns.Remove(column);
}
}
else if (ne.Action == NotifyCollectionChangedAction.Replace)
{
dataGrid.Columns[ne.NewStartingIndex] = ne.NewItems[0] as DataGridColumn;
}
};
}
public static void SetBindableColumns(DependencyObject element, ObservableCollection<DataGridColumn> value)
{
element.SetValue(BindableColumnsProperty, value);
}
public static ObservableCollection<DataGridColumn> GetBindableColumns(DependencyObject element)
{
return (ObservableCollection<DataGridColumn>)element.GetValue(BindableColumnsProperty);
}
}
Fredrik Hedblad
la source
CollectionChanged
événement de la collection de colonnes, mais vous ne l'annulez jamais. De cette façon, leDataGrid
sera maintenu en vie aussi longtemps que le modèle de vue existe, même si le modèle de contrôle qui contenait leDataGrid
en premier lieu a été remplacé entre-temps. Existe-t-il un moyen garanti de désinscrire à nouveau ce gestionnaire d'événements lorsque leDataGrid
n'est plus nécessaire?dataGrid.Columns.Add(column)
DataGridColumn avec l'en-tête «X» déjà existant dans la collection Columns d'un DataGrid. DataGrids ne peut pas partager de colonnes et ne peut pas contenir des instances de colonne en double.J'ai poursuivi mes recherches et je n'ai trouvé aucun moyen raisonnable de le faire. La propriété Columns sur DataGrid n'est pas quelque chose que je peux lier, en fait, elle est en lecture seule.
Bryan a suggéré que quelque chose pourrait être fait avec AutoGenerateColumns alors j'ai jeté un coup d'œil. Il utilise une simple réflexion .Net pour examiner les propriétés des objets dans ItemsSource et génère une colonne pour chacun. Peut-être que je pourrais générer un type à la volée avec une propriété pour chaque colonne, mais cela est en train de déraper.
Étant donné que ce problème est si facilement résolu dans le code, je m'en tiendrai à une méthode d'extension simple que j'appelle chaque fois que le contexte de données est mis à jour avec de nouvelles colonnes:
la source
J'ai trouvé un article de blog de Deborah Kurata avec une belle astuce pour afficher un nombre variable de colonnes dans un DataGrid:
Remplissage d'un DataGrid avec des colonnes dynamiques dans une application Silverlight à l'aide de MVVM
Fondamentalement, elle crée un
DataGridTemplateColumn
et met à l'ItemsControl
intérieur qui affiche plusieurs colonnes.la source
J'ai réussi à rendre possible l'ajout dynamique d'une colonne en utilisant juste une ligne de code comme celle-ci:
En ce qui concerne la question, il ne s'agit pas d'une solution basée sur XAML (car comme mentionné, il n'y a pas de moyen raisonnable de le faire), ni d'une solution qui fonctionnerait directement avec DataGrid.Columns. Il fonctionne en fait avec ItemsSource liés à DataGrid, qui implémente ITypedList et en tant que tel fournit des méthodes personnalisées pour la récupération de PropertyDescriptor. En un seul endroit du code, vous pouvez définir des «lignes de données» et des «colonnes de données» pour votre grille.
Si vous aviez:
vous pouvez utiliser par exemple:
et votre grille utilisant la liaison à MyItemsCollection serait remplie avec les colonnes correspondantes. Ces colonnes peuvent être modifiées (ajoutées ou supprimées existantes) au moment de l'exécution de manière dynamique et la grille actualisera automatiquement sa collection de colonnes.
DynamicPropertyDescriptor mentionné ci-dessus est juste une mise à niveau vers PropertyDescriptor standard et fournit une définition de colonnes fortement typées avec quelques options supplémentaires. DynamicDataGridSource fonctionnerait autrement très bien avec PropertyDescriptor de base.
la source
Réalisation d'une version de la réponse acceptée qui gère la désinscription.
la source
Vous pouvez créer un contrôle utilisateur avec la définition de la grille et définir des contrôles «enfants» avec des définitions de colonne variées en xaml. Le parent a besoin d'une propriété de dépendance pour les colonnes et d'une méthode pour charger les colonnes:
Parent:
Enfant Xaml:
Et enfin, la partie délicate est de trouver où appeler «LoadGrid».
J'ai du mal avec cela mais j'ai obtenu que les choses fonctionnent en appelant après
InitalizeComponent
dans mon constructeur de fenêtre (childGrid est x: nom dans window.xaml):Entrée de blog associée
la source
Vous pourrez peut-être le faire avec AutoGenerateColumns et un DataTemplate. Je ne suis pas sûr que cela fonctionne sans beaucoup de travail, il faudrait jouer avec. Honnêtement, si vous avez déjà une solution qui fonctionne, je ne ferais pas encore le changement à moins qu'il n'y ait une grande raison. Le contrôle DataGrid devient très bon, mais il a encore besoin de travail (et il me reste beaucoup à apprendre) pour pouvoir effectuer facilement des tâches dynamiques comme celle-ci.
la source
Il existe un exemple de la façon dont je fais par programmation:
la source