Problem mit selbsterstellter Property und INotifyPropertyChanged

  • WPF

Es gibt 5 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Problem mit selbsterstellter Property und INotifyPropertyChanged

    Abend,

    Ich hab hier (mal) wieder ein Problem mit INotifyPropertyChanged.
    Nach dem ich mich jetzt näher mit INotifyPropertyChanged beschäftigt habe und es endlich zum laufen gebracht habe stehe ich jetzt vor einem sehr interessanten Problem.

    Ich verwende das EntityFramework zum erstellen einer Klasse => Model
    Die heißt Konto_V01 hat mehrere Properties die für die Frage relevanten sind:

    VB.NET-Quellcode

    1. Public Overridable Property nv_kto_2_trans As ObservableCollection(Of transactions_v01) = New ObservableCollection(Of transactions_v01)
    2. Public Overridable Property nv_kto_2_trans_transfer As ObservableCollection(Of transactions_v01) = New ObservableCollection(Of transactions_v01)

    Diese sind 2 NavigationProperties im EF zu einer anderen Entität "Transactions_V01"

    in meinem hier ausdifferenzierten Viewmodel sind die beiden in einer eigenen Property eingebaut (ich weiß Boilerplate Code usw. aber ich versuche den Aufbau von MVVM zum üben exakt zu leben)

    VB.NET-Quellcode

    1. Public Property m_nv_kto_2_trans As ObservableCollection(Of transactions_v01)
    2. Get
    3. Debug.Print("nv_kto_2_trans Änderung")
    4. Return nv_kto_2_trans
    5. End Get
    6. Set(value As ObservableCollection(Of transactions_v01))
    7. If Not value Is nv_kto_2_trans Then
    8. nv_kto_2_trans = value
    9. NotifyPropertyChanged()
    10. Debug.Print("nv_kto_2_trans Notify")
    11. End If
    12. End Set
    13. End Property
    14. Public Property m_nv_kto_2_trans_transfer As ObservableCollection(Of transactions_v01)
    15. Get
    16. Debug.Print("nv_kto_2_trans_transfer Änderung")
    17. Return nv_kto_2_trans_transfer
    18. End Get
    19. Set(value As ObservableCollection(Of transactions_v01))
    20. If Not value Is nv_kto_2_trans_transfer Then
    21. nv_kto_2_trans_transfer = value
    22. NotifyPropertyChanged()
    23. Debug.Print("nv_kto_2_trans_transfer notify")
    24. End If
    25. End Set
    26. End Property


    Die Methode NotifyPropertyChanged sieht so aus:

    VB.NET-Quellcode

    1. Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
    2. RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    3. End Sub



    Beide Properties benötige ich ein einer gemeinsamen Property um sie in einem Datagrid anzuzeigen:

    VB.NET-Quellcode

    1. Public ReadOnly Property MixedChildren As ObservableCollection(Of transactions_v01) 'As IEnumerable
    2. Get
    3. 'Return nv_kto_2_trans.Cast(Of Object).Concat(nv_kto_2_trans_transfer.Cast(Of Object))
    4. Debug.Print("Konto Mixed Children call")
    5. Debug.Print("Kto2Trans " & m_nv_kto_2_trans.Count.ToString)
    6. Debug.Print("Kto2TransTransfer " & m_nv_kto_2_trans_transfer.Count.ToString)
    7. Return New ObservableCollection(Of transactions_v01)(m_nv_kto_2_trans.Concat(m_nv_kto_2_trans_transfer))
    8. End Get
    9. End Property

    (Danke an EDR für diesen Code Tipp)

    Blöderweise bekommt aber diese MixedChildren keine Änderungen mit wenn eine Transaction hinzugefügt wird (egal ob ich das direkt in Transactions_v01 teste oder über die Navigationproperty versuche)

    Kann ich INotifyPropertyChanged irgendwie dafür nutzen MixedChildren zu aktualisieren oder bin ich total auf dem Holzweg?
    mfG.
    Stephan
    Mach dir klar, was das Wörtchen New bedeutet. Da wird bei jedem Abruf eine neue OC erzeugt und an den Aufrufer gegeben.
    Während du selbst keinen Verweis mehr auf diese OC hast.
    Womit du natürlich nicht mitkriegst, wenn der User was dieser MixedOC zufügt oder löscht - wie denn auch?

    Also finde eine Lösung, bei der nicht bei jedem Abruf das Wörtchen New auftaucht (denn die Änderungen von hunderten von OCs wirst du eh niemals sinnvoll verfolgen können).

    Das wird nicht ganz trivial, denn du musst die MixedOC so hinbasteln, dass sie nur einmal erstellt wird, und dann muss sie sich beständig mit den beiden anderen OCs abgleichen.
    Also wird in m_nv_kto_2_trans was zugefügt, muss es auch in MixedOC zugefügt werden, und wird in m_nv_kto_2_trans_transfer (mann, was schreckliche Code-Namen!) was zugefügt, muss es auch in MixedOc zugefügt werden.
    Und wird in m_nv_kto_2_trans was gelöscht, muss es auch in MixedOC gelöscht werden, und wird in m_nv_kto_2_trans_transfer (mann, was schreckliche Code-Namen!) was gelöscht , muss es auch in MixedOc gelöscht werden.
    Und wird in MixedOC was gelöscht, so muss es auch entweder in m_nv_kto_2_trans_transfer oder in m_nv_kto_2_trans_transfer (mann, was schreckliche Code-Namen!) gelöscht werden.
    Und wird in MixedOC was zugefügt, so muss es auch entweder in m_nv_kto_2_trans_transfer oder in m_nv_kto_2_trans_transfer (mann, was schreckliche Code-Namen!) zugefügt werden.

    Wie gesagt: nicht trivial, aber mit dem OC_CollectionChanged-Event immerhin prinzipiell möglich.
    Also ich habe jetzt die letzten Tage daran rumgebastelt aber ich komme auf keine Lösung der Ansatz mit CollectionChanged leuchtet mir ein aber ich krieg es nicht hin hier mein Versuch
    (Ps: Ja ich weiß die Code-Namen sind etwas schwierig folegen aber eine Logig: m ist nur eine Hilfspräfix aber nv steht für NavigationProperty_Entity 1_2_Entity2 somit erkenne ich direkt von welcher Entität mich die Navigation Property führt und zu welcher es zeigt).

    Zum Code: (Relevanter Auszug der klasse)

    VB.NET-Quellcode

    1. Partial Public Class konto_v01
    2. Implements INotifyCollectionChanged
    3. Private Sub CollectionChangedHandler(sender As Object, e As Specialized.NotifyCollectionChangedEventArgs) Handles Me.CollectionChanged
    4. Debug.Print("changed: " & e.Action.ToString)
    5. MixedChildren = m_nv_kto_2_trans.Cast(Of Object).Concat(m_nv_kto_2_trans_transfer.Cast(Of Object))
    6. End Sub
    7. Public Overridable Property nv_kto_2_trans As ObservableCollection(Of transactions_v01) = New ObservableCollection(Of transactions_v01) 'diese Property hat das EF selbst erzeugt.
    8. Public Property m_nv_kto_2_trans As ObservableCollection(Of transactions_v01)
    9. Get
    10. Debug.Print("nv_kto_2_trans Änderung")
    11. Return nv_kto_2_trans
    12. End Get
    13. Set(value As ObservableCollection(Of transactions_v01))
    14. ' If Not value Is nv_kto_2_trans Then
    15. nv_kto_2_trans = value
    16. 'NotifyPropertyChanged()
    17. Debug.Print("nv_kto_2_trans Notify")
    18. RaiseEvent CollectionChanged(Me, New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add))
    19. Debug.Print("nv_kto_2_trans Notify")
    20. 'End If
    21. MixedChildren = m_nv_kto_2_trans.Cast(Of Object).Concat(m_nv_kto_2_trans_transfer.Cast(Of Object))
    22. End Set
    23. End Property
    24. Sub New()
    25. MixedChildren = m_nv_kto_2_trans.Cast(Of Object).Concat(m_nv_kto_2_trans_transfer.Cast(Of Object))
    26. End Sub
    27. Public Property MixedChildren As IEnumerable
    28. End Class


    Zum Hinzufügen einer Transaction nutze ich testweise im Mainmodel ein Relaycommand mit folgender Sub:

    VB.NET-Quellcode

    1. Private Sub TransactionKontoAdd(v_values() As Object)
    2. 'Element 0 ist der Typ
    3. 'Element 1 ist die Konto ID
    4. Dim v_typ As String = v_values(0).ToString.ToLower
    5. Dim v_kto As Integer = CInt(v_values(1))
    6. If v_typ Is Nothing Then v_typ = "ein" 'Sicherheitshalber wenn etwas nicht übergeben wird.
    7. 'Ein Versuch:
    8. Dim v_ckto As konto_v01 = __konten.First(Function(x) x.kto_id = v_kto)
    9. If v_ckto IsNot Nothing Then
    10. Konten.MoveCurrentTo(v_ckto)
    11. Dim v_konto As konto_v01 = CType(Konten.CurrentItem, konto_v01)
    12. With v_konto
    13. .m_nv_kto_2_trans.Add(New transactions_v01 With {.datum = CDate(Date.Now.ToShortDateString), .transtyp_id = get_transtyp_from_string(v_typ), .comment = "Test"})
    14. End With
    15. End If
    16. End Sub


    Klappt aber noch immer nicht, es wird interessanterweise aber das Set Event der m_nv_kto_2_trans nicht aufgerufen obwohl die .add Methode aufgerufen wird.
    mfG.
    Stephan
    Danke für das ausführliche Beispiel jetzt funktioniert es, ich versuche gerade den Code nachzuvollziehen soweit leuchtet mir das meiste ein allerdings stolpere ich über den

    VB.NET-Quellcode

    1. ​Private _Suspender As New ActionFrame(AddressOf SuspendEvents)


    Im Sub New wird ein _Supsender.Enter "geused" damit wird im Actionframe einmal der Handler entfernt und einmal hinzugefügt.
    Ich vermute jetzt einfach mal ganz plump der _suspender verhindert das während der ich nenne es mal "Bauphase" der Mixed Collection immer das Changedevent gecallt wird?
    mfG.
    Stephan