Oto obejście dla Binding Columns w DataGrid. Ponieważ właściwość Columns jest ReadOnly, jak wszyscy zauważyli, utworzyłem Attached Property o nazwie BindableColumns, która aktualizuje kolumny w DataGrid za każdym razem, gdy kolekcja zmienia się za pośrednictwem zdarzenia CollectionChanged.
Jeśli mamy tę kolekcję DataGridColumn
public ObservableCollection<DataGridColumn> ColumnCollection
{
get;
private set;
}
Następnie możemy powiązać BindableColumns z ColumnCollection w ten sposób
<DataGrid Name="dataGrid"
local:DataGridColumnsBehavior.BindableColumns="{Binding ColumnCollection}"
AutoGenerateColumns="False"
...>
Dołączona właściwość 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
zdarzeniem kolekcji kolumn, jednak nigdy jej nie wyrejestrujesz. W ten sposóbDataGrid
zostanie utrzymany przy życiu tak długo, jak istnieje model widoku, nawet jeśli szablon kontrolny, który zawierałDataGrid
w pierwszej kolejności, został w międzyczasie zastąpiony. Czy istnieje gwarantowany sposób ponownego wyrejestrowania tego programu obsługi zdarzeń, gdyDataGrid
nie jest on już wymagany?dataGrid.Columns.Add(column)
DataGridColumn z nagłówkiem „X” już istnieje w kolekcji Columns elementu DataGrid. DataGrids nie może współużytkować kolumn i nie może zawierać zduplikowanych wystąpień kolumn.Kontynuowałem moje badania i nie znalazłem żadnego rozsądnego sposobu, aby to zrobić. Właściwość Columns w DataGrid nie jest czymś, z czym mogę się powiązać, w rzeczywistości jest tylko do odczytu.
Bryan zasugerował, że można coś zrobić z AutoGenerateColumns, więc rzuciłem okiem. Używa prostej refleksji .Net, aby przyjrzeć się właściwościom obiektów w ItemsSource i generuje kolumnę dla każdego z nich. Być może mógłbym wygenerować typ w locie z właściwością dla każdej kolumny, ale to jest zbyteczne.
Ponieważ ten problem jest tak łatwy do rozwiązania w kodzie, będę trzymać się prostej metody rozszerzenia, którą wywołuję za każdym razem, gdy kontekst danych zostanie zaktualizowany o nowe kolumny:
źródło
Znalazłem artykuł na blogu Deborah Kurata z niezłą sztuczką pokazującą zmienną liczbę kolumn w DataGrid:
Wypełnianie DataGrid z dynamicznymi kolumnami w aplikacji Silverlight przy użyciu MVVM
Zasadniczo tworzy
DataGridTemplateColumn
i umieszczaItemsControl
wewnątrz, który wyświetla wiele kolumn.źródło
Udało mi się umożliwić dynamiczne dodawanie kolumny za pomocą zaledwie linii kodu takiego:
Wracając do pytania, nie jest to rozwiązanie oparte na XAML (ponieważ jak wspomniano, nie ma na to rozsądnego sposobu), ani też nie jest to rozwiązanie, które działałoby bezpośrednio z DataGrid.Columns. W rzeczywistości działa z komponentem ItemsSource powiązanym z DataGrid, który implementuje ITypedList i jako taki zapewnia niestandardowe metody pobierania PropertyDescriptor. W jednym miejscu w kodzie możesz zdefiniować „wiersze danych” i „kolumny danych” dla swojej siatki.
Jeśli chciałbyś:
możesz użyć na przykład:
a Twoja siatka przy użyciu powiązania z MyItemsCollection zostanie wypełniona odpowiednimi kolumnami. Te kolumny można dynamicznie modyfikować (nowe dodane lub istniejące usunięte) w czasie wykonywania, a grid automatycznie odświeży swoją kolekcję kolumn.
Wspomniany powyżej DynamicPropertyDescriptor to tylko uaktualnienie do zwykłego PropertyDescriptor i zapewnia definicję kolumn o silnym typie z kilkoma dodatkowymi opcjami. W przeciwnym razie DynamicDataGridSource działałoby po prostu dobrze zdarzenie z podstawowym PropertyDescriptor.
źródło
Stworzono wersję zaakceptowanej odpowiedzi, która obsługuje anulowanie subskrypcji.
źródło
Możesz utworzyć kontrolę użytkownika z definicją siatki i zdefiniować kontrolki „podrzędne” z różnymi definicjami kolumn w XAML. Element nadrzędny potrzebuje właściwości zależności dla kolumn i metody ładowania kolumn:
Rodzic:
Dziecko Xaml:
I wreszcie, najtrudniejsza część polega na znalezieniu, gdzie wywołać „LoadGrid”. Zmagam
się z tym, ale coś działało, wywołując after
InitalizeComponent
w moim konstruktorze okna (childGrid to x: name in window.xaml):Powiązany wpis na blogu
źródło
Możesz to zrobić za pomocą AutoGenerateColumns i DataTemplate. Nie jestem pewien, czy to działałoby bez dużego nakładu pracy, musiałbyś się tym bawić. Szczerze mówiąc, jeśli masz już działające rozwiązanie, nie wprowadziłbym jeszcze zmiany, chyba że jest duży powód. Formant DataGrid staje się bardzo dobry, ale nadal wymaga trochę pracy (i zostało mi dużo do zrobienia), aby móc łatwo wykonywać dynamiczne zadania, takie jak ta.
źródło
Oto przykład sposobu, w jaki robię to programowo:
źródło