A classe BindingList
O primeiro problema na nossa implementação é o fato de termos utilizado a classe “List” para criarmos a nossa coleção de produtos. A classe “List” não foi desenvolvida com o intuito de ser utilizada em data binding. Porém, não se preocupe. Para isso nós temos a classe “BindingList“, que é justamente uma “List” que suporta data binding!
Se trocarmos a declaração da nossa coleção de produtos por uma BindingList, nós teremos um resultado um pouco melhor:
1
2
| // C# private System.ComponentModel.BindingList<Produto> _produtos = new System.ComponentModel.BindingList<Produto>(); |
1
2
| ' VB.NET Private Produtos As New System.ComponentModel.BindingList(Of Produto) |
Veja que o texto do primeiro produto foi alterado de imediato. Além disso, o novo produto que inserimos na lista em tempo de execução também foi exibido com sucesso. Porém, nós ainda temos um probleminha a ser resolvido. Se nós continuarmos inserindo novas linhas no grid, chega uma hora em que o grid para de atualizar o nome do primeiro produto:
Isso acontece porque a nossa classe “Produto” não implementa a interface “INofifyPropertyChanged“.
A interface INotifyPropertyChanged
A interface INotifyPropertyChanged é utilizada para que o mecanismo de data binding seja notificado quando o valor de uma propriedade for alterado. Nas classes internas do .NET (nos controles do Windows Forms, por exemplo), isso tudo já está implementado nativamente. Porém, se criarmos novas classes no nosso projeto (como foi o caso da classe “Produto” que criamos nesse exemplo), nós temos que implementar essa interface caso a classe venha a ser utilizada em algum data binding.
Essa não é uma particularidade do Windows Forms. Muito pelo contrário, essa interface é essencial também no famoso data binding do WPF. Se não implementarmos essa interface nas nossas classes, nós teremos esse mesmo problema no WPF.
Uns tempos atrás eu escrevi um artigo falando das interfaces de notificação de alteração no WPF. Hoje eu só vou mostrar como implementá-las nesse exemplo, e não vou entrar em muitos detalhes sobre elas. Caso você queira aprender mais sobre esse assunto, confira o artigo que eu acabei de mencionar.
A implementação da interface INotifyPropertyChanged é muito tranquila. Nós só temos que disparar o evento “NotifyPropertyChanged” em todos os “setters” das nossas propriedades (que agora não poderão ser auto-implementadas):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| // C# public class Produto : System.ComponentModel.INotifyPropertyChanged { private int _id; public int Id { get { return _id; } set { _id = value; OnPropertyChanged( "Id" ); } } private string _nome; public string Nome { get { return _nome; } set { _nome = value; OnPropertyChanged( "Nome" ); } } private decimal _valorUnitario; public decimal ValorUnitario { get { return _valorUnitario; } set { _valorUnitario = value; OnPropertyChanged( "ValorUnitario" ); } } public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged( string propertyName) { if (PropertyChanged != null ) { PropertyChanged( this , new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| ' VB.NET Public Class Produto Implements System.ComponentModel.INotifyPropertyChanged Private _id As Integer Public Property Id As Integer Get Return _id End Get Set (value As Integer ) _id = value OnPropertyChanged( "Id" ) End Set End Property Private _nome As String Public Property Nome As String Get Return _nome End Get Set (value As String ) _nome = value OnPropertyChanged( "Nome" ) End Set End Property Private _valorUnitario As Decimal Public Property ValorUnitario As Decimal Get Return _valorUnitario End Get Set (value As Decimal ) _valorUnitario = value OnPropertyChanged( "ValorUnitario" ) End Set End Property Public Event PropertyChanged As System.ComponentModel.PropertyChangedEventHandler Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged Private Sub OnPropertyChanged( ByVal PropertyName As String ) RaiseEvent PropertyChanged( Me , New System.ComponentModel.PropertyChangedEventArgs(PropertyName)) End Sub End Class |
Se testarmos o comportamento do grid mais uma vez depois dessa alteração, nós veremos que agora ele se comportará corretamente:
Como você deve ter notado, a implementação da interface INotifyPropertyChanged pode acabar sendo um pouco “maçante“. Imagine termos que implementar todo esse código em todas as nossas classes de modelo? Vai dar um certo trabalho. É pensando nisso que a Microsoft vem investindo em novas funcionalidades no C# e VB.NET para facilitar a nossa vida. Eu já escrevi uns tempos atrás sobre o atributo CallerMemberName, que facilita um pouco esse processo. Se pesquisarmos na internet, encontraremos maneiras mais fáceis de implementarmos essa interface, como esta que eu encontrei no StackOverflow:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| // C# public class Produto : System.ComponentModel.INotifyPropertyChanged { private int _id; public int Id { get { return _id; } set { SetField( ref _id, value); } } private string _nome; public string Nome { get { return _nome; } set { SetField( ref _nome, value); } } private decimal _valorUnitario; public decimal ValorUnitario { get { return _valorUnitario; } set { SetField( ref _valorUnitario, value); } } protected bool SetField<T>( ref T field, T value, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = null ) { if (EqualityComparer<T>.Default.Equals(field, value)) return false ; field = value; OnPropertyChanged(propertyName); return true ; } public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged( string propertyName) { if (PropertyChanged != null ) { PropertyChanged( this , new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| ' VB.NET Public Class Produto Implements System.ComponentModel.INotifyPropertyChanged Private _id As Integer Public Property Id As Integer Get Return _id End Get Set (value As Integer ) SetField(_id, value) End Set End Property Private _nome As String Public Property Nome As String Get Return _nome End Get Set (value As String ) SetField(_nome, value) End Set End Property Private _valorUnitario As Decimal Public Property ValorUnitario As Decimal Get Return _valorUnitario End Get Set (value As Decimal ) SetField(_valorUnitario, value) End Set End Property Protected Function SetField(Of T)( ByRef field As T, value As T, <System.Runtime.CompilerServices.CallerMemberName> Optional propertyName As String = Nothing ) As Boolean If EqualityComparer(Of T).[ Default ].Equals(field, value) Then Return False End If field = value OnPropertyChanged(propertyName) Return True End Function Public Event PropertyChanged As System.ComponentModel.PropertyChangedEventHandler Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged Private Sub OnPropertyChanged( ByVal PropertyName As String ) RaiseEvent PropertyChanged( Me , New System.ComponentModel.PropertyChangedEventArgs(PropertyName)) End Sub End Class |
Continua na parte 3
Nenhum comentário:
Postar um comentário