Ferramentas de Usuário

Ferramentas de Site


revistaprogramar_arquivo:28_edicao:wpf_datagrids_na_pratica

WPF - Datagrids na Prática

Datagrid em Windows Presentation Foundation

Neste artigo pretendo apresentar a Datagrid em Windows Presentation Foundation (WPF) na .Net Framework 4.0. Vou começar por uma breve apresentação teórica e em seguida irei apresentar vários exemplos. De salientar que não terei em conta Design Patterns.

A DataGrid é um controlo que permite apresentar dados, representando cada linha um item de uma lista de objectos do mesmo tipo e as colunas representam as várias características do objecto. Ou seja, se na instância da datagrid apresento uma lista de empregados, cada linha representa um empregado e cada coluna representa uma propriedade do empregado.

A classe DataGrid está incluída no namespace System.Windows.Controls e é um selector que permite seleccionar mais do que um item ao mesmo tempo e tem por base a classe ItemsControl, que é um Controlo e que implementa a interface IAddChild. A seguinte imagem mostra-nos esta hierarquia de classes.

Vejamos agora algumas propriedades, métodos e eventos relevantes da DataGrid.

As propriedades mais relevantes são:

  • Name: permite definir o nome.
  • Foreground: permite definir a cor da letra.
  • Background: permite definir a cor de fundo.
  • AlternatingRowBackground; AlternationIndex: permitem definir a cor de fundo de cada linha, de forma alternada.
  • Item numa Lista Não OrdenadaWidth; Height; MaxHeight; MinWidth; MaxWidth; MinHeight: permitem definir a largura, altura e seus valores mínimos e máximo. ActualHeight; ActualWidth permitem obter qual é o valor actual da altura e da largura.
  • ColumnWidth; MaxColumnWidth; MinColumnWidth; ColumnHeaderHeight; permitem definir a largura, largura máxima, largura mínima e a altura do cabeçalho da coluna.
  • RowHeight; MinRowHeight: permitem definir a altura e altura mínima da linha.
  • GridLinesVisibility: permite definir a visibilidade das linhas que delimitam as linhas e colunas. Caso sejam visíveis as propriedades HorizontalGridLinesBrush e VerticalGridLinesBrush permitem definir aparência das linhas.
  • SelectionMode; SelectionUnit permitem definir o modo de selecção dos itens, ou seja, se é possível seleccionar mais de que um item e se é permitido seleccionar linha(s) completa(s) ou célula(s).
  • AutoGenerateColumns: permite gerar automáticas as colunas da datagrid. Para a geração automática é considerada todas as propriedades do tipo de objecto em causa, ou no caso de se estar a usar um dataTable a geração é baseada nas colunas da dataTable.
  • Columns: permite obter a colecção de colunas.
  • CellStyle: permite definir o estilo da célula.
  • ColumnHeaderStyle: permite definir o estilo do cabeçalho da coluna.
  • CanUserAddRows: permite que o utilizado adicione novos itens.
  • CanUserDeleteRows: permite que o utilizado apague itens.
  • CanUserReorderColumns permite que o utilizador organize as colunas.
  • CanUserResizeColumns: permite que o utilizador redimensione as colunas.
  • CanUserResizeRows: permite que o utilizador redimensione as linhas.
  • CanUserSortColumns: permite que o utilizador ordene os itens ao fazer duplo clique no cabeçalho da coluna.
  • CurrentColumn: permite obter a coluna actual.
  • CurrentCell: permite obter a célula actual.
  • CurrentItem: permite saber o item actual.
  • SelectedCells: permite saber quais as células que estão seleccionadas.
  • SelectedIndex: permite saber qual o índex do item seleccionado.
  • SelectedItem: permite obter o item que está seleccionado.
  • SelectedItems: permitem saber os itens que estão seleccionados.
  • HasItems: permite saber se a datagrid contém itens.
  • Items: permite obter a lista de itens.
  • ItemsSource: permite obter a lista de itens.
  • ItemTemplate; ItemTemplateSelector: permitem definir o template aplicar aos itens.
