Hier ist eine Problemumgehung für das Binden von Spalten im DataGrid. Da die Columns-Eigenschaft ReadOnly ist, habe ich, wie jeder bemerkt hat, eine Attached-Eigenschaft namens BindableColumns erstellt, die die Columns im DataGrid jedes Mal aktualisiert, wenn sich die Auflistung durch das CollectionChanged-Ereignis ändert.
Wenn wir diese Sammlung von DataGridColumns haben
public ObservableCollection<DataGridColumn> ColumnCollection
{
get;
private set;
}
Dann können wir BindableColumns wie folgt an die ColumnCollection binden
<DataGrid Name="dataGrid"
local:DataGridColumnsBehavior.BindableColumns="{Binding ColumnCollection}"
AutoGenerateColumns="False"
...>
Die angehängte Eigenschaft 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);
}
}
CollectionChanged
Ereignis der Spaltensammlung, heben jedoch die Registrierung auf. Auf diese WeiseDataGrid
bleibt das so lange am Leben, wie das Ansichtsmodell existiert, auch wenn die Steuerungsvorlage, die dasDataGrid
ursprünglich enthielt, inzwischen ersetzt wurde. Gibt es eine garantierte Möglichkeit, die Registrierung dieses Ereignishandlers erneut aufzuheben, wenn derDataGrid
nicht mehr benötigt wird?dataGrid.Columns.Add(column)
DataGridColumn mit dem Header 'X' angezeigt, die bereits in der Columns-Auflistung eines DataGrid vorhanden ist. DataGrids können keine Spalten gemeinsam nutzen und keine doppelten Spalteninstanzen enthalten.Ich habe meine Forschung fortgesetzt und keinen vernünftigen Weg gefunden, dies zu tun. Die Columns-Eigenschaft im DataGrid kann ich nicht binden, sondern ist schreibgeschützt.
Bryan schlug vor, dass etwas mit AutoGenerateColumns gemacht werden könnte, also habe ich es mir angesehen. Es verwendet eine einfache .NET-Reflexion, um die Eigenschaften der Objekte in ItemsSource zu überprüfen, und generiert für jedes Objekt eine Spalte. Vielleicht könnte ich einen Typ im laufenden Betrieb mit einer Eigenschaft für jede Spalte generieren, aber dies gerät aus der Bahn.
Da dieses Problem so leicht im Code gelöst werden kann, bleibe ich bei einer einfachen Erweiterungsmethode, die ich aufrufe, wenn der Datenkontext mit neuen Spalten aktualisiert wird:
quelle
Ich habe einen Blog-Artikel von Deborah Kurata mit einem schönen Trick gefunden, wie man eine variable Anzahl von Spalten in einem DataGrid anzeigt:
Auffüllen eines DataGrid mit dynamischen Spalten in einer Silverlight-Anwendung mit MVVM
Grundsätzlich erstellt sie ein
DataGridTemplateColumn
und fügt einItemsControl
, das mehrere Spalten anzeigt.quelle
Ich habe es geschafft, eine Spalte dynamisch mit nur einer Codezeile wie der folgenden hinzuzufügen:
In Bezug auf die Frage handelt es sich weder um eine XAML-basierte Lösung (da es, wie erwähnt, keinen vernünftigen Weg gibt), noch um eine Lösung, die direkt mit DataGrid.Columns funktioniert. Es arbeitet tatsächlich mit DataGrid-gebundener ItemsSource, die ITypedList implementiert und als solche benutzerdefinierte Methoden für das Abrufen von PropertyDescriptor bereitstellt. An einer Stelle im Code können Sie "Datenzeilen" und "Datenspalten" für Ihr Raster definieren.
Wenn Sie hätten:
Sie könnten zum Beispiel verwenden:
und Ihr Raster, das die Bindung an MyItemsCollection verwendet, wird mit entsprechenden Spalten gefüllt. Diese Spalten können zur Laufzeit dynamisch geändert (neu hinzugefügt oder vorhanden entfernt) werden, und das Raster aktualisiert automatisch die Spaltensammlung.
Der oben erwähnte DynamicPropertyDescriptor ist nur ein Upgrade auf den regulären PropertyDescriptor und bietet eine stark typisierte Spaltendefinition mit einigen zusätzlichen Optionen. DynamicDataGridSource würde ansonsten mit dem grundlegenden PropertyDescriptor einwandfrei funktionieren.
quelle
Es wurde eine Version der akzeptierten Antwort erstellt, die das Abbestellen behandelt.
quelle
Sie können eine Benutzersteuerung mit der Rasterdefinition erstellen und untergeordnete Steuerelemente mit verschiedenen Spaltendefinitionen in xaml definieren. Das übergeordnete Element benötigt eine Abhängigkeitseigenschaft für Spalten und eine Methode zum Laden der Spalten:
Elternteil:
Kind Xaml:
Und schließlich ist der schwierige Teil, herauszufinden, wo man 'LoadGrid' aufruft.
Ich habe
InitalizeComponent
Probleme damit, aber ich habe Dinge zum Laufen gebracht, indem ich in meinem Fensterkonstruktor nach aufgerufen habe (childGrid ist x: name in window.xaml):Verwandter Blogeintrag
quelle
Möglicherweise können Sie dies mit AutoGenerateColumns und einer DataTemplate tun. Ich bin mir nicht sicher, ob es ohne viel Arbeit funktionieren würde, man müsste damit herumspielen. Ehrlich gesagt, wenn Sie bereits eine funktionierende Lösung haben, würde ich die Änderung noch nicht vornehmen, es sei denn, es gibt einen großen Grund. Das DataGrid-Steuerelement wird sehr gut, aber es muss noch einige Arbeit geleistet werden (und ich muss noch viel lernen), um solche dynamischen Aufgaben problemlos ausführen zu können.
quelle
Es gibt ein Beispiel für meine programmatische Vorgehensweise:
quelle