Aquí hay una solución para las columnas de enlace en DataGrid. Como la propiedad Columns es ReadOnly, como todos notaron, hice una propiedad adjunta llamada BindableColumns que actualiza las columnas en el DataGrid cada vez que la colección cambia a través del evento CollectionChanged.
Si tenemos esta colección de DataGridColumn's
public ObservableCollection<DataGridColumn> ColumnCollection
{
get;
private set;
}
Entonces podemos vincular BindableColumns a ColumnCollection de esta manera
<DataGrid Name="dataGrid"
local:DataGridColumnsBehavior.BindableColumns="{Binding ColumnCollection}"
AutoGenerateColumns="False"
...>
La propiedad adjunta 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
evento de la colección de columnas, sin embargo, nunca lo anula. De esa manera,DataGrid
se mantendrá vivo mientras exista el modelo de vista, incluso si la plantilla de control que contenía elDataGrid
en primer lugar ha sido reemplazada mientras tanto. ¿Hay alguna forma garantizada de cancelar el registro de ese controlador de eventos nuevamente cuandoDataGrid
ya no se requiere?dataGrid.Columns.Add(column)
DataGridColumn con el encabezado 'X' ya existe en la colección Columns de una DataGrid. DataGrids no puede compartir columnas y no puede contener instancias de columnas duplicadas.He continuado mi investigación y no he encontrado ninguna forma razonable de hacerlo. La propiedad Columnas en DataGrid no es algo con lo que pueda vincularme, de hecho, es de solo lectura.
Bryan sugirió que se podría hacer algo con AutoGenerateColumns, así que eché un vistazo. Utiliza la reflexión simple .Net para observar las propiedades de los objetos en ItemsSource y genera una columna para cada uno. Tal vez podría generar un tipo sobre la marcha con una propiedad para cada columna, pero esto se está desviando.
Dado que este problema se soluciona tan fácilmente en el código, seguiré con un método de extensión simple al que llamo cuando el contexto de datos se actualiza con nuevas columnas:
fuente
Encontré un artículo de blog de Deborah Kurata con un buen truco sobre cómo mostrar un número variable de columnas en un DataGrid:
Rellenar una cuadrícula de datos con columnas dinámicas en una aplicación de Silverlight usando MVVM
Básicamente, crea un
DataGridTemplateColumn
y poneItemsControl
dentro que muestra varias columnas.fuente
Logré hacer posible agregar dinámicamente una columna usando solo una línea de código como esta:
Con respecto a la pregunta, esta no es una solución basada en XAML (ya que, como se mencionó, no hay una forma razonable de hacerlo), tampoco es una solución que funcione directamente con DataGrid.Columns. Realmente opera con DataGrid enlazado ItemsSource, que implementa ITypedList y, como tal, proporciona métodos personalizados para la recuperación de PropertyDescriptor. En un lugar en el código puede definir "filas de datos" y "columnas de datos" para su cuadrícula.
Si tuvieras:
podrías usar por ejemplo:
y la cuadrícula que usa el enlace a MyItemsCollection se completará con las columnas correspondientes. Esas columnas se pueden modificar (nuevas agregadas o eliminadas existentes) en tiempo de ejecución dinámicamente y la cuadrícula actualizará automáticamente su colección de columnas.
DynamicPropertyDescriptor mencionado anteriormente es solo una actualización del PropertyDescriptor normal y proporciona una definición de columnas fuertemente tipada con algunas opciones adicionales. DynamicDataGridSource de lo contrario funcionaría muy bien con un PropertyDescriptor básico.
fuente
Hizo una versión de la respuesta aceptada que maneja la cancelación de la suscripción.
fuente
Puede crear un control de usuario con la definición de cuadrícula y definir controles 'secundarios' con definiciones de columna variadas en xaml. El padre necesita una propiedad de dependencia para las columnas y un método para cargar las columnas:
Padre:
Niño Xaml:
Y finalmente, la parte difícil es encontrar dónde llamar a 'LoadGrid'.
Estoy luchando con esto, pero hice que las cosas funcionen llamando después
InitalizeComponent
en mi constructor de ventanas (childGrid es x: nombre en window.xaml):Entrada de blog relacionada
fuente
Es posible que pueda hacer esto con AutoGenerateColumns y un DataTemplate. No estoy seguro si funcionaría sin mucho trabajo, tendrías que jugar con eso. Honestamente, si ya tienes una solución que funcione, no haría el cambio todavía a menos que haya una gran razón. El control DataGrid se está volviendo muy bueno, pero aún necesita algo de trabajo (y me queda mucho por aprender) para poder realizar tareas dinámicas como esta fácilmente.
fuente
Hay una muestra de la forma en que lo hago programáticamente:
fuente