Двусторонняя WPF-привязка к свойствам класса внутри ObservableCollection - PullRequest
1 голос
/ 18 ноября 2011

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

Хорошо, у меня есть одноэлементный класс, к которому я в конечном итоге привязан:

Imports System.ComponentModel
Imports System.Collections.ObjectModel

Public Class AmandaSeyfried
    Implements INotifyPropertyChanged

    Shared _config As New config

    Public Event PropertyChanged(sender As Object, E As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged

    Protected Overridable Sub OnPropertyChanged(propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

    Private Shared _thisInstance As AmandaSeyfried

    Protected Sub New()
        ' initialization goes here
    End Sub

    Public Shared Function GetSingleton() As AmandaSeyfried
        ' initialize object if it hasn't already been done
        If _thisInstance Is Nothing Then
            _thisInstance = New AmandaSeyfried
        End If

        ' return the initialized instance
        Return _thisInstance
    End Function

    Public Class CountryTranslation
        Implements INotifyPropertyChanged

        Private Property _englishCountryName As String = ""
        Public Property EnglishCountryName As String
            Get
                Return _EnglishCountryName
            End Get
            Set(value As String)
                If _englishCountryName <> value Then
                    _englishCountryName = value
                    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("EnglishCountryName"))
                End If
            End Set
        End Property

        Private Property _foreignCountryName As String = ""
        Public Property ForeignCountryName As String
            Get
                Return _foreignCountryName
            End Get
            Set(value As String)
                If _foreignCountryName <> value Then
                    _foreignCountryName = value
                    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("ForeignCountryName"))
                End If
            End Set
        End Property

        Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
    End Class

    Private WithEvents _countryTranslations As New ObservableCollection(Of CountryTranslation)
    Public Property CountryTranslations As ObservableCollection(Of CountryTranslation)
        Get
            If _config.GetKeyTextValue("countryTranslations") <> "" Then
                Dim reader As New IO.StringReader(_config.GetKeyTextValue("countryTranslations"))
                Dim Serializer As New Xml.Serialization.XmlSerializer(_countryTranslations.GetType)
                _countryTranslations = Serializer.Deserialize(reader)
            End If

            Return _countryTranslations
        End Get
        Set(value As ObservableCollection(Of CountryTranslation))
            _countryTranslations = value
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("CountryTranslations"))
        End Set
    End Property

    Private Sub CountryTranslationCollectionChanged(sender As Object, e As Specialized.NotifyCollectionChangedEventArgs) Handles _countryTranslations.CollectionChanged
        Dim newStringWriter As New IO.StringWriter
        Dim NewSerializer As New Xml.Serialization.XmlSerializer(_countryTranslations.GetType)
        NewSerializer.Serialize(newStringWriter, _countryTranslations)
        _config.SaveKeyTextValue("countryTranslations", newStringWriter.ToString)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("CountryTranslations"))
    End Sub

End Class

_config - это вспомогательный класс с неправильным именем, который хранит и извлекает данные из локального экземпляра SqlCe. По сути, объект сериализуется, сохраняется в БД, а затем извлекается из БД в любое время, когда это необходимо, и десериализуется обратно в объект. В целом, похоже, это работает довольно хорошо.

Моя проблема заключается в том, что, хотя я могу связываться с объектом и отслеживать, когда в WPF DataGrid добавляется строка через обработчик CollectionChangedMethod, я не получаю никаких уведомлений при изменении любого из двух свойств CountryTranslation. .

Остальная часть моего связанного кода: ... XAML ... очевидно, есть и другое, но я не верю, что виновата часть связывания XAML, поэтому я подрежу ее до соответствующей:

<toolkit:DataGrid Margin="12,12,12,12" ItemsSource="{Binding Path=KarenSmith.CountryTranslations, Mode=TwoWay}" AutoGenerateColumns="False" HorizontalAlignment="Stretch" Width="Auto" SelectionMode="Single">
    <toolkit:DataGrid.Columns>
        <toolkit:DataGridTextColumn Width="283" Binding="{Binding EnglishCountryName,Mode=TwoWay}" />
        <toolkit:DataGridTextColumn Width="283" Binding="{Binding ForeignCountryName,Mode=TwoWay}" />
    </toolkit:DataGrid.Columns>
</toolkit:DataGrid>

И красивый и простой код:

Public Class Preferences

    Public Property KarenSmith As AmandaSeyfried = AmandaSeyfried.GetSingleton

    Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        DataContext = Me

    End Sub

    Private Sub Close_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
        Me.Close()
    End Sub
End Class

Если я добавлю несколько точек останова в Getter и Setters класса CountryTranslation, я смогу отслеживать, когда они меняются (через сетку данных, так что связывание работает), но стараюсь, как могу, не могу понять как вызвать событие на основе этого обратно в главном классе, чтобы впоследствии обновить хранилище данных, чтобы показать изменения.

1 Ответ

2 голосов
/ 18 ноября 2011

Обычно я добавляю событие CollectionChanged к ObservableCollection, которое присоединяет событие PropertyChanged к его элементам, когда они добавляются, и этот прослушиватель событий прослушивает изменения и обрабатывает их по мере необходимости.

Вот пример: (надеюсь, синтаксис правильный, так как я только что запустил его через конвертер C # в VB.Net)

Public Sub New()
    AddHandler MyCollection.CollectionChanged, AddressOf MyCollection_CollectionChanged
End Sub

Private Sub MyCollection_CollectionChanged(sender As Object, e As CollectionChangedEventArgs)
    If e.NewItems IsNot Nothing Then
        For Each item As MyItem In e.NewItems
            AddHandler item.PropertyChanged, AddressOf MyItem_PropertyChanged
        Next
    End If

    If e.OldItems IsNot Nothing Then
        For Each item As MyItem In e.OldItems
            RemoveHandler item.PropertyChanged, AddressOf MyItem_PropertyChanged
        Next
    End If
End Sub

Private Sub MyItem_PropertyChanged(sender As Object, e As PropertyChangedEventArgs)
    If e.PropertyName = "Some Property" Then
        DoWork()
    End If
End Sub

Версия C # выглядит следующим образом:

public MyViewModel()
{
    MyCollection.CollectionChanged += MyCollection_CollectionChanged;
}

void MyCollection_CollectionChanged(object sender, CollectionChangedEventArgs e)
{
    if (e.NewItems != null)
        foreach(MyItem item in e.NewItems)
            item.PropertyChanged += MyItem_PropertyChanged;

    if (e.OldItems != null)
        foreach(MyItem item in e.OldItems)
            item.PropertyChanged -= MyItem_PropertyChanged;
}

void MyItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "Some Property")
        DoWork();
}
...