Datagrid em Windows Presentation Foundation

Exemplos práticos

Passemos agora aos exemplos práticos.

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.

WPF Datagrids: classes de exemplo

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 empregados 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 responsá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:

WPF Datagrids: Exemplo 1

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)

WPF Datagrids: Exemplo 2

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:

WPF Datagrids: Exemplo 3

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>
  • Code Behind:
private void WindowLoaded(object sender, RoutedEventArgs e) {
  var employees = new Employees();
  // add the employee list
  dataGridEmployees.ItemsSource = employees;
}
  • Resultado:

WPF Datagrids: Exemplo 4

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:

WPF Datagrids: Exemplo 5