Nota: Existem diferenças entre a propriedade Items e a propriedade ItemsSource e apenas se pode usar uma delas. Quando se usa a propriedade ItemsSource, não é possível adicionar ou remover itens através desta propriedade ou através da propriedade Items. No entanto, se atribuirmos uma ObservableCollection ao ItemsSource, essa questão é ultrapassada.

Os métodos mais relevantes são:

  • BeginEdit: permite invocar o comando BeginEditCommand, que será responsável por alterar para modo de edição a linha ou célula seleccionada.
  • CommitEdit: permite invocar o comando CommitEditCommand para célula ou linha que está em modo de edição.
  • CancelEdit:permite invocar o comando CancelEditCommand para célula ou linha que está em modo de edição.

Os eventos mais relevantes são:

  • BeginningEdit: ocorre antes da célula ou linha entrar em modo de edição.
  • PreparingCellForEdit: ocorre quando a célula entra em modo de edição.
  • CellEditEnding: ocorre antes de haver um “commit” da célula ou antes de ser cancelada a edição.
  • RowEditEnding: ocorre antes de haver um “commit” da linha ou antes de ser cancelada a edição.
  • SelectionChanged: ocorre quando a selecção é alterada.
  • CurrentCellChanged: ocorre quando o valor da propriedade CurrentCell é alterado.
  • SelectedCellsChanged: ocorre quando o valor da propriedade SelectedCells é alterado.
Nota: O leitor que pretenda efectuar um estudo mais pormenorizado sobre as propriedades, métodos e eventos da DataGrid, deve consultar a página DataGrid Class no MSDN: http://bit.ly/ewIdaP

Antes de passarmos aos exemplos práticos, vejamos os vários tipos de colunas da DataGrid.

Em WPF, podemos definir os seguintes tipos de colunas:

  • DataGridTextColumn – permite apresentar e editar texto;
  • DataGridCheckBoxColumn – permite atribuir um valor lógico (verdadeiro / falso)
  • DataGridComboBoxColumn – permite seleccionar uma opção de um conjunto de opções;
  • DataGridHyperlinkColumn – permite adicionar uma hiperligação;
  • DataGridTemplateColumn – permite definir o template da coluna, isto é, podemos customizar a coluna.

O seguinte diagrama, permite-nos perceber melhor qual a classe de base de cada tipo de coluna.

Exemplos práticos

Passemos agora aos exemplos práticos.

Nota: De apoio a este artigo criei uma demo, o leitor poderá obter em: http://bit.ly/efSP1W

Suponhamos que temos um objecto empregado (Employee) e um conjunto de empregados (Employees). O empregado, tem como principais características: código, nome, se é o patrão, salário, taxas e aniversário.

Notas:

• A classe Employee implementa a interface INotifyPropertyChanged para que sejam feitas notificações quando alguma propriedade é alterada. Isto é em parte responsável pela actualização da informação na Datagrid.

• A classe Employees é uma ObservableCollection de Employee, porque pretendo que sejam feitas notificações quando um ou mais empregrados são adicionados/removidos da lista de empregados. Caso tivesse optado por List<Employee>, Collection<Employee> ou IEnumerable<Employee> essas notificações não aconteceriam. Na imagem anterior é possível analisar o que é uma ObservableCollection, sendo a implementação das interfaces INotifyCollectionChanged e INotifyPropertyChanged reponsável

1º Exemplo:

Objectivo: Apresentar numa janela uma Datagrid com uma lista de empregados, as colunas da Datagrid devem ser geradas automaticamente:

  • XAML
<DataGrid Name="dataGridEmployees"
Grid.Column="1" Grid.ColumnSpan="3"
Grid.Row="2"
AutoGenerateColumns="True"/>
  • Code Behind:

Usando a propriedade ItemsSource para atribuir a lista de empregados.

