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.
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
é umaObservableCollection
deEmployee
, porque pretendo que sejam feitas notificações quando um ou mais empregados são adicionados/removidos da lista de empregados. Caso tivesse optado porList<Employee>
,Collection<Employee>
ouIEnumerable<Employee>
essas notificações não aconteceriam. Na imagem anterior é possível analisar o que é umaObservableCollection
, sendo a implementação das interfacesINotifyCollectionChanged
eINotifyPropertyChanged
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:
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>
- 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: