Aqui está uma solução alternativa para vincular colunas no DataGrid. Como a propriedade Columns é ReadOnly, como todo mundo notou, criei uma propriedade Attached chamada BindableColumns que atualiza as colunas no DataGrid sempre que a coleção é alterada pelo evento CollectionChanged.
Se tivermos essa coleção de DataGridColumn's
public ObservableCollection<DataGridColumn> ColumnCollection
{
get;
private set;
}
Em seguida, podemos vincular BindableColumns ao ColumnCollection assim
<DataGrid Name="dataGrid"
local:DataGridColumnsBehavior.BindableColumns="{Binding ColumnCollection}"
AutoGenerateColumns="False"
...>
A propriedade anexada 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 da coleção de colunas, mas nunca o cancela o registro. Dessa forma,DataGrid
ele será mantido vivo enquanto o modelo de exibição existir, mesmo que o modelo de controle que continhaDataGrid
o primeiro tenha sido substituído enquanto isso. Existe alguma maneira garantida de cancelar o registro desse manipulador de eventos novamente quandoDataGrid
não for mais necessário?dataGrid.Columns.Add(column)
DataGridColumn com o cabeçalho 'X' já existente na coleção Columns de um DataGrid. O DataGrids não pode compartilhar colunas e não pode conter instâncias de coluna duplicadas.Continuei minha pesquisa e não encontrei nenhuma maneira razoável de fazer isso. A propriedade Columns no DataGrid não é algo contra o qual possa me vincular; na verdade, é somente leitura.
Bryan sugeriu que algo poderia ser feito com o AutoGenerateColumns, então eu dei uma olhada. Ele usa reflexão .Net simples para examinar as propriedades dos objetos no ItemsSource e gera uma coluna para cada um. Talvez eu possa gerar um tipo em tempo real com uma propriedade para cada coluna, mas isso está saindo do caminho.
Como esse problema é tão facilmente resolvido no código, continuarei com um método de extensão simples que chamo sempre que o contexto dos dados for atualizado com novas colunas:
fonte
Eu encontrei um artigo de blog de Deborah Kurata com um bom truque como mostrar o número variável de colunas em um DataGrid:
Preenchendo um DataGrid com Colunas Dinâmicas em um Aplicativo Silverlight usando MVVM
Basicamente, ela cria um
DataGridTemplateColumn
e colocaItemsControl
dentro que exibe várias colunas.fonte
Consegui tornar possível adicionar dinamicamente uma coluna usando apenas uma linha de código como esta:
Em relação à pergunta, essa não é uma solução baseada em XAML (já que, como mencionado, não há uma maneira razoável de fazê-lo), nem é uma solução que funcionaria diretamente com o DataGrid.Columns. Na verdade, ele opera com o ItemsSource ligado ao DataGrid, que implementa ITypedList e, como tal, fornece métodos personalizados para a recuperação do PropertyDescriptor. Em um lugar no código, você pode definir "linhas de dados" e "colunas de dados" para sua grade.
Se você tivesse:
você poderia usar por exemplo:
e sua grade usando a ligação a MyItemsCollection seria preenchida com as colunas correspondentes. Essas colunas podem ser modificadas (novas adicionadas ou removidas) em tempo de execução dinamicamente e a grade atualizará automaticamente sua coleção de colunas.
O DynamicPropertyDescriptor mencionado acima é apenas uma atualização para o PropertyDescriptor regular e fornece uma definição de colunas fortemente tipada com algumas opções adicionais. Caso contrário, o DynamicDataGridSource funcionaria bem com o PropertyDescriptor básico.
fonte
Criou uma versão da resposta aceita que lida com a desinscrição.
fonte
Você pode criar um controle de usuário com a definição de grade e definir controles 'filho' com definições de coluna variadas no xaml. O pai precisa de uma propriedade de dependência para colunas e um método para carregar as colunas:
Pai:
Xaml filho:
E, finalmente, a parte complicada é encontrar onde chamar 'LoadGrid'.
Estou lutando com isso, mas consegui que as coisas funcionassem chamando depois
InitalizeComponent
no meu construtor de janelas (childGrid é x: name em window.xaml):Entrada de blog relacionada
fonte
Você pode fazer isso com AutoGenerateColumns e um DataTemplate. Não tenho certeza se funcionaria sem muito trabalho, você teria que brincar com isso. Honestamente, se você já tem uma solução funcional, eu não faria a alteração ainda, a menos que haja um grande motivo. O controle DataGrid está ficando muito bom, mas ainda precisa de algum trabalho (e ainda tenho muito aprendizado a fazer) para poder executar tarefas dinâmicas como essa facilmente.
fonte
Há uma amostra da maneira que eu faço programaticamente:
fonte