private void WindowLoaded(object sender, RoutedEventArgs e){
var employees=new Employees();
// add the employee list
//using ItemsSource
dataGridEmployees.ItemsSource = employees;

Usando a propriedade Items para atribuir a lista de empregados.

private void WindowLoaded(object sender, RoutedEventArgs e){
var employees=new Employees();
// add the employee list
//using Items
foreach (var employee in employees)
dataGridEmployees.Items.Add(employee);
}
  • Resultado:

2º Exemplo:

Objectivo: Apresentar numa janela uma Datagrid com uma lista de empregados, as colunas da Datagrid não devem ser geradas automaticamente e apenas se pretende visualizar as colunas relativas ao nome, se é patrão, salário e taxas.

  • XAML:
<DataGrid Name="dataGridEmployees"
Grid.Column="1" Grid.ColumnSpan="3"
Grid.Row="2"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn
Header="Nome" MinWidth="200"
Binding="{Binding Name}" />
<DataGridTextColumn
Header="Salário" Width="100" MaxWidth="150"
Binding="{Binding Salary}"/>
<DataGridTextColumn
Header="Taxa" Width="50" Binding="{BindingRates}"/>
<DataGridCheckBoxColumn
Header="É patrão" Binding="{BindingIsBoss}"/>
</DataGrid.Columns>
</DataGrid>
Notas: • Através do objecto Binding da coluna é definido qual a propriedade do objecto que se vai apresentar. • Foram definidas as medidas para a largura, largura mínima e largura máxima das várias colunas.
  • Code Behind:
private void WindowLoaded(object sender, RoutedEventArgs e) {
var employees = new Employees();
// add the employee list
dataGridEmployees.ItemsSource = employees;
}
  • Resultado:

(A imagem com o resultado pode ser visto na coluna do lado direito em cima de todo)

3º Exemplo:

Objectivo: Formatar a apresentação do salário e das taxas, ou seja, um salário com valor 0 (zero) deve apresentar o valor “Não está atribuído” e os salários que estejam definidos devem apresentar o símbolo da moeda € (euro). As Taxas devem apresentar o valor da taxa com o símbolo de percentagem.

Neste exemplo, temos necessidade de criar duas classes, cada classe representa o conversor que será usado no objecto de Binding da coluna. O conversor será responsável por transformar o valor original no valor que é pretendido apresentar. Ambas as classes implementam a interface IValueConverter.

Portanto, o conversor para o salário será definido da seguinte forma:

[ValueConversion(typeof(double), typeof(string))]
public class SalaryValueConverter:IValueConverter{
private const string NotDefinedValue = "Não está definido";
private const string EuroSymbol = "€";
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if (value == null || (double.Parse(value.ToString()) == 0))
return NotDefinedValue;
return string.Format("{0} {1}",value, EuroSymbol);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if (value is string && value.ToString().Equals(NotDefinedValue))
return 0;
return value is string && value.ToString().Contains(EuroSymbol)?
double.Parse(value.ToString().Replace(EuroSymbol, string.Empty)) : value;
}
}

O conversor para a taxa será definido da seguinte forma:

[ValueConversion(typeof(int),typeof(string))]
public class
RatesValueConverter:IValueConverter{
private const string PercentageSymbol = "%";
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture){
if (value is int && int.Parse(value.ToString()) == 0)
return string.Empty;
return string.Format("{0} {1}", value, PercentageSymbol);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture){
throw new NotImplementedException();
}
}
  • XAML:

É necessário adicionar o namespace dos resources

xmlns:Converters="clrnamespace:WPFDatagridImplementation.Converters"

Nos resources da janela, inicializamos os dois conversores

<Window.Resources>
<Converters:RatesValueConverterx:Key="ratesValueConverter"/>
<Converters:SalaryValueConverterx:Key="salaryValueConverter"/>
</Window.Resources>

É necessário alterar o Binding das colunas, é neste objecto que definimos o conversor aplicar.

<DataGrid Name="dataGridEmployees"
Grid.Column="1" Grid.ColumnSpan="3"
Grid.Row="2"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn
Header="Nome" MinWidth="200"
Binding="{Binding Name}" />
<DataGridTextColumn
Header="Salário" Width="100" MaxWidth="150"
Binding="{BindingPath=Salary,Converter={StaticResourcesalaryValueConverter}}"/>
<DataGridTextColumn Header="Taxa" Width="50" Binding="{BindingPath=Rates,Converter={StaticResourceratesValueConverter}}"/>
<DataGridCheckBoxColumnHeader="É patrão" Binding="{BindingIsBoss}"/>
</DataGrid.Columns>
</DataGrid>
  • Code Behind:
