2015-08-14 2 views
0

У меня есть List(T), который связан с некоторыми элементами управления, DataGridView только для чтения, ComboBox и несколькими ярлыками. Это прекрасно работает, все элементы управления заполнены правильно, когда загружается форма, все изменения в Label.Text и DataGridView изменяются по мере изменения выбора ComboBox.Обновление контроля привязки данных при изменении данных

Но если я изменяю данные в объекте в Списке, данные, отображаемые в элементах управления, не обновляются, чтобы отражать измененные данные.

Мой класс T реализует интерфейс INotifyChanged, а режим обновления привязок данных управления меткой установлен на OnPropertychanged.

Я могу заставить DataGridView обновить, вызвав его метод Refresh(), но попытка же для ярлыков кажется неэффективной.

Как я могу внести изменения в данные в объектах моего списка, обновить данные, указанные в элементах управления Label? Я сделал что-то не так?

Мой MRE до сих пор:

Class Form1 
    ' Form1 has a DataGridView, a ComboBox, a Label, a Button and a TextBox 
    Dim FooList As New List(Of Foo)(3) 

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load 
     For Index As Integer = 0 To FooList.Capacity - 1 
      FooList.Add(New Foo() With {.Bar = Index, .Baz = 0}) 
     Next 
     ' Shows all Bar and Baz 
     DataGridView1.DataSource = FooList 
     ' User selects Bar value 
     ComboBox1.DataSource = FooList 
     ComboBox1.DisplayMember = "Bar" 
     ' Related Baz value shows 
     Label1.DataBindings.Add(New Binding("Text", FooList, "Baz", DataSourceUpdateMode.OnPropertyChanged))  
    End Sub 

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click 
     ' This is not _actually_ how I'm selecting indexes and changing the data 
     ' But for the MRE it changes the Baz property 

     'Change the Baz value on the List, should result in Label1 changing 
     FooList(ComboBox1.SelectedItem.Bar).Baz = TextBox1.Text.Convert.ToUInt16 
     ' Should I even need this when my list objects have INotifyChanged? 
     DataGridView1.Refresh() 
    End Sub 
End Class 

Class Foo 
    Implements INotifyChanged 
    Private _bar As UInt16 
    Private _baz As UInt16 

    Public Event PropertyChanged As PropertyChangedEventHandler _ 
     Implements INotifyPropertyChanged.PropertyChanged 

    Private Sub NotifyPropertyChanged(ByVal PropertyName As String) 
     RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName)) 
    End Sub 

    Property Bar As UInt 16 
     Get 
      Return _bar 
     End Get 
     Set(value As Byte) 
      If Not (value = _bar) Then 
       _bar = Bar 
       NotifyPropertyChanged("Bar") 
      End If 
     End Set 
    End Property 

    Property Baz As UInt 16 
     Get 
      Return _baz 
     End Get 
     Set(value As Byte) 
      If Not (value = _baz) Then 
       _baz = Baz 
       NotifyPropertyChanged("Baz") 
      End If 
     End Set 
    End Property 
End Class 

ответ

1

Один из способов, чтобы изменения в коллекции отражены в связанных элементов управления, чтобы «сбросить» на DataSource:

FooList.Add(New Foo(...)) 
dgv1.DataSource = Nothing 
dgv1.DataSource = FooList 

Если элемент управления является то, как ListBox, вы должны также сбросить DisplayMember и ValueMember потому что они очищаются. Это замаскированный способ уведомлять об изменениях в списке, потому что многие вещи сбрасываются. Например, в ListBox коллекция SelectedItems очищается.

Гораздо лучший способ внести изменения в поток вашей коллекции через элементы управления - использовать BindingList(Of T). Изменения в коллекции/списке (добавляет, удаляет) будут автоматически и мгновенно отображаться в вашем элементе управления.

INotifyPropertyChanged идет дальше. Если значение свойства по элементу в списке изменяется, то BindingList<T> поймает события PropertyChanged, которые ваш класс повышает и «переводит» изменения в элемент управления.

Чтобы было ясно, что BindingList handlea изменения в список, в то время как INotifyPropertyChanged обрабатывает изменения в элементов в списке.

FooList(13).Name = "Ziggy" 

Используя подмену List<T> имя не будет отображаться, если вы «сброс» на DataSource.Используя только BindingList<T>, он не появится сразу же - когда список изменится, он должен появиться. Внедрение INotifyPropertyChanged позволяет сразу же изменять.

Ваш код, в основном, правильный, кроме него не INotifyChanged. Но есть также ярлык от Net 4.5:

Private Sub NotifyChange(<CallerMemberName> Optional propname As String = "") 
    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propname)) 
End Sub 

CallerMemberName является атрибутом, который позволяет отказаться от фактически передавая имя; названный заканчивается замещено во время выполнения:

Private _name As String 
Public Property Name As String 
    Get 
     Return _name 
    End Get 
    Set(value As String) 
     If _name <> value Then 
      _name = value 
      NotifyChange() 
     End If 

    End Set 
End Property 

Если нет ничего другого, что может сократить ошибки копирования/вставки, если есть много свойств, чтобы поднять события для (т.е. Bar имущества с использованием NotifyChange("Foo") потому, что вы скопировали код из этот сеттер).

+0

Спасибо, используя работу BindingList, хотя мне действительно нужно было создать свой собственный класс, унаследованный BindingList, чтобы я мог добавить некоторые специальные методы List, чтобы уменьшить изменения в текущей кодовой базе. WRT 'CallerMemberName' получается, что хотя у меня установлен .Net 4.5, VS2010 использует только до версии 4.0. Я до сих пор считаю глупо, как языковые обновления привязаны к IDE, а не только к версии фреймворка. Grr. – Toby

0

Я полагал, что я могу передать данные по другому. То есть вместо обновления данных в списке, а затем при попытке обновить элементы управления, я обновляю элемент управления, и данные списка обновляются через привязку.

E.g. Мой щелчок событие сверху теперь становится:

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click 
     Label1.Text = TextBox1.Text 
     DataGridView1.Refresh() 
    End Sub 

Хотя ТВН Im не фанат, что и я до сих пор недоумевают, каким я мог бы лучше использовать интерфейс INotifyPropertyChanged.

Смежные вопросы