Fragensteller
WPF - Verschiedene ViewModels - Zentrale Collections

Allgemeine Diskussion
-
Hallo Forum,
ein Fenster einer WPF Anwendung enthält ein TabControl. Für das Fenster möchte ich ein MasterViewModel als DataContext setzen, das MasterViewModel stellt für jedes einzelne TabItem (UserControl) ein eigenes (Sub) ViewModel als Eigenschaft bereit. Die ViewModels sollen auf die gleichen Auflistungen von Objekten zufreifen, damit bei Änderungen an Objekten in einem TabItem die anderen TabItems falls erforderlich automatisch synchronisiert werden.
Im MasterViewModel möchte ich die Daten in ObservableCollection-Member laden, diese bei Instanzierung der ChildViewModels als Parameter übergeben.
Ist dieser Ansatz sinnvoll?
Wo und wie sind die ObservableCollections idealerweise zu platzieren?
Viele Grüße, Jan
- Bearbeitet Jan Kornblum Dienstag, 10. Mai 2016 16:00
- Typ geändert Dimitar DenkovMicrosoft contingent staff, Administrator Donnerstag, 2. Juni 2016 12:32 Keine Rückmeldung des Fragenstellers
Alle Antworten
-
Hi Jan,
es gibt viele Lösungsmöglichkeiten. Um zu bestimmen, welche Lösungsmöglichkeit optimal ist, müssen Ziele (Aufgabenstellung) und Bewertungskriterien der betrachteten Varianten spezifiziert vorliegen.Verschiedene ViewModels für Teile der Oberfläche ist kein Problem. Setze den DataContext des betreffenden Container auf die gewünschte Instanz des ViewModels.
Als Datenquelle für die unterschiedlichen Sichten in den unterschiedlichen ViewModels solltest Du den gleichen Datenpuffer im Model nutzen. Dann gibt es auch keine Notwendigkeit, unterschiedliche Auflistungen zu synchronisieren. Wichtig ist nur, dass alle ViewModels dann auf die gleiche Pufferinstanz zugreifen.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Hallo Peter,
im MasterViewModel instanziere ich den Context, lade die Collections und instanziere die ChildViewModels. Letzteren übergebe ich bei Instanzierung als Parameter den Context sowie die geladenen jeweils notwendigen Collections.
MasterViewModel wird also einmal instanziert (DataContext vom Fenster) und ich kann die Container an die darin bereitgestellten Properties (ChildViewModels) binden, in denen alles Notwendige schon zur Verfügung steht.
Das funktioniert so und synchronisiert prima. Sicherlich gibt es elegantere Lösungen, mir genügt es um nun weiter in die Materie einzusteigen.
Viele Grüße, Jan
P.S. Erster Aha-Effekt sozusagen vor dem Hintergrund "Mit WinForms, DataTables und BindingSources ging das alles einfacher" ;)
-
Hi Jan,
ich habe es so verstanden, dass Du kein MVVM (Model-View-ViewModel) realisierst, sondern die Model-Aufgaben im ViewModel realisierst (in Deinem MasterViewModel). Damit es natürlich schwierig, 3-Schichten wie im MVVM aufzubauen und zu verwalten. Eleganter wäre, MVVM zu implementieren, indem der ViewModel nur die Geschäftslogik implementiert und sich die benötigten Daten vom Model holt. Im Sonderfall kann man natürlich auch den Context als Model ansehen.Wenn Du als ersten Aha-Effekt meinst, dass mit Winforms, DataTables und BindingSources alles einfacher ist, dann warte auf den zweiten Aha-Effekt. Der kommt, wenn Du mal etwas anpassen musst, wie beispielsweise in WindowsForms eine spezielle DataGrid-Spalte, in WindowsForms ein spezielles Aussehen von Steuerelementen in Abhängigkeit von Bedieneraktivitäten, in WindowsForms das Aussehen der TreeViewItems in Abhängigkeit vom Typ der Datenobjekte, In WindowsForms eine Anwendung für Smartphone usw. Einfache WindowsForms Anwendungen sind nicht einfacher zu erstellen als einfache WPF Anwendungen mit gleicher Funktionalität, vorausgesetzt man beherrscht WPF.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Hi Peter,
nein, ich meinte damit natürlich einen *positiven* Aha-Effekt! Zusätzlich mag ich XAML für die Oberflächengestaltung inzwischen auch nicht mehr missen :)
MVVM: Ups, bin eigentlich davon ausgegangen, dass ich das gerade implementiere... Die Views sind losgelöst und ohne Code-Behind. Das ViewModel enthält die Logik und stellt die Daten für die Views bereit. Gut, Model sind aktuell eben nur die Entities und der Context. Aber was ist das Model denn sonst bzw. wenn das nicht zum Model gehört, was sind dann Context und Entities?
Viele Grüße, Jan
- Bearbeitet Jan Kornblum Dienstag, 10. Mai 2016 22:13
-
Hi Jan,
MVVM ist kein Dogma und die Abgrenzung zwischen den Ebenen kann fließend sein. Entsprechend Aufgabenstellung kann es z.B. sinnvoll sein, für User- und CustomControls keine ViewModels vorzusehen, sondern dies im Codebehind zu machen. Und auch da kann man Datenbindung nutzen oder die Eigenschaften der Steuerelemente direkt beschreiben oder auslesen.Dasselbe betrifft die Model-Ebene. Der ViewModel nutzt die Daten der Model-Ebene. Wenn der ViewModel die Load-Methode ausführt, kann das bereits als Aufgabe des Models betrachtet werden und ist in diesem Fall in die Model-Ebene einzuordnen.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Hi Peter,
mir fällt es etwas schwer, das "Model" richtig einzuordnen. EF generiert ja schon Models (Model.tt und darunter). Das sind nicht "die" Models, auf die sich das erste "M" in MVVM bezieht, richtig? Meinem Verständnis nach sieht der Zusammenhang so aus:
View - ViewModel - Model - EF
Trifft das zu? Am Beispiel "Customer": In einer View sollen neben "Name" und "Adresse" vom Kunden auch ein berechneter Wert "Umsatz / Jahr" ausgegeben werden. Diese "calculated" Eigenschaft könnte ich nun entweder:
- In das EF-Model integrieren (schlecht, hat da nichts zu suchen)
- In das ViewModel integrieren (schlecht, nicht wiederverwertbar)
- Ein "Model" Customer implementieren
Nach meinem Verständnis ist Variante 3 die richtige. Aber wie sieht dieses Model dann aus? Hält es eine private Instanz auf die EF-Entity, reicht nur die "gewünschten" Eigenschaften durch und stellt zusätzlich die "calculated" Eigenschaft bereit?
Viele Grüße, Jan
- Bearbeitet Jan Kornblum Mittwoch, 11. Mai 2016 19:20
-
Hi Jan,
MVVM ist kein Dogma. Es zwingt nicht, auf einer Ebene nur eine Klasse zu haben (nur eine Sicht, nur eine Klasse mit Geschäftslogik und nur eine Klasse, die die Daten besorgt und hält). Ein berechneter Wert kann auf allen Ebenen sinnvoll sein, z.B. auch in der Sicht, indem ein Converter aus den Daten eine berechnete "Anzeige" erzeugt, wenn diese Information nur für eine bedienerfreundliche Anzeige benötigt wird und dem ViewModel die "Rohdaten" reichen. Andererseits, wenn aus Gründen der Normalisierung oder bei Zugriff auf bereits bestehende Datenstrukturen Werte benötigt werden, die z.B. aus verschiedenen Tabellen gewonnen und "berechnet" werden müssen, kann dieser Prozess auch im Model sinnvoll sein. Mit Model ist nicht der EF-Model allein gemeint, sondern die Ebene Model.Als Gegenfrage solltest Du mal überlegen, wo Du beispielsweise die Parametrierung des ConnectionStrings ansiedelst, um alternativ Testdaten und Produktivdaten zu nutzen bzw. Produktivdaten unterschiedlicher Mandanten (unterschiedliche Datenbanken). Dem ViewModel solltes es völlig egal sein, wo die Daten herkommen bzw. abgelegt werden. Aus meiner Sicht hat so etwas mit der Geschäftslogik nichts zu suchen. Die Geschäftslogik hat maximal Kennzeichen an die Model-Ebene weiterzureichen (z.B. bei Mandantenfähigkeit), mit deren Hilfe dann die konkrete Datenbank bestimmt werden kann. Das ist aber Ansichtssache bzw. es sind Details der Konzeption.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Hi Peter,
zur ConnectionString Thematik erstelle ich besser einen eigenen Thread. Um nochmal auf den beispielhaften berechneten Wert "Anzahl Umsätze" zurückzukommen:
Customer -> Project -> Activity (~ Umsatz)
ActivitiesView erfasst neue Umsätze (Activity) oder editiert vorhandene. CustomerDetailView stellt die Customer Details dar, inklusive berechneter Property im EF-Model "ActivitiesCount":
Partial Public Class Customer Public Readonly Property ActivitiesCount as Integer Get Return Me.Projects.Sum(Function(p) p.Activities.Count) End Get End Property End Class
Wie kann das Customer-Model / die CustomerDetailView darüber benachrichtigt werden, dass in ActivitiesView Elemente geändert wurden bzw. die im ViewModel vorhandene Auflistung "Activities" sich geändert hat?
Derzeit holt sich die Customer-View den neuen Wert für "ActivitiesCount" nur dann, wenn eine der in der Kette involvierten Auflistungen geändert (Add, Remove) wird UND ZUSÄTZLICH der ausgewählte Kunde neu gesetzt wird (Wechsel in anderen Kunden und zurück).
Wo kann eine Benachrichtigung fehlen oder ein anderer Fehler vorliegen?
Viele Grüße, Jan
- Bearbeitet Jan Kornblum Donnerstag, 12. Mai 2016 16:30
-
Hi Jan,
bei Deinem Beispiel bleibt die Frage, warum benötigt der ViewModel diese Eigenschaft? Wenn die Geschäftslogik diesen Wert benötigt, dann kann sie sich diesen Wert selbst ermitteln. Wenn dieser Wert nur in der Anzeige benötigt wird, dann reicht die Bindung an die Count-Eigenschaft:<Label Content="{Binding ViewChilds.Count}"/>
Der Model verwaltet inline einen Teil der extern gehaltenen Daten. Zusätzliche Eigenschaften, wie Count, die indirekt aus der Menge der geladenen Daten abgeleitet werden können, sind vergeudeter Aufwand, der nicht nötig ist.
Anders sieht es aus mit Daten, die NUR aus den externen Daten ermittelt werden können, wie beispielsweise Anzahl von Datensätzen, ohne das die Daten selbst geladen werden. In diesem Fall ist aber genau zu spezifizieren, wie diese Mengenänderungen vom externen Datenbanksystem gemeldet werden. Das kann u.U. recht komplex werden, insbesondere, wenn sich im Mehrnutzerbetrieb die Daten oft ändern.
In jedem Fall, wenn Änderungen der Werte der Eigenschaften aus dem Model bis an die Oberfläche gemeldet werden soll, muss der Verursacher melden, dass sich ein Wert geändert hat und diese Meldung muss dann bis an die Oberfläche weitergemeldet werden.
In Deinem Fall ändert sich die Auflistung, egal, ob durch das eigene Programm oder durch Neuladen im Ergebnis eines anderen Ereignisses. Die Änderung der Auflistung muss dann abgefangen werden und z.B. durch NotifyPropertyChanged weitergemeldet werden. Wenn diese Meldekette nicht unterbrochen ist, dann ändert sich auch die Anzeige in der Oberfläche.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Hi Peter,
die direkt ableitbare Eigenschaft "Count" war zur Vereinfachung hier nur stellvertretend für eine eigentlich komplexere Berechnung angeführt.
Ja, irgendwo muss eine Unterbrechung in der Meldekette vorliegen. Mal sehen ob sie sich aufspüren lässt.
Meine Posts beziehen sich aktuell übrigens nur auf eine überschaubare Applikation zum Einstieg in die WPF, EF, MVVM Materie. Externe Daten usw., wie sie die andere dir bekannte Anwendung enthält, sind hier erst mal kein Thema.
Viele Grüße, Jan
-
Hier mal 3 Screenshots, um das Vorhaben etwas zu veranschaulichen...
- Bearbeitet Jan Kornblum Freitag, 13. Mai 2016 17:29
-
Hallo Peter,
eigentlich synchronisiert alles richtig, auch zwischen den Views, bis auf eines:
Das Objekt "Activity" hat einen Verweis auf "Project". Wenn in der ActivitiesView für ein bestehendes Activity-Objekt nun der Verweis geändert wird auf ein anderes Project-Objekt, dann bekommt die InvoicesView (Activities gefiltert nach gewähltem Project bzw. Customer ausgegeben) das nicht mit. Es ist also das "Umhängen" eines Childs an einen anderen Master, was nicht bis zur Oberfläche synchronisiert.
Hier fehlt mir der Ansatz, an welcher Stelle ggf. eine zusätzliche Benachrichtigung ausgelöst werden muss. Und ob das wirklich erforderlich ist, denn eigentlich verfügen die EF generierte Objekte doch über alle erforderlichen Benachrichtigungen...?
Der Filter für die nicht synchronisierende InvoicesView (ist das der Haken?):
ActivitiesNotAssignedView.Filter = New Predicate(Of Object)(Function(x) _ CBool(CType(x, Activity).Project.Customer.Equals(_SelectedCustomer) AndAlso CBool(CType(x, Activity).Invoice Is Nothing)) )
Viele Grüße, Jan
-
Hi Jan, aus Deiner Oberfläche leite ich ViewModels ab mit folgenden Eigenschaften:
1. Sicht auf Kunden
2. Aus der Sicht auf Kunden (1.) aktuell ausgewählter Kunde
3. Sicht auf Projekte
4. Aus der Sicht auf Projekte (3.) aktuell ausgewähltes Projekt
5. Sicht auf Verknüpfungstabelle "Einträge", die eine n:m-Beziehung zwischen Kunden und Projekten verwaltet und weitere Eigenschaften pro Verknüpfung (-Objekt) enthält
6. Aus der Sicht auf Verknüpfung (5.) aktuell ausgewählte Verknüpfung
7. Rechnungsliste mit Fremdschlüssel auf Kunde, wobei über Filter ("Rechnung wählen") nur eine Rechnung angezeigt wird
8. Sicht auf Verknüpfungstabelle "Zeiten zugewiesen", die eine n:m-Beziehung zwischen Rechnung und Daten aus Verknüpfungstabelle zwischen Kunden und Projekte enthält (s. 5.), gefiltert nach Rechnung (s. 7.).
9. Sicht auf Verknüpfungstabelle "Einträge", gefiltert nach Kunde und auf fehlende Entsprechung in Verknüpfungstabelle "Zeiten zugewiesen".
Wenn jetzt aus 9. ein Datenobjekt ausgewählt wird und dazu in 8. ein Datenobjekt hinzugefügt wird, dann müssen beide Sichten 8. und 9. Veränderungen melden. Da 8. vermutlich eine ObservableCollection zu Grunde liegt, braucht da nicht gemacht zu werden. Für die Eigenschaft, die 9. bereitstellt, muss natürlich NotifyPropertyChanged ausgelöst werden, damit Deine Abfrage erneut ausgeführt wird (s. Deinen Beitrag).
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut
- Bearbeitet Peter Fleischer Montag, 16. Mai 2016 13:28
-
Hi Peter,
danke dass du hier so ins Detail gegangen bist. Tatsächlich nutze ich derzeit nur 3 ViewModels, für jeden Tab eines.
8 und 9 basieren auf der gleichen ObservableCollection, nur jeweils unterschiedlich gefiltert via ICollectionView. Die GridViews von 8 und 9 beinhalten zusätzlich eine Spalte mit einem Hyperlink "hinzufügen" bzw. "entfernen". Klick darauf setzt den Verweis auf die aktuelle Rechnung bzw. entfernt diesen.
Wenn ich die richtig verstehe erreiche ich die gewünschte Synchronisation also durch weitere Modifizierung des T4 Templates, so dass nicht nur ObservableCollections generiert werden sondern zusätzlich auch INotifyPropertyChanged für die Properties.
Es gibt nicht zufällig irgendwo ein T4 Template, in das beides schon implementiert ist?
Viele Grüße, Jan
-
Hi Jan,
nach Deiner Schilderung kann also ein Zeiteintrag 3 Fremdschlüssel haben: Kunde und Projekt als Pflicht und Rechnung optional. Mit dem Eintrag eines Rechnungs-Fremdschlüssels werden die Sichten auf "Zeiten zugewiesen" und "Zeiten nicht zugewiesen" gefiltert. In diesem Fall brauchst Du nur jeweils ein NotifyPropertyChanged für jede der beiden Sichten Ausführen, wenn der Frendschlüsseleintrag geändert wird.Hier mal eine Demo:
XAML:
<Window x:Class="Window20" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApplication1VB" mc:Ignorable="d" Title="Window20" Height="300" Width="500" DataContext="{Binding RelativeSource={RelativeSource Self}}" Name="fenster"> <Window.Resources> <Style TargetType="Label"> <Setter Property="Margin" Value="5"/> <Setter Property="HorizontalAlignment" Value="Right"/> </Style> </Window.Resources> <Grid Margin="0 50 0 0"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="150"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <DataGrid Grid.Row="0" Grid.Column="0" ItemsSource="{Binding View1}" CanUserAddRows="False" /> <Grid Grid.Row="1" Grid.Column="0" DataContext="{Binding View1Detail}" > <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Label Grid.Row="0" Grid.Column="0" Content="ID"/> <Label Grid.Row="0" Grid.Column="1" Content="{Binding ID}"/> <Label Grid.Row="1" Grid.Column="0" Content="Info"/> <Label Grid.Row="2" Grid.Column="1" Content="{Binding Info}"/> </Grid> <DataGrid Grid.Row="0" Grid.Column="1" ItemsSource="{Binding View2}" CanUserAddRows="False"> <DataGrid.Columns> <DataGridTemplateColumn> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Button Content="Entfernen" Command="{Binding Path=Cmd, ElementName=fenster}" CommandParameter="Remove"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> <DataGrid Grid.Row="1" Grid.Column="1" ItemsSource="{Binding View3}" CanUserAddRows="False"> <DataGrid.Columns> <DataGridTemplateColumn> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Button Content="Zuweisen" Command="{Binding Path=Cmd, ElementName=fenster}" CommandParameter="Add"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> </Grid> </Window>
Und der Code dazu:
Imports System.Collections.ObjectModel Imports System.ComponentModel Imports System.Runtime.CompilerServices Public Class Window20 Implements INotifyPropertyChanged Private cvs1 As CollectionViewSource Public ReadOnly Property View1 As ICollectionView Get If cvs1 Is Nothing Then cvs1 = New CollectionViewSource cvs1.Source = GetCol1() AddHandler cvs1.View.CurrentChanged, Sub() View1Detail = cvs1.View.CurrentItem End Sub End If Return cvs1.View End Get End Property Private _view1Detail As Object Public Property View1Detail As Object Get Return Me._view1Detail End Get Set(value As Object) If Me._view1Detail IsNot value Then Me._view1Detail = value OnPropertyChanged() OnPropertyChanged("View2") OnPropertyChanged("View3") End If End Set End Property Private Function GetCol1() As ObservableCollection(Of Data1) Dim col As New ObservableCollection(Of Data1) For i = 1 To 10 col.Add(New Data1 With {.ID = i, .Info = $"Zeile {i}"}) Next Return col End Function Private cvs2 As CollectionViewSource Public ReadOnly Property View2 As ICollectionView Get If cvs2 Is Nothing Then cvs2 = New CollectionViewSource cvs2.Source = Col2 End If cvs2.View.Filter = Function(p As Object) Dim d = TryCast(p, Data2) If d Is Nothing OrElse Not d.FK.HasValue OrElse Me._view1Detail Is Nothing Then Return False Return d.FK.HasValue AndAlso d.FK.Value = CType(Me._view1Detail, Data1).ID End Function Return cvs2.View End Get End Property Private cvs3 As CollectionViewSource Public ReadOnly Property View3 As ICollectionView Get If cvs3 Is Nothing Then cvs3 = New CollectionViewSource cvs3.Source = Col2 End If cvs3.View.Filter = Function(p As Object) Dim d = TryCast(p, Data2) Return d IsNot Nothing AndAlso Not d.FK.HasValue AndAlso Me._view1Detail IsNot Nothing End Function Return cvs3.View End Get End Property Private _col2 As ObservableCollection(Of Data2) Public ReadOnly Property Col2 As Object Get If Me._col2 Is Nothing Then Dim rnd As New Random Me._col2 = New ObservableCollection(Of Data2) For i = 1 To 100 Me._col2.Add(New Data2 With {.ID = i, .FK = Nothing, .Info = $"Zeile {i}"}) Next End If Return Me._col2 End Get End Property Public ReadOnly Property Cmd As ICommand Get Return New RelayCommand(AddressOf CmdExec) End Get End Property Private Sub CmdExec(obj As Object) Dim d As Data2 Select Case obj.ToString Case "Add" d = TryCast(Me.cvs3.View.CurrentItem, Data2) If d Is Nothing Then Exit Sub d.FK = CType(Me._view1Detail, Data1).ID Case "Remove" d = TryCast(Me.cvs2.View.CurrentItem, Data2) If d Is Nothing Then Exit Sub d.FK = Nothing End Select OnPropertyChanged("View2") OnPropertyChanged("View3") End Sub #Region " OnpropertyChanged" Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Friend Overloads Sub OnPropertyChanged(<CallerMemberName> Optional propName As String = "") RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName)) End Sub #End Region Public Class Data1 Public Property ID As Integer Public Property Info As String End Class Public Class Data2 Public Property ID As Integer Public Property FK As Integer? Public Property Info As String End Class End Class
Und dazu noch die RelayCommand-Klasse.--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut -
Hi Peter,
nein, ein Zeiteintrag hat nur 2 Fremdschlüssel, "Rechnung" und "Projekt". "Projekt" selbst hat dann wiederum einen FK auf "Kunde".
Ich werde dein Beispiel ausprobieren, aber um nochmal kurz auf meine vorangegangene Frage zurückzukommen: Wenn die Entity-Properties mit INotifyPropertyChanged generiert werden, dann müsste mein Code doch auch zum gewünschten Ergebnis führen, oder?
Viele Grüße, Jan
-
Hi Jan,
wenn ein Zeiteitrag keine Verknüpfung zur Rechnung hat, wie soll dann erkannt werden, welche Zeiteinträge einer Rechnung zugeordnet sind? Ich war davon ausgegangen, dass es eine Verknüpfungstabelle gibt. Du schreibst, dass das nicht so ist und dass "Zeiten zugewiesen" und "Zeiten nicht zugewiesen" in der gleichen Tabelle stehen. Wie werden dann die Zustände unterschieden? Was ist dann in Deinem Codeauszug "Invoice", wenn es keine Zuordnung zur Rechnung gibt?Wenn eine Eigenschaft eines Datenobjektes NotifyPropertyChanged auslöst, dann wird die Anzeige dieses Eigenschaftswertes aktualisiert, nicht aber eine gefilterte Sicht. Das die Änderung auslösende Ereignis muss am Methodenende auch noch über NotifyPropertyChanged mitteilen, welche gefilterte Sicht sich geändert hat.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
Kommas richtig setzen!
Schüler sagen, Lehrer haben es gut.
Schüler, sagen Lehrer, haben es gut