private void WindowLoaded(object sender, RoutedEventArgs e) {
var employees = new Employees();
// add the employee list
dataGridEmployees.ItemsSource = employees;
}
  • Resultado:

4º Exemplo:

Objectivo: Apresentar uma coluna extra, que será o resultado do produto do salário com a taxa associada.

É necessário criar um conversor, que receba o salário e a taxa, e devolva o resultado final, formado. Neste caso, como é preciso receber dois valores a classe terá que implementar a interface IMultiValueConverter.

public class FinalySalaryConverter:IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
double salary = 0.0;
int rates = 0;
if (values[0] is double)
salary = double.Parse(values[0].ToString());
if (values[1] is int)
rates = int.Parse(values[1].ToString());
if (salary == 0 && rates == 0)
return string.Empty;
return string.Format("{0} €", salary - salary * rates / 100);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
Nota: O objecto values que está definido no argumento do método Convert, recebe os valores de acordo com a ordem que for definido no multibinding que será definido na coluna.
  • XAML:
<Window.Resources>
<Converters:RatesValueConverter x:Key="ratesValueConverter"/>
<Converters:SalaryValueConverter x:Key="salaryValueConverter"/>
<Converters:FinalySalaryConverter x:Key="finalySalaryConverter"/>
</Window.Resources>

Adicionamos a nova coluna com o nome “Salário Final”, repare-se que usou-se um Multibinding, para permitir enviar o valor do salário e o valor da taxa.

<DataGrid.Columns>
<DataGridTextColumn Header="Nome"
MinWidth="200" Binding="{Binding Name}" />
<DataGridTextColumn Header="Salário"
Width="100" MaxWidth="150" Binding="{BindingPath=Salary,Converter={StaticResourcesalaryValueConverter}}"/>
<DataGridTextColumn Header="Taxa" Width="50" Binding="{BindingPath=Rates,Converter={StaticResourceratesValueConverter}}"/>
<DataGridTextColumn Header="SalárioFinal">
<DataGridTextColumn.Binding>
<MultiBindingConverter="{StaticResourcefinalySalaryConverter}">
<BindingPath="Salary"/>
<Binding Path="Rates"/>
</MultiBinding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<DataGridCheckBoxColumn Header="Épatrão" Binding="{Binding IsBoss}"/>
</DataGrid.Columns>
</DataGrid>
  • Code Behind:
private void WindowLoaded(object sender, RoutedEventArgs e) {
var employees = new Employees();
// add the employee list
dataGridEmployees.ItemsSource = employees;
}
  • Resultado:

5ºExemplo:

Objectivo: Apresentar a coluna de Aniversário, recorrendo ao controlo DataPicker.

  • XAML:
<DataGridTemplateColumn Header="Aniversário"
MinWidth="100">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<DatePicker
SelectedDate="{Binding Birthday}"
SelectedDateFormat="Short" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Birthday, StringFormat=d}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Nota: Apenas apresento o XAML da coluna criada. Foram definidos dois templates: um para o caso em que se está em modo de edição, em que usou aplicar um DatePicker. O outro template apenas apresenta a data.
  • Resultado:

6º Exemplo:

Objectivo: Apresentar os empregados ordenados por Nome.

  • Code Behind:
private void WindowLoaded(object sender, RoutedEventArgs e) {
var employees = new Employees();
dataGridEmployees.ItemsSource = employees;
ICollectionView view = CollectionViewSource.GetDefaultView(dataGridEmployees.ItemsSource);
view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
}
  • Resultado:

7º Exemplo:

Objectivo: Filtrar os empregados com salário superior a 500.00€.

  • Code Behind:
private void WindowLoaded(object sender, RoutedEventArgs e) {
var employees = new Employees();
dataGridEmployees.ItemsSource =employees;
ICollectionView view = CollectionViewSource.GetDefaultView(dataGridEmployees.ItemsSource);
view.Filter = new Predicate<object>(EmployeesWithSalaryGreaterThan500);
}
private bool EmployeesWithSalaryGreaterThan500(object param) {
var employee = (Employee)param;
if (employee.Salary > 500)
return true;
return false;
}
  • Resultado:

