DataBinding mit einem TabControl auf einem untergeordnetem Fenster

  • WPF

Es gibt 47 Antworten in diesem Thema. Der letzte Beitrag () ist von Nofear23m.

    Hallo

    Da gibt es mehrere Möglichkeiten. MVVM verbietet ja jetzt nicht explizit CodeBehind. Also könntest du theoretisch beim Button_click das Fenster schliessen und auch im ViewModel den Savecomand ausführen.
    Gibt aber auch die Möglichkeit über sauberes MVVM.

    Du hast ja ein Window als MyWindow.xaml. Dieses weis das es IMMER eine IWindowVm bekommen wird. Du könntest ja in die IWindowVM Schnittstelle ein Event CloseWindow hineinpacken.
    Im MyWindow packst du dann im Loaded Event ein AddHandler ein und machst das Fenster zu.

    Richtig sauber und komfortabel geht das über Messages. Wo sich Fenster, Usercontrols usw. die Nachrichten Abonnieren und du im VM ohne Events die Nachricht wirfst. Das geht sogar mit Parametern die man übergeben kann.
    Hierfür würde ich aber einen neuen Thread aufmachen.

    Ich mache es nur über Messages. Kann ich dir gerne zeigen wenn Interesse vorhanden.

    Grüße
    Sascha

    PS: Freut mich das dieses Thema mal wem interessiert. Wird zu selten behandelt.
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    ich hab mir NoFear23's Code nur in einer jetzt "veralteten" Version angeguckt - wäret ihr beide daran interesssiert, zu erlernen, wie man den Wpf-Designer stärker als wirklichen Designer nutzen kann?
    Dann würde ich mir die neueste NoFear23-Version mal vorknöpfen und versuchen, umzufrickeln.
    Ich bau dann allerdings auch Option Strict On ein, und schmeisse den Microsoft.Visualbasic-Namespace raus.
    Also worauf ich hinaus wollen würde sind 2 Dinge: Visual Studio - Empfohlene Einstellungen
    und Grundlagen - MVVM: "Binding-Picking" im Xaml-Editor

    Aber wäre einiges an Arbeit für mich... :(
    Hi,
    gerne.
    Option strict ist doch on? Genauso wie der Namespace zwar angehakt ist aber fast nicht benutzt wird. Zumindest sind mir beim kopieren in das andere Projekt, welches beides berücksichtigt keine Fehler aufgefallen.
    ​Edit: habe alles mal im Projekt hier umgestellt, es sind kaum bzw. keine Fehler. Diese habe ich aber bereits hier angesprochen.

    Auf das Binding-Picking welches du in deinem Therad ansprichst wollte ich die ganze Zeit hinaus.
    Hallo

    ​wäret ihr beide daran interesssiert, zu erlernen, wie man den Wpf-Designer stärker als wirklichen Designer nutzen kann?

    ​Na auf jeden Fall. Bin IMMER für was neues.

    ​Ich weis nur nicht ganz was Ihr mit dem Binding-Picking meint. Das ist doch das Quadrat im PropertieWindow wo man draufklickt und das Binding holen kann. Funzt doch wunderbar sobald man den DesignTime DataContext festlegt.
    ​Wobei ich sagen muss, persönlich tippe ich immer. Wenn der DesignTime Context drinnen ist habe ich ja auch IntelliSense, bin ich meist schneller als mit dem "Picking".

    Die Visual Basic verweise habe ich bei mir bereits entfernt, hatte ich total übersehen und waren leider ein Überbleibsel. Danke hierfür nochmals an @SKeks.

    ​Aber nur her damit, immer gut was neues zu lernen, ob ichs dann verwende kann ich nicht sagen, aber ich bin immer offen für neues.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    SKeks schrieb:

    Option
    strict ist doch on?


    Nofear23m schrieb:


    Ich weis nur nicht ganz was Ihr mit dem Binding-Picking
    meint.
    Deswegen gab ich doch den Link - musste halt auch folgen,
    und mal ins Video reingugge.

    Nofear23m schrieb:


    Das ist doch das Quadrat im PropertieWindow wo man draufklickt und das
    Binding holen kann. Funzt doch wunderbar sobald man den DesignTime
    DataContext festlegt.
    Jepp - das meinte ich.



    Jo dann war das veraltete wohl wirklich veraltet.
    Weil was ich mir angeguckt hab, da war Strict Off, und BindingPicking war nicht möglich (kein d:DataContext gesetzt).



    Ok - jetzt das aktuelle angeguckt: In 2 von 3 Projekten ist Strict Off und in 3en ist MVB general-importiert - also soo alles paletti scheints mir doch nicht.



    Aber mein akt. Hauptproblem ist, dass mir scheints 2 Dateien fehlen:
    Error 1 Unable to open module file 'C:\Users\Account2\Downloads\MVVM_DEMO_Neu_Trigger\Demo.Views\BoolView.xaml.vb': Das System kann die angegebene Datei nicht finden. C:\Users\Account2\Downloads\MVVM_DEMO_Neu_Trigger\Demo.Views\BoolView.xaml.vb 1 1 Demo.Views
    Error 2 Unable to open module file 'C:\Users\Account2\Downloads\MVVM_DEMO_Neu_Trigger\Demo.Views\IntView.xaml.vb': Das System kann die angegebene Datei nicht finden. C:\Users\Account2\Downloads\MVVM_DEMO_Neu_Trigger\Demo.Views\IntView.xaml.vb 1 1 Demo.Views
    diese beiden Dateien kannste löschen.Dies sind Überbleibsel aus den Iterationen.
    ​MVB raus ergibt einen Fehler im RelayCommand bei der Prüfung auf "nothing" und "Option Strict" On müsste Fehler bei den Modalen Dialogen ergeben, wenn mich nicht alles täuscht. Die Subs, die bei der Umstellung einen Fehler ergeben kann man hier auskommentieren. Das meinte ich damit.
    ​Zuletzt muss noch das Startprojekt auf die MyWindowsApp gelegt werden.

    ErfinderDesRades schrieb:

    Deswegen gab ich doch den Link - musste halt auch folgen,
    und mal ins Video reingugge.


    Habe ich, Danke. Ich weis das man Links folgt. Genau deshalb war ich verwirrt, da wir das ja drin hatten. Aber das war nicht die Fragestellung weshalb ich es beim ersten Beispiel nicht eingebaut hatte.
    ​Man muss ja niemanden gleich mit Infos erschlagen oder?
    Du musst dir hald das letzte Beispiel herunterladen. Also, was funzt in diesem Model jetzt nicht?

    ​also soo alles paletti scheints mir doch nicht


    ​Wenn wir jetzt die i Tüpfelchen anfangen rauszupicken, von einem Anschauungsbeispiel dann muss ich sagen. Danke. Das mit Strict On ist klar. Kein Problem das zu ändern.
    ​Wolltest du jetzt nur Fehler suchen oder uns was zeigen? Wenn letzteres dann gerne. Bin immer für neues und lerne gerne dazu.
    ​Vielleicht hast du einen anderen Ansatz. Wäre cool, denn ich hab mir auch nur alles zusammengesucht und das rausgenommen was ich für gut empfunden habe. Wenn es besser geht freue ich mich SEHR. Wäre ja cool.

    Schöne Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    naja, was zeigen.
    Aber dazu nicht unbedingt erst alle Fehler berichtigen müssen.

    Aber ich sehe auch nicht, dass im Xaml irgendwo ein d:DataContext festgelegt wäre, dabei habe ich inzwischen die neueste Version am Wickel, also aus post#18
    Ah - jetzt doch noch gefunden - im Demo-Views-Projekt.

    Also ich guggemol weiter... ;)
    ​Also ich guggemol weiter

    Top ! 8o

    ​Freu mich, bin immer offen für Vorschläge wie es besser geht, oft sieht man ja den Wald vor lauter Bäumen nicht.
    PS: Sollen wir hierfür einen neuen Thread machen??

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Nicht zerfleischen bitte :)

    Im Anhang das Projekt mit Strict On und ohne VB-Namespace. Messageboxen sind nun auskommentiert, also funktionieren auch die beiden SpeichernButtons nicht mehr
    ​Edit: Oder man formuliert den Rückgabetyp beim Dialog als Nullable(of Boolean). dann kann man alles einkommentiert lassen:

    VB.NET-Quellcode

    1. Public Function ShowDialog(windowname As String, title As String, datacontext As Object) As Boolean? Implements IDialogWindowService.ShowDialog
    2. Dim win As New ChildWindowBase
    3. win.DataContext = datacontext : win.Title = title : win.Name = windowname
    4. Return win.ShowDialog
    5. End Function​


    Dateien
    • MVVM_DEMO 2.zip

      (369,76 kB, 126 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „SKeks“ ()

    SKeks schrieb:

    Oder man formuliert den Rückgabetyp beim Dialog als Nullable(of Boolean)


    Oder man baut den Enumerator einfach nach wie ich es mit ​EnuMessageBoxResult oder ​EnuMessageBoxButton oder mit ​EnuMessageBoxImage gemacht habe. Ist am saubersten.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Also ich hab jetzt mal eine ich finde aufs nötige reduzierte Version gemacht - 15 Dateien in 3 Projekten auf nur 5 in einem reduziert - Infrastruktur bei der Zählung nicht berücksichtigt.
    Erkennbar ist auch das Designtime-Daten-Feature, das geht noch bisserl weiter als "nur" BindingPicking.
    Auch hab ich ein bischen logische Logik ans SaveCommand gemacht, also da wird die aktuelle Person nu tatsächlich einer Liste zugefügt, und sieht man auch.

    Insgesamt ist das Beispiel ja ziemlich weltfremd, also dass da Personen erzeugt werden, in nicht-modalen Fenstern zur Bearbeitung angezeigt, und wenn man Fenster schließt, sind sie weg...
    Und dann muss man sich drum kümmern, dass nicht mehrere Personen gleichzeitig geöffnet werden - solch würde man normalerweise wohl einfach mit modalen Dialoge abhandeln...

    In Grundlagen - MVVM-Anwendungs-Struktur hab ich was gemacht, wo man Personen editieren, zufügen, löschen kann - also was man normal von einer Datenverarbeitung erwartet.

    Edit: Dateianhang siehe jetzt post#39

    Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Hallo

    Danke für die mühe.
    Ich fang mal so an wie ich die Files geöffnet habe.

    1.) NotifyPRopertyChanged
    • Warum ICloneable implementieren? Mir ist klar was ich damit machen kann. Aber: Habe ich jetzt einen Denkfehler. Die RelayCommand Schnittstelle hat keinen Parameterlosen, somit kann keine VM Klasse welches ein RelayCommand verwendet kopiert werden oder? fände ich cool wenn das ginge. Ich bilde mir ein das ich da mal gegen eine Wand gelaufen bis, kann aber sein das ich das jetzt verwechsle.
    • Wozu PropIfDifferent? Wo liegt der Vorteil. Schon klar. Wenn der Wert gleich ist muss nicht die View aktualisieren. Sonst noch was?
    • Auf Serializable gehe ich jetzt nicht weiter ein. Ist klar was es tut.
    • Was ich interessant finde. Das IsInDesignMode Property. So hatte ich das noch nicht gesehen, erkenne natürlich auch die Vorteile. Ich habe es immer mit DesignerProperties.GetIsInDesignMode(DependancyObject) gelöst. Damit war ich aber nie zufrieden da es ja einen Verweis auf System.Windows benötigt wegen DependencyObject. Das ist elegant gelöst. Muss ich mal probieren.
    • MockDesignMode verwendest du dann für UnitTests richtig??

    2.) Mainmodel (Eigentlich ja MainViewModel)
    • Private WithEvents wndPerson As Window hat in einem ViewModel nix zu suchen!!
    • Wenn du schon so genau bist das du meinst man muss einen Kunden speichern obwohl das nicht di efrgestellung war und nix mit dem Pattern zu tun hat dann bitte im RelayCommand auch CanExecute vergeben. Speichern OHNE Kundendaten darf nicht möglich sein. (Du hast angefangen)
    • Private Sub wndPerson_Closed(sender As Object, e As EventArgs) Handles wndPerson.Closed Oh mein Gott.

    Der Rest ist soweit eigentlich so wie ich es habe. Nur das ich das öffnen und schliessen von Fenstern sowie spezialle dinge mit Controls oder Views wo binden nicht möglich ist oder wo gewisse Dinge angestoßen werden müssen über ein MessageSystem mache. Da hat sich das dann auch erledigt. Sehr komfortabel.

    Das kannst du dir mal ansehen:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Reflection
    2. ''' <summary>
    3. ''' Provides loosely-coupled messaging between
    4. ''' various colleague objects. All references to objects
    5. ''' are stored weakly, to prevent memory leaks.
    6. ''' </summary>
    7. Public Class Messenger
    8. #Region "Constructor"
    9. Public Sub New()
    10. End Sub
    11. #End Region
    12. #Region "Register"
    13. ''' <summary>
    14. ''' Registers a callback method to be invoked when a specific message is broadcasted.
    15. ''' </summary>
    16. ''' <param name="message">The message to register for.</param>
    17. ''' <param name="callback">The callback to be called when this message is broadcasted.</param>
    18. Public Sub Register(message As String, callback As [Delegate])
    19. If String.IsNullOrEmpty(message) Then
    20. Throw New ArgumentException("'message' cannot be null or empty.")
    21. End If
    22. If callback Is Nothing Then
    23. Throw New ArgumentNullException("callback")
    24. End If
    25. Dim parameters As ParameterInfo() = callback.Method.GetParameters()
    26. If parameters IsNot Nothing AndAlso parameters.Length > 1 Then
    27. Throw New InvalidOperationException("The registered delegate can have no more than one parameter.")
    28. End If
    29. Dim parameterType As Type = If((parameters Is Nothing OrElse parameters.Length = 0), Nothing, parameters(0).ParameterType)
    30. _messageToActionsMap.AddAction(message, callback.Target, callback.Method, parameterType)
    31. End Sub
    32. Public Sub UnRegister(message As String, target As Object)
    33. _messageToActionsMap.RemoveAction(message, target)
    34. End Sub
    35. #End Region
    36. #Region "NotifyColleagues"
    37. ''' <summary>
    38. ''' Notifies all registered parties that a message is being broadcasted.
    39. ''' </summary>
    40. ''' <param name="message">The message to broadcast.</param>
    41. Public Sub NotifyColleagues(message As String)
    42. If String.IsNullOrEmpty(message) Then
    43. Throw New ArgumentException("'message' cannot be null or empty.")
    44. End If
    45. Dim actions = _messageToActionsMap.GetActions(message)
    46. If actions IsNot Nothing Then
    47. actions.ForEach(Function(action) action.DynamicInvoke())
    48. End If
    49. End Sub
    50. ''' <summary>
    51. ''' Notifies all registered parties that a message is being broadcasted.
    52. ''' </summary>
    53. ''' <param name="message">The message to broadcast</param>
    54. ''' <param name="parameter">The parameter to pass together with the message</param>
    55. Public Sub NotifyColleagues(message As String, parameter As Object)
    56. If String.IsNullOrEmpty(message) Then
    57. Throw New ArgumentException("'message' cannot be null or empty.")
    58. End If
    59. Dim actions = _messageToActionsMap.GetActions(message)
    60. If actions IsNot Nothing Then
    61. For Each a In actions
    62. a.DynamicInvoke(parameter)
    63. Next
    64. ' actions.ForEach(Function(action) action.DynamicInvoke(parameter))
    65. End If
    66. End Sub
    67. #End Region
    68. #Region "MessageToActionsMap [nested class]"
    69. ''' <summary>
    70. ''' This class is an implementation detail of the Messenger class.
    71. ''' </summary>
    72. Private Class MessageToActionsMap
    73. ' Stores a hash where the key is the message and the value is the list of callbacks to invoke.
    74. ReadOnly _map As New Dictionary(Of String, List(Of WeakAction))()
    75. Friend Sub New()
    76. End Sub
    77. ''' <summary>
    78. ''' Adds an action to the list.
    79. ''' </summary>
    80. ''' <param name="message">The message to register.</param>
    81. ''' <param name="target">The target object to invoke, or null.</param>
    82. ''' <param name="method">The method to invoke.</param>
    83. ''' <param name="actionType">The type of the Action delegate.</param>
    84. Friend Sub AddAction(message As String, target As Object, method As MethodInfo, actionType As Type)
    85. If message Is Nothing Then
    86. Throw New ArgumentNullException("message")
    87. End If
    88. If method Is Nothing Then
    89. Throw New ArgumentNullException("method")
    90. End If
    91. SyncLock _map
    92. If Not _map.ContainsKey(message) Then
    93. _map(message) = New List(Of WeakAction)()
    94. End If
    95. _map(message).Add(New WeakAction(target, method, actionType))
    96. End SyncLock
    97. End Sub
    98. Friend Sub RemoveAction(message As String, target As Object)
    99. Try
    100. SyncLock _map
    101. Dim weakActions As List(Of WeakAction) = _map(message)
    102. For i As Integer = weakActions.Count - 1 To -1 + 1 Step -1
    103. If (weakActions(i)._targetRef.Target Is target) OrElse (TryCast(weakActions(i), WeakAction) IsNot Nothing AndAlso DirectCast(weakActions(i), WeakAction)._method Is target) Then
    104. weakActions.Remove(weakActions(i))
    105. End If
    106. Next
    107. End SyncLock
    108. Catch ex1 As KeyNotFoundException
    109. Debug.WriteLine(ex1.ToString)
    110. End Try
    111. End Sub
    112. ''' <summary>
    113. ''' Gets the list of actions to be invoked for the specified message
    114. ''' </summary>
    115. ''' <param name="message">The message to get the actions for</param>
    116. ''' <returns>Returns a list of actions that are registered to the specified message</returns>
    117. Friend Function GetActions(message As String) As List(Of [Delegate])
    118. If message Is Nothing Then
    119. Throw New ArgumentNullException("message")
    120. End If
    121. Dim actions As List(Of [Delegate])
    122. SyncLock _map
    123. If Not _map.ContainsKey(message) Then
    124. Return Nothing
    125. End If
    126. Dim weakActions As List(Of WeakAction) = _map(message)
    127. actions = New List(Of [Delegate])(weakActions.Count)
    128. For i As Integer = weakActions.Count - 1 To -1 + 1 Step -1
    129. Dim weakAction As WeakAction = weakActions(i)
    130. If weakAction Is Nothing Then
    131. Continue For
    132. End If
    133. Dim action As [Delegate] = weakAction.CreateAction()
    134. If action IsNot Nothing Then
    135. actions.Add(action)
    136. Else
    137. ' The target object is dead, so get rid of the weak action.
    138. weakActions.Remove(weakAction)
    139. End If
    140. Next
    141. ' Delete the list from the map if it is now empty.
    142. If weakActions.Count = 0 Then
    143. _map.Remove(message)
    144. End If
    145. End SyncLock
    146. Return actions
    147. End Function
    148. End Class
    149. #End Region
    150. #Region "WeakAction [nested class]"
    151. ''' <summary>
    152. ''' This class is an implementation detail of the MessageToActionsMap class.
    153. ''' </summary>
    154. Private Class WeakAction
    155. ReadOnly _delegateType As Type
    156. Friend ReadOnly _method As MethodInfo
    157. Friend ReadOnly _targetRef As WeakReference
    158. ''' <summary>
    159. ''' Constructs a WeakAction.
    160. ''' </summary>
    161. ''' <param name="target">The object on which the target method is invoked, or null if the method is static.</param>
    162. ''' <param name="method">The MethodInfo used to create the Action.</param>
    163. ''' <param name="parameterType">The type of parameter to be passed to the action. Pass null if there is no parameter.</param>
    164. Friend Sub New(target As Object, method As MethodInfo, parameterType As Type)
    165. If target Is Nothing Then
    166. _targetRef = Nothing
    167. Else
    168. _targetRef = New WeakReference(target)
    169. End If
    170. _method = method
    171. If parameterType Is Nothing Then
    172. _delegateType = GetType(Action)
    173. Else
    174. _delegateType = GetType(Action(Of )).MakeGenericType(parameterType)
    175. End If
    176. End Sub
    177. ''' <summary>
    178. ''' Creates a "throw away" delegate to invoke the method on the target, or null if the target object is dead.
    179. ''' </summary>
    180. Friend Function CreateAction() As [Delegate]
    181. ' Rehydrate into a real Action object, so that the method can be invoked.
    182. If _targetRef Is Nothing Then
    183. Return [Delegate].CreateDelegate(_delegateType, _method)
    184. Else
    185. Try
    186. Dim target As Object = _targetRef.Target
    187. If target IsNot Nothing Then
    188. Return [Delegate].CreateDelegate(_delegateType, target, _method)
    189. End If
    190. Catch
    191. End Try
    192. End If
    193. Return Nothing
    194. End Function
    195. End Class
    196. #End Region
    197. #Region "Fields"
    198. ReadOnly _messageToActionsMap As New MessageToActionsMap()
    199. #End Region
    200. End Class


    Edit: Achja, und jetzt teile mal das ganze auf. Gib die Views in ein Projekt, das ViewModel in ein Projekt und schau dann mal.
    ​In MVVM soll Das Model, das ViewModel und die View austauschbar sein. Wenn ich jetzt eine MVC Anwendung oder eine Windows 10 App machen will schau ich schön drein wenn ich im ViewModel direkt Fenster anspreche. Das hier:

    Quellcode

    1. ​These patterns provide a clean separation between the UI and the rest of the application.

    ​... ist aus dieser Quelle: msdn.microsoft.com/en-us/library/ff798384.aspx

    Schöne Grüße und Danke für den Input, einiges werde ich sicher verwenden.
    Grüße
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Nofear23m“ ()

    Nofear23m schrieb:


    1.) NotifyPRopertyChanged
    Warum ICloneable implementieren? Mir ist klar was ich damit machen kann.
    Jo, NotifyPRopertyChanged zählt zur Infrastruktur, und weil ich das nicht extra für dieses Sample reduziert hab, sind da Features drin, die hier konkret nicht gebraucht werden - Das mit dem Klonen ist nützlich, wenn man IEditableObject in bestimmter Weise implementieren will - weil da muss man ja einen vorherigen Zustand wieder herstellen können - und da ists evtl. praktisch, wenn man sich zuvor die Daten in form eines Klons "auf Seite gelegt hat".
    Aber: Habe ich jetzt einen Denkfehler. Die RelayCommand Schnittstelle hat keinen Parameterlosen, somit kann keine VM Klasse welches ein RelayCommand verwendet kopiert werden oder?
    Es ist auch nur eine Klon-Hilfe - da ist was mit Overridable eingebastelt, weil wie ein bestimmtes VM im Einzelnen zu klonen ist - das ist oft nach Bedarf nachzuarbeiten.
    Etwa ein RelayCommand ist wohl meist besser vom Klonen auszuschließen.
    Der Witz ist wohl das (Protected) MemberwiseClone(), was da ausgeführt wird. Weil das deckt alle Structure-Datentypen und zusätzlich String ab - also wenn man meine Viewmodelse klonen muss hat man deutlich weniger Schreibarbeit.

    Wozu PropIfDifferent? Wo liegt der Vorteil. Schon klar. Wenn der Wert gleich ist muss nicht die View aktualisieren. Sonst noch was?
    Jepp - dassis ein Prinzip: Events nur feuern, wenn wirklich nötig. Überflüssig gefeuerte Events können sehr auf die Performance gehen, aber auch beim Debuggen kann man da echt einen Vogel kriegen.

    ...
    MockDesignMode verwendest du dann für UnitTests richtig??
    Zum Testen, ja, in schwierigen Fällen. Richtig UnitTests was man allgemein darunter versteht mach ich garnet, bislang. Aber gut möglich, dasses sich dafür verwenden liesse.

    2.) Mainmodel (Eigentlich ja MainViewModel)

    Private WithEvents wndPerson As Window hat in einem ViewModel nix zu suchen!!
    Wer sagt das? Du?
    Weisst du... überzeugen kannman mich so nicht. Bring Argumente, nenn mir nachvollziehbare gute Gründe. Zeige an einem Beispiel, dasses so nicht geht.
    Weil ich habe am Beispiel gezeigt, dass es so geht. Und ich erreiche das Ziel - Re-enablen der Buttons beim Schließen des windows - exorbitant viel einfacher als die Klimmzüge über mehrere Projekte, Viewmodels und Interfaces hinweg, die ich vorgefunden hatte.

    Wenn du schon so genau bist das du meinst man muss einen Kunden speichern obwohl das nicht di efrgestellung war...
    nicht wegen Genauigkeit. Ich wollte es einfach bisserl realistischer haben - dass die bearbeitete Person einfach spurlos verschwindet widerstrebte mir.

    ... dann bitte im RelayCommand auch CanExecute vergeben. Speichern OHNE Kundendaten darf nicht möglich sein.
    Zustimm! Und ist es auch nicht - probiers aus :P
    Aber da kommen wir wohl wieder an die Sicherheits-Philosophie-Diskussion - weisst ja: ich finde es sinnlos und sogar schädlich, gegen Dinge abzusichern die garnicht vorkommen können.

    Private Sub wndPerson_Closed(sender As Object, e As EventArgs) Handles wndPerson.Closed Oh mein Gott.
    Wen meinst du - mich? Sehr schmeichelhaft, aber es muss sich um eine Verwechslung handeln. ;)

    Der Rest ist soweit eigentlich so wie ich es habe.
    hmm - ich find, ist doch iwie auch ziemlich anders ;)
    Aber zB in dem Punkt hast auch wieder recht: das mit dem BindingPicking hat nicht ganz gefehlt (ich hatte es nur erst ziemlich spät gefunden).

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ​Wer sagt das? Du?
    Weisst du... überzeugen kann man mich so nicht. Bring Argumente, nenn mir nachvollziehbare gute Gründe.


    ​Nein, ich sage das nicht. Siehe mein Edit. Wie war das mit Links folgen ;)

    ​Weist du. Ich finde es gut wenn mir jemand etwas aufzeigt was ich falsch oder schlecht mach. Aber wenn DU, der immer von sich behauptet das sein Code ach so perfekt ist hier ein Beispiel hochladest wo das ViewModel das View kennt dann muss ich sagen. Gut für dich das es geht, richtig ist es jedoch nicht.
    ​Das MVVM spezifiziert dies. Siehe wieder Link.

    ​Und übrigens: Ich KANN in deinem Beispiel alle Daten aus den Textfelder löschen und auf speichern klicken.
    Weiters: Du meinst meine RelayCommand Klasse wäre so schlecht und verwendet auch Is Nothing(obj) in deiner. Oder ist das eh meine. Toll
    Ausserdem: Du generierst einen Bindingfehler in deiner Solution:
    Spoiler anzeigen
    ​System.Windows.Data Error: 7 : ConvertBack cannot convert value '' (type 'String'). BindingExpression:Path=Size; DataItem='PersonVM' (HashCode=21522166); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String') FormatException:'System.FormatException: Die Eingabezeichenfolge hat das falsche Format.
    bei System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
    bei System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
    bei System.String.System.IConvertible.ToInt32(IFormatProvider provider)
    bei System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
    bei MS.Internal.Data.SystemConvertConverter.ConvertBack(Object o, Type type, Object parameter, CultureInfo culture)
    bei System.Windows.Data.BindingExpression.ConvertBackHelper(IValueConverter converter, Object value, Type sourceType, Object parameter, CultureInfo culture)'


    ​DU redest immer von Performance: Bindingfehler sind die wohl SCHLIMMSTE Bremse in der WPF. Egal ob diese nun einen Fehler verursachen oder nicht.

    ​Versteh mich bitte nicht falsch, ich hoffe ich darf dich kritisieren wie du auch mich. Du hast gemeint das du uns hier was lernen willst weil unser/mein vorhaben noch viel besser geht. Aber dann zeig uns auch wie es besser geht. Und bitte nicht wie es nicht MVVM konform ist.
    ​Ich hoffe das auch DU Kritik verträgst, austeilen kannst du ja auch.

    ​Fazit: Ich nehme ein bis zwei dinge sicherlich mit, vieles aber nicht und einiges Verurteile ich definitiv.

    ​Grüße
    Sascha

    ​PS: Nicht böse sein. :thumbup:
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Streitet euch nicht :)
    Eins vorweg ich habe mir keinen Code angeschaut, da ich aktuell kein VS an diesem Rechner habe. Morgen früh dann wieder.

    Ich finde die ganze Diskussion interessant vom Gesichtspunkten her, dass ich mir genau vorstellen kann, wie EDR sein Code aussieht :) Immerhin habe ich WPF mit seinen MVVM Tuts hier im Forum angefangen. Rein pragmatisch her hatte ich es auch so gemacht, Jedoch war dies eben der Stein des Anstoßes, dass das VM die View kennt. Klar ist dies der einfache Weg und er funktioniert, da dieser wie soll ich sagen der naheliegendste ist. Deswegen wollte ich auch den anderen Weg sehen.
    Rein Codemäßg und zeilenmäßig ist der pragmatische Weg auch kürzer. Aber bei MVVM gelten halt unter Umständen auch andere Gesichtspunkte.
    Das Problem an MVVM und WPF ist, ich habe es bereits angesprochen, dass die Lernkurve sehr flach ist und man viel Wissen über viele verschiedene Dinge benötigt (Binding, templates, Styles, VM, ....) um es richtig anzuwenden. Leider ist diese Forumssektion teilweise wie tot im Gegensatz zu der "Sonstige Problemstellungen" in der aber leider viel WinForms abgehandelt wird.
    Wenn man sich das Ganze selbst beibringt muss man also auf sein Google-Fu zurückgreifen und landet oft halt bei StackOverflow. Dort wird aber viel mit Codebehind oder programmatisch gelöst statt die Features von WPF zu nutzen. unabhängig davon sind halt auch mittlerweile viele Beiträge schon x Jahre alt und beziehen sich auf sehr alte Frameworks.
    Schlußendlich bleibt es jedem selbst überlassen wie er Problemstellungen löst, vor allem wenn es privater Code ist. Jedoch ist es nie verkehrt auch neben dem Tellerrand zu schauen. Ich für meinen Teil kann sagen, dass bei mir niemals die View ausgetauscht wird. Oder sich das Viewmodel ändert. Trotzdem ist es sicher nicht verkehrt das "richtige" Trennen vom VM, V und M zu lernen. Vielleicht bringt es ja irgendwann mal etwas. Man weiß ja nie wohin man sich (auch beruflich) entwickelt.
    MVVM ist ja kein Dogma. Was mir zum Beispiel an Nofear23m Code danfangs Probleme bereitet hat, ist dieses verzetteln von Code. Klar muss die View für sich entscheiden wie sie was rendert, aber ich hätte das trotzdem gerne zentral gesammelt wie zum Beispiel in Application.Ressources. Wenn ich über das VM sagen kann welches Control genutzt wird warum ein weiteres Lay einführen?

    @Nofear23m kannst du das mit den Messages näher ausführen? Anwendung und Nutzen? Ich kann leider aus deinem Code ndas nicht rauslesen.

    Das Beispiel von mir war natürlich realitätsfern. Es sollte nur aufzeigen, dass ich ein VM auf 2 Fenstern bearbeiten möchte und dass eine Trennung auf 2 VM eben nicht geht, da diese auf die gleiche Instanz des VM zurückgreifen. Dass ein Speichern von personen nicht geht oder sonst wie nicht ganz ausgereift ist, war mir klar und nicht meine Intention :)

    Gruß

    SKeks schrieb:

    Streitet euch nicht
    Oooch!
    Ich will aber!!

    ;)



    @Nofear23m:
    Sry - den Edit nicht gesehen - da siehste mal wie lange ich für sone Antwort brauche ;)
    Ja, MS sagt, das ist nicht MVVM, was ich verzapfe.
    Aber damit ist mir immer noch nicht gezeigt, was nun ein Problem damit ist, und welche Lösung besser ist. Deine Lösung findich nicht besser, weil ist vielfach komplizierter und äusserst undurchschaubar.

    Nofear23m schrieb:

    Und übrigens: Ich KANN in deinem Beispiel alle Daten aus den Textfelder löschen und auf speichern klicken.
    Ja, dann wird eben ein Datensatz mit lauter leer-einträgen gespeichert, aber dass kein Datensatz da ist, und man trotzdem Speichern klickst - kann nicht auftreten.
    Ach du meintest eine Validierung der Eingaben des vorhandenen Datensatzes? - ja - habich nicht dran gedacht - genau wie du!

    Komisch - den BindingFehler kriege ich nicht.

    Nofear23m schrieb:

    Du meinst meine RelayCommand Klasse wäre so schlecht
    Hmm?
    Kann mich nicht erinnern, das gesagt zu haben. Wenn doch, nehm ich das auf jeden Fall zurück. Jdfs. in meiner Variante hab ich das RelayCommand von dir übernommen (meins ist viel komplizierter (und für dieses Beispiel garnet besser)).

    Ups - jetzt guck ichs erstmalig an, und musses jetzt doch sagen: Dein RelayCommand geht so nicht:

    VB.NET-Quellcode

    1. Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
    2. AddHandler(value As EventHandler)
    3. If IsNothing(_canExecute) = False Then
    4. AddHandler CommandManager.RequerySuggested, value
    5. End If
    6. End AddHandler
    7. RemoveHandler(value As EventHandler)
    8. If IsNothing(_canExecute) = False Then
    9. AddHandler CommandManager.RequerySuggested, value
    10. End If
    11. End RemoveHandler
    12. RaiseEvent(sender As Object, e As EventArgs)
    13. End RaiseEvent
    14. End Event
    Bei RemoveHandler addest du den Handler! - ist sicher ein Versehen.
    Ach - und bei RaiseEvent erfolgt ja gar keine Aktion!

    Also da mach ich jetzt doch mal hin, wie ich gelernt hab, dass muss:

    VB.NET-Quellcode

    1. Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
    2. AddHandler(value As EventHandler)
    3. AddHandler CommandManager.RequerySuggested, value
    4. End AddHandler
    5. RemoveHandler(value As EventHandler)
    6. RemoveHandler CommandManager.RequerySuggested, value
    7. End RemoveHandler
    8. RaiseEvent(sender As Object, e As EventArgs)
    9. CommandManager.InvalidateRequerySuggested()
    10. End RaiseEvent
    11. End Event
    Obwohl - jetzt ausprobiert: es funktioniert auch ohne eine Aktion beim RaiseEvent - also ICommand.CanExecuteChanged ist schon sehr eigenartig.



    Iiieh!
    Da ist ja der MVB-Namespace in meiner eigenen Solution! <X
    Sofort alles löschen und neu aufnehmen (Dateianhang)!



    ...Beispiel ... ein VM auf 2 Fenstern bearbeiten möchte und dass eine Trennung auf 2 VM eben nicht geht, da diese auf die gleiche Instanz des VM zurückgreifen.
    Ja, sorry - ich hab nix beitragen können zum Thema.
    Sowohl Bindingpicking als auch Option Strict (meine ursprünglichen Anliegen) waren zumindest teilweise bekannt.
    Und meine Lösung, um dassselbe VM mit verschiedenen Views anzuzeigen ist bodenlos trivial und verstößt gegen MVVM.
    Was für mich ja keine heilige Kuh ist - ich verstoße halt ganz unverfroren gegen Pattern, wenn sie behindern statt zu helfen.
    Dateien
    • SimpleWpf.zip

      (27,11 kB, 56 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Hallo Leute

    @SKeks
    ​Klar muss die View für sich entscheiden wie sie was rendert, aber ich hätte das trotzdem gerne zentral gesammelt wie zum Beispiel in Application.Ressources. Wenn ich über das VM sagen kann welches Control genutzt wird warum ein weiteres Lay einführen?

    ​In meinem Beispiel habe ich ein weiteres Usercontrol dazwischen. Geht allerdings auch direkt wie duu willstin der Application.xaml. Probiers mal. Funzt. Dann hast du wieder alles auf einem Platz.

    ​kannst du das mit den Messages näher ausführen? Anwendung und Nutzen? Ich kann leider aus deinem Code ndas nicht rauslesen.

    ​Sicher, aber leider erst am Abend wenn ich wieder daheim bin.

    ​Das Beispiel von mir war natürlich realitätsfern.

    ​Wie? Völlig egal wenn es um die funktionsweise von etwas ganz anderen geht. Lass dich bitte nicht verwirren.

    @ErfinderDesRades
    ​Sry - den Edit nicht gesehen - da siehste mal wie lange ich für sone Antwort brauche

    ​OK, war auch gemein von mir, dachte ich war schnell genug mit dem Edit. ;)

    ​Aber damit ist mir immer noch nicht gezeigt, was nun ein Problem damit ist, und welche Lösung besser ist. Deine Lösung findich nicht besser, weil ist vielfach komplizierter und äusserst undurchschaubar.

    ​Es geht NICHT darum welche Lösung besser ist, es geht darum welche korrekt ist. DU meinst immer alle ausbessern zu müssen, nicht ich. MVVM impliziert die strikte Trennung der Layer.
    ​Wenn ich im Team Arbeite kann der "Designer" ungehindert Controls und Windows austauschen/umbenennen/abändern. Wenn das VM die V kennt kompiliert es nicht mehr, wo sich der Designer dann im Code nicht mehr auskennt und nicht weiter kann. Weiters, kann das selbe ViewModel für z.b. eine Windows 10 App oder eine HandyApp oder gut gemacht eine MVC Website verwendet werden.
    ​Du trennst ja auch Model vom ViewModel damit man die Datenzugriffsschicht austauschen kann. Was wenn ich später mal von EntityFramework auf reinen SQL umsteigen will. Oder auf NHibernate.
    ​Außerdem ist der Designer viel flexibler und muss nicht auf irgendwas aufpassen. Stickwort umbenennen.
    ​Wenn der Kunde ins Lastenheft schreibt, er hätte doch gerne mal später vielleicht eine App welche die selben oder einen Teil der Funktionalitäten hat musst du dich mit deinem Ansatz wieder neu hinsetzen und das ganze VM wieder neu runtertippen. Genau deshalb. Das "Problem" wie du sagst, ist nicht wie du es machst. Ist ja auch gut und sicher einfacher. Aber es ist nicht MVVM, FAKT!!!!!

    ​Dein RelayCommand geht so nicht:

    Lerne gerne was neues wie schon mehrmals geschrieben. Hast du eine bessere Lösung. Die funzt bei mir so tadellos.

    ​Und meine Lösung, um dass selbe VM mit verschiedenen Views anzuzeigen ist bodenlos trivial und verstößt gegen MVVM.
    Was für mich ja keine heilige Kuh ist - ich verstoße halt ganz unverfroren gegen Pattern, wenn sie behindern statt zu helfen.

    ​Moment, Moment. eines will ich klar stellen. Es wäre NIE zu dieser Diskussion gekommen wenn du nicht immer der jenige wärst der alle gern in die Schranken weist und meinst das dein Code der einzige wäre.
    ​Ja, ich habe einige Posts von dir gelesen und das kommt tatsächlich so rüber. Du meinst selbst das du "unverfroren" gegen das Pattern verstößt weil es behindert. (Whaaaat?)
    ​Oft behindert einem etwas, da geht man dem nicht aus dem Weg sondern stellt sich dem. War da nicht mal deine Devise?

    ​OK, Idee. Wir nennen dein Pattern nun MVM und belassen es dabei das es auch MVVM noch gebe.

    Hoffe das wir jetzt nicht dauernd zusammenstoßen, hab ich keinen bock drauf. Ich respektiere dich und weis das du was auf dem Kasten hast, auch respektiere ich deine Leistung hier im Forum.
    ​15 Beiträge am Tag im Durchschnitt. wow. Ich habe leider einen Arbeitgeber auch.

    Grüße
    ​Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##