8º Exemplo:

Objectivo: As linhas da datagrid devem apresentar uma cor alternada.

  • XAML:
<DataGrid Name="dataGridEmployees"
Grid.Column="1" Grid.ColumnSpan="3"
Grid.Row="2"
AutoGenerateColumns="True"
AlternatingRowBackground="LightGray"
AlternationCount="2"/>
private void WindowLoaded(object sender, RoutedEventArgs e) {
var employees = new Employees();
dataGridEmployees.ItemsSource = employees;
}
  • Resultado:

9º Exemplo:

Objectivo: Alterar a altura do cabeçalho das colunas, alterar o tipo de fonte da letra e tamanho e cor. Alterar a cor das linhas horizontais e verticais da datagrid.

  • XAML:

Nos resources da Window, definimos o estilo que iremos aplicar às células.

<Window.Resources>
<Style x:Key="columnStyle"
TargetType="DataGridColumnHeader">
<Setter Property="FontFamily"
Value="Arial Black" />
<Setter Property="FontSize"
Value="14"/>
<Setter Property="Foreground"
Value="Red"/>
</Style>
</Window.Resources>
<DataGrid Name="dataGridEmployees"
Grid.Column="1" Grid.ColumnSpan="3"
Grid.Row="2"
AutoGenerateColumns="True"
ColumnHeaderStyle="{DynamicResourcecolumnStyle}"
ColumnHeaderHeight="40"
GridLinesVisibility="All"
HorizontalGridLinesBrush="Red"
VerticalGridLinesBrush="Green"/>
  • Code Behind:
private void WindowLoaded(object sender, RoutedEventArgs e){
var employees = new Employees();
dataGridEmployees.ItemsSource = employees;
}
  • Resultado:

10º Exemplo:

Objectivo: Ao seleccionar uma célula e mostrar uma mensagem de aviso de que foi detectada uma alteração, apresentando o nome do empregado seleccionado e o nome da coluna seleccionada.

  • XAML:
<DataGrid Name="dataGridEmployees"
Grid.Column="1" Grid.ColumnSpan="3"
Grid.Row="2"
AutoGenerateColumns="True"
SelectionMode="Single"
SelectionUnit="Cell"
CurrentCellChanged="DataGridEmployeesCurrentCellChanged"/>
  • Code Behind:
private void WindowLoaded(object sender, RoutedEventArgs e)
{
var employees = new Employees();
dataGridEmployees.ItemsSource = employees;
}
private void DataGridEmployeesCurrentCellChanged(objectsender, System.EventArgs e) {
Employee selectedEmployee = (Employee)dataGridEmployees.CurrentItem;
string message = String.Format("Foi seleccionada a coluna '{0}' do empregado(a) {1}.", dataGridEmployees.CurrentCell.Column.Header,selectedEmployee.Name);
MessageBox.Show(message);
}
  • Resultado:

Conclusão

Demo do artigo: http://bit.ly/efSP1W

Contém:

  • Diagrama de Classes da hierarquia das colunas
  • Diagrama de classes da hierarquia da Datagrid
  • Diagrama de classes do modelo usado no demo
  • Interfaces
  • Classes de dados
  • Conversores
  • Janela principal com menu para os vários exemplos
  • Janelas dos vários exemplos

Referências:

Link para o artigo: http://tinyurl.com/RPED28-05

Autor

Escrito por Sara Silva

É licenciada em Matemática – Especialidade em Computação, pela Universidade de Coimbra, actualmente é Software Developer no Porto. O entusiasmo pela área resultou na obtenção dos títulos de Microsoft Certified Profissional Developer – Windows 3.5, Microsoft Certified Tecnology Specialist – WPF 3.5, WPF 4 e Windows Forms. Faz parte de várias comunidades, tendo uma participação activa na Comunidade NetPonto e no P@P.

http://tinyurl.com/RPED28-05

revistaprogramar_arquivo/28_edicao/wpf_datagrids_na_pratica.txt · Última modificação em: 2018/05/14 21:37 (edição externa)