Entity Framework - MVVM - Verständnisfrage und ein paar Problemchen

  • WPF

Es gibt 29 Antworten in diesem Thema. Der letzte Beitrag () ist von kaifreeman.

    Entity Framework - MVVM - Verständnisfrage und ein paar Problemchen

    Hallo zusammen,

    Nach dem mich der ErfinderdesRades überzeugt hat das das MVVM Pattern und WPF einfach gut zusammenpassen hab ich mein komplettes Testprogramm neu geschrieben ich habe versucht das MVVM komplett durchzuziehen dazu habe ich ein paar Verständnisfragen und auch ein paar Problemchen ich packe das alles in diesen Thread rein.

    Problem 1: Einbindung des MainModels in die Applikation

    Das Mainmodel bzw. die Klasse wird ja in der Application.xaml als Application Resource eingebunden:

    XML-Quellcode

    1. <Application.Resources>
    2. <local:MainModel x:Key="Mainmodel"/>
    3. </Application.Resources>


    in der Error List bringt er mir aber diese Meldung:
    Error No connection string named 'data_modelContainer' could be found in the application config file.


    In der app.config ist aber der Connection String hinterlegt und das Programm funktioniert auch:

    XML-Quellcode

    1. <connectionStrings>
    2. <add name="data_modelContainer" connectionString="metadata=res://*/Model.data_model.csdl|res://*/Model.data_model.ssdl|res://*/Model.data_model.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.\SQLEXPRESS;initial catalog=hhhb_template2;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
    3. </connectionStrings>


    Ich habe schon rumgesucht anscheinend muss man den ConnectionString kopieren oder so aber irgendwie werd ich da nicht ganz schlau?

    Verständnisfragen

    Das MVVM Pattern ist mir soweit klar ich habe ein Modell das die Daten definiert, ein Viewmodel das eine Art Controller ist und die Logik bereitstellt sowie die View die alles präsentiert soweit so gut.

    Beim Entity Framework werden die Entitäten als Klassen implementiert => Viewmodel
    Das MainModel ist eine weitere Klasse die die Logik stellt hier fahren alle Entitäten als Listen zusammen und werden bearbeitet (CRUD)

    Ist das soweit korrekt?

    das führt nun zu meinem 2. Problem

    Ich greife mir jetzt mal eine Entität aus meinem Modell heraus:
    trans_status_v01 eine simple Tabelle mit einigen wenigen Propertys.

    VB.NET-Quellcode

    1. Partial Public Class trans_status_v01
    2. Public Property status_id As Integer
    3. Public Property bezeichnung As String
    4. Public Property color As String = "#000000"
    5. Public Property symbol As Nullable(Of Byte)
    6. Public Property aktiv As Boolean = true
    7. Public Overridable Property nv_transstatus_trans As ICollection(Of transactions_v01) = New HashSet(Of transactions_v01)
    8. End Class


    Im Mainmodel habe ich jetzt das "Viewmodel" eingebunden (der Übersichtlichkeit halber Zeige ich hier nur die Region die die Entität betrifft:

    VB.NET-Quellcode

    1. #Region "Buchung:Status"
    2. #Region "Commands"
    3. Public Property cmdAddTransStatus() As New RelayCommand(Sub() Transstati.AddNewItem(New trans_status_v01() With {.bezeichnung = "<New Status>"}), Function() True)
    4. Public Property cmdSaveTransStatus() As New RelayCommand(AddressOf TransStatusSave, Function() __transstatus.Count > 0)
    5. Public Property cmdColorTransStatus() As New RelayCommand(AddressOf TransStatusColor, Function() __transstatus.Count > 0)
    6. #End Region
    7. Public WithEvents __transstatus As New ObservableCollection(Of trans_status_v01)()
    8. Public Property Transstati() As New ListCollectionView(__transstatus)
    9. Private Sub TransStatusLoad()
    10. __transstatus.Clear()
    11. dbo.trans_status_v01.Load
    12. __transstatus = dbo.trans_status_v01.Local
    13. Transstati.MoveCurrentToFirst()
    14. Debug.Print(__transstatus.Count.ToString)
    15. Debug.Print(Transstati.Count.ToString)
    16. End Sub
    17. Private Sub TransStatusSave()
    18. Debug.Print(__transstatus.Count.ToString())
    19. dbo.SaveChanges()
    20. End Sub
    21. ''' <summary>
    22. ''' Behandelt das ändern der Schriftfarbe für ein Objekt vom Typ TransStatus
    23. ''' </summary>
    24. Private Sub TransStatusColor()
    25. Dim v_color As Color = get_color_from_dialog()
    26. If Not v_color = Nothing Then
    27. Dim v_helper = CollectionViewSource.GetDefaultView(__transstatus.ToList)
    28. Dim v_transstatus As trans_status_v01 = CType(v_helper.CurrentItem, trans_status_v01)
    29. v_transstatus.color = v_color.ToString
    30. End If
    31. End Sub
    32. Private Sub __transstatus_CollectionChanged(sender As Object, e As Specialized.NotifyCollectionChangedEventArgs) Handles __transstatus.CollectionChanged
    33. If e.Action <> Specialized.NotifyCollectionChangedAction.Remove Then Return
    34. If Not Transstati.MoveCurrentToNext Then Transstati.MoveCurrentToPrevious()
    35. End Sub
    36. #End Region


    Ich überlade also das Viewmodel in eine ObservableCollection __transstatus diese wiederum sollte dem Beispiel von EdR folgend (EFMostSimple) eine ListCollectionView "synchronisieren" was es nicht tut, das wiederum liegt wie mir EdR erklärt hat daran das die transstati() nichts von der Änderung in der OCol __transstatus mitbekommt wegen der Bindung dafür habe ich noch keine Lösung gefunden derzeit binde ich einfach alles an die __transstatus Collection das funktioniert soweit auch recht gut.

    Nun habe ich mir eine View gebastelt die Links eine Listview beinhaltet und rechts Textfelder usw. für die Propertys:

    XML-Quellcode

    1. <Window x:Class="dia_verw_trans_status"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    6. xmlns:local="clr-namespace:HHHB2"
    7. mc:Ignorable="d"
    8. Title="Buchung: Status" Height="300" Width="450" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner"
    9. DataContext="{Binding Source={StaticResource Mainmodel}}">
    10. <Grid>
    11. <Grid.RowDefinitions>
    12. <RowDefinition Height="25"/>
    13. <RowDefinition/>
    14. </Grid.RowDefinitions>
    15. <Grid.ColumnDefinitions>
    16. <ColumnDefinition Width="150"/>
    17. <ColumnDefinition/>
    18. </Grid.ColumnDefinitions>
    19. <StackPanel x:Name="stackPanel" Orientation="Horizontal" DockPanel.Dock="Top">
    20. <StackPanel.Background>
    21. <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
    22. <GradientStop Color="#FFAEAEAE" Offset="1"/>
    23. <GradientStop Color="#FFAEAEAE"/>
    24. <GradientStop Color="#FFC7C7C7" Offset="0.5"/>
    25. </LinearGradientBrush>
    26. </StackPanel.Background>
    27. <Button Margin="1" ToolTip="Erstellen" Background="{x:Null}" BorderBrush="{x:Null}" Command="{x:Static local:central_commands.Folder_Add_Edit}">
    28. <Image Source="/Resources/Images/but_add.png" />
    29. </Button>
    30. <Button Margin="1" ToolTip="Bearbeiten" Background="{x:Null}" BorderBrush="{x:Null}">
    31. <Image Source="/Resources/Images/but_edit.png" />
    32. </Button>
    33. <Button Margin="1" ToolTip="Löschen" Background="{x:Null}" BorderBrush="{x:Null}">
    34. <Image Source="/Resources/Images/but_delete.png" />
    35. </Button>
    36. </StackPanel>
    37. <ListView x:Name="lvw_status" Grid.Column="0" Grid.Row="1" ItemsSource="{Binding __transstatus}" DisplayMemberPath="bezeichnung" IsSynchronizedWithCurrentItem="True"></ListView>
    38. <StackPanel Grid.Column="1" Grid.RowSpan="2" DataContext="{Binding __transstatus/, Mode=TwoWay, UpdateSourceTrigger=Default}">
    39. <DockPanel>
    40. <Label x:Name="lbl_bezeichnung" Margin="3" Content="Bezeichnung" DockPanel.Dock="Left"/>
    41. <TextBox x:Name="txt_bezeichnung" Margin="3" DockPanel.Dock="Left" Text="{Binding bezeichnung}"/>
    42. </DockPanel>
    43. <DockPanel>
    44. <Label Margin="3" Content="Farbe" DockPanel.Dock="Left" Width="{Binding ActualWidth, ElementName=lbl_bezeichnung, Mode=OneWay}"/>
    45. <Button Command="{Binding Source={StaticResource Mainmodel},Path=cmdColorTransStatus}" Margin="3" Width="{Binding ActualHeight, Mode=OneWay, RelativeSource={RelativeSource Self}}">
    46. <Image Source="/HHHB2;component/Resources/Images/search.png"/>
    47. </Button>
    48. <TextBox x:Name="txt_color" Margin="3" DockPanel.Dock="Left" Text="{Binding color, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    49. </DockPanel>
    50. <DockPanel LastChildFill="False">
    51. <Label Margin="3" Content="Symbol" DockPanel.Dock="Left" Width="{Binding ActualWidth, ElementName=lbl_bezeichnung, Mode=OneWay}"/>
    52. <Button x:Name="cmd_search_image" Margin="3" DockPanel.Dock="Left" Width="{Binding ActualHeight, Mode=OneWay, RelativeSource={RelativeSource Self}}">
    53. <Image Source="/HHHB2;component/Resources/Images/search.png"/>
    54. </Button>
    55. <Border Margin="3" Background="Gainsboro" Width="24" Height="24">
    56. <Image x:Name="img_symbol" Width="24" Height="24" Stretch="Fill"/>
    57. </Border>
    58. </DockPanel>
    59. <DockPanel>
    60. <Label Margin="3" Content="Aktiv" DockPanel.Dock="Left" Width="{Binding ActualWidth, ElementName=lbl_bezeichnung, Mode=OneWay}"/>
    61. <CheckBox VerticalAlignment="Center" Margin="3" DockPanel.Dock="Left" IsChecked="{Binding aktiv}"/>
    62. </DockPanel>
    63. <DockPanel LastChildFill="False" HorizontalAlignment="Right">
    64. <Button Margin="5,0,2,0" DockPanel.Dock="Right" Content="Abbrechen" MinWidth="65" IsCancel="True"/>
    65. <Button Margin="0" DockPanel.Dock="Right" Content="Speichern" MinWidth="70" IsDefault="True" Command="{Binding Source={StaticResource Mainmodel},Path=cmdSaveTransStatus}"/>
    66. </DockPanel>
    67. </StackPanel>
    68. </Grid>
    69. </Window>


    In diese Bind ich mein Mainmodel ein und Binde die Listview an die Collection, das Stackpanel hat den Datacontext und das ganze ist synchronisiert. Soweit so gut funktioniert hier ergeben sich für mich wiederum 2 Probleme:
    Wähle ich in der Listview ein Element aus und Editiere es dann im Stackpanel z.b. Namensänderung speichert er mir das sofort synchronisiert aber nicht die Listview wie eigentlich gewünscht erst wenn ich in der Listview einen Eintrag neu wähle und zurückspringe...
    Ähnliches Problem ergibt sich bei meinem Command cmdColorTransStatus dieses wird über einen Button aufgerufen holt einen Colorpicker Dialog und schreibt ihn zurück in die Collection funktioniert auch ganz gut aber hier bekommt die Textbox die an die Property color gebunden ist nichts davon mit
    Lustigerweise werden die Änderungen aber sofort in der Collection übernommen (nicht zurück in die Datenbank) aber der User hat damit keine Möglichkeit seine Änderungen rückgängig zu machen....

    Ich habe schon versucht das EF Model um die Hilfsklasse PropertyChanged zu erweitern aber auch das funktioniert nicht.

    Tut mir leid das der Thread mega lange ist aber ich versuche wirklich das Thema MVVM zu verstehen und es vernünftig einzusetzen ;(
    mfG.
    Stephan

    kaifreeman schrieb:

    Error No connection string named 'data_modelContainer' could be found in the application config file.
    Beachte, dass bereits im Designer der Konstruktor des Mainmodels ausgeführt wird, und auch Fehler werfen kann, die sich dann so im Xaml äussern.
    Ist schwierig zu debuggen, insbesondere, wenn das Mainmodel dann zur Laufzeit fehlerfrei hochfährt.

    Bei deim Code blick ich ühaupt nicht durch - was soll zB das hier:

    VB.NET-Quellcode

    1. Private Sub TransStatusColor()
    2. Dim v_color As Color = get_color_from_dialog()
    3. If Not v_color = Nothing Then
    4. Dim v_helper = CollectionViewSource.GetDefaultView(__transstatus.ToList)
    5. Dim v_transstatus As trans_status_v01 = CType(v_helper.CurrentItem, trans_status_v01)
    6. v_transstatus.color = v_color.ToString
    7. End If
    8. End Sub
    dieses v_helper - krams?
    wäre das so nicht logischer?

    VB.NET-Quellcode

    1. Private Sub TransStatusColor()
    2. Dim v_color As Color = get_color_from_dialog()
    3. If v_color isnot Nothing Then
    4. Dim transstatus = CType(Transstati.CurrentItem, trans_status_v01)
    5. transstatus.color = v_color.ToString
    6. End If
    7. End Sub
    Funktioniert das, oder zickt das rum?
    Hallo
    Ok d.h. Mit der Fehlermeldung leben.

    Zum anderen Thema der v_helper dazu da um auf das aktuelle der ObservableColl zuzugreifen eine Art Workaround weil eben die Listcollection zickt wie wir schon einmal hatten wird diese nicht aktualisiert wenn in der ObsColl mit .local die Datenbank reingeladen wird d.h. Itemcount der __transstatus ist 3 der der Transstati ist 0 da half mir auch dein Example EF Simple nicht...
    Vielleicht interpretiere ich auch das Viemodel falsch? (sry schreibe grad vom Handy wg. Rechtschreibung usw.
    mfG.
    Stephan

    kaifreeman schrieb:

    Itemcount der __transstatus ist 3 der der Transstati ist 0

    glaub ich nicht.

    probierma

    VB.NET-Quellcode

    1. Private Sub TransStatusColor()
    2. messagebox.Show(string.concat("Transstati: ",Transstati.Count,", trans_status_v01: ",trans_status_v01.Count))
    3. Dim v_color As Color = get_color_from_dialog()
    4. If v_color isnot Nothing Then
    5. Dim transstatus = CType(Transstati.CurrentItem, trans_status_v01)
    6. transstatus.color = v_color.ToString
    7. End If
    8. End Sub
    Wäre sehr sonderbar, wenn die Counts sich unterschieden - oder hast du Filter drinne inne CollectionView?



    Und natürlich solteste keinesfalls mit der Fehlermeldung leben - die verdirbt doch komplett die Designer-Vorschau.

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

    Nabend,

    Ich weiß es ist komisch aber so ist es was mir aufgefallen ist ist folgendes:

    Hier nochmal die Definition:

    VB.NET-Quellcode

    1. Public WithEvents __transstatus As New ObservableCollection(Of trans_status_v01)()
    2. Public Property Transstati() As New ListCollectionView(__transstatus)


    Das Load Event:

    VB.NET-Quellcode

    1. Private Sub TransStatusLoad()
    2. __transstatus.Clear()
    3. dbo.trans_status_v01.Load
    4. __transstatus = dbo.trans_status_v01.Local
    5. Transstati.MoveCurrentToFirst()
    6. Debug.Print(__transstatus.Count.ToString)
    7. Debug.Print(Transstati.Count.ToString)
    8. End Sub


    Das Load Event funktioniert auch allerdings ist mir aufgefallen das er im Part __transstatus = dbo.trans_status_v01.local danach das CollectionChanged Event nicht feuert.
    Ich habe probeweise das __transstatus mit einer For Schleife mal von Hand mit .add gefüllt da wird das CollectionChanged Event gefeuert und die Transstati ist wieder "synchron". Allerdings ist mir aufgefallen das in deinem Beispiel das Viewmodel noch PropertyChanged implementiert was bei mir nicht der Fall ist auch das erweitern der vom EF erstellten trans_status_v01 Klasse um die Helferfunktion hat nicht geholfen.

    ---- Zur Fehlermeldung:
    Ja gut dann nicht damit leben aber ich habe keine Idee wie ich die wegbringen soll ich hab ein neues Projekt angelegt und eine Datenbank mit einer Entität angebunden sobald ich das Mainmodel reinziehe wieder dasselbe...

    Edit:
    Noch vergessen Filter in der CollectionView ist nicht drinnen ich lade ganz "dumm" einfach nur die Tabelle in die Collection und das wars.
    mfG.
    Stephan

    kaifreeman schrieb:

    Das Load Event funktioniert auch allerdings ist mir aufgefallen das er im Part __transstatus = dbo.trans_status_v01.local danach das CollectionChanged Event nicht feuert.
    warum sollte er da ein CollectionChanged feuern?
    Das Event wird gefeuert, wenn du ein Item zufügst oder entfernst, aber doch nicht, wenn du die ganze Collection austauschst.
    @Counterbug
    Jup sollte er zumindest lt. Codedefinition:

    VB.NET-Quellcode

    1. Public Overridable ReadOnly Property Local As ObservableCollection(Of TEntity)

    (Auszug aus der DbSet Klasse)

    @ErfinderDesRades
    OK das war mir nicht bewusst ich dachte die CollectionChanged wird gefeuert wenn sich an der Collection selbst etwas ändert weil im Code ja das steht:

    VB.NET-Quellcode

    1. '
    2. ' Summary:
    3. ' Occurs when an item is added, removed, changed, moved, or the entire list is
    4. ' refreshed.
    5. Public Event CollectionChanged As NotifyCollectionChangedEventHandler

    Ich interpretierte den letzten Part eben so das die komplette Liste ausgetauscht werden kann und das Event dann feuert...

    Somit bleiben 2 Fragen über:
    Kriege ich das zum laufen ohne die Listcollection zu benutzen und ohne das ich die ObservableCollection mit einer Schleife befüllen muss?


    Achja und vielleicht nochwas den Code den du gepostet hast:

    VB.NET-Quellcode

    1. Private Sub TransStatusColor()
    2. messagebox.Show(string.concat("Transstati: ",Transstati.Count,", trans_status_v01: ",trans_status_v01.Count))
    3. Dim v_color As Color = get_color_from_dialog()
    4. If v_color isnot Nothing Then
    5. Dim transstatus = CType(Transstati.CurrentItem, trans_status_v01)
    6. transstatus.color = v_color.ToString
    7. End If
    8. End Sub


    Hab ich nochmal durchlaufen lassen er meldet bei trans_status_v01.count den Fehler das die Count Eigenschaft nicht implementiert ist, trans_status_v01 ist die Partial Klasse die das EF Framework unter dem data_model.tt erstellt hat eventuell ist da das Problem zu finden?
    mfG.
    Stephan
    in deim Code aus post#1 ist Transstaati eine ListCollectionView, und sollte eine Count-Eigenschaft haben.

    achso - ja, namensverwechslung mit dem andern:
    muss heissen:

    VB.NET-Quellcode

    1. messagebox.Show(string.concat("Transstati: ",Transstati.Count,", transstatus : ",transstatus.Count))

    Und wie gesagt, sollte ergeben, dass deren count nicht unterschiedlich sind.

    Äh, wird wohl ergeben, dass sie unterschiedlich sind, denn in zeile#17 weist du an die transstatus-Variable ja was anneres zu.

    Dann ist klar, dass die ListCollectionView andere Inhalte hat, denn deren ItemSource ist ja nachwievor die vorherige ObservableCollection.

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

    Jup ist unterschiedlich 3 / 0 wie gesagt das Problem hatten wir beide schon einmal in einem anderen Thread.

    Ich habe jetzt die Klasse so umgebaut:

    VB.NET-Quellcode

    1. Public WithEvents __transstatus As New ObservableCollection(Of trans_status_v01)()
    2. 'Public Property Transstati() As New ListCollectionView(__transstatus)
    3. Public Property Transstati() As ListCollectionView
    4. Private Sub TransStatusLoad()
    5. __transstatus.Clear()
    6. dbo.trans_status_v01.Load
    7. __transstatus = dbo.trans_status_v01.Local
    8. Transstati = New ListCollectionView(__transstatus)
    9. Transstati.MoveCurrentToFirst()
    10. Debug.Print(__transstatus.Count.ToString)
    11. Debug.Print(Transstati.Count.ToString)
    12. End Sub


    Weise also der Transstati ListCol erst nach dem Laden aus der DB die __transstatus zu das funktioniert der Count wird nun synchron gehalten 3/3

    Mein Problem mit der Aktualisierung hat sich damit nicht gelöst ich musste dazu noch den "Pointer" bewegen:

    VB.NET-Quellcode

    1. Private Sub TransStatusColor()
    2. Dim v_color As Color = get_color_from_dialog()
    3. If Not v_color = Nothing Then
    4. Dim transstatus = CType(Transstati.CurrentItem, trans_status_v01)
    5. transstatus.color = v_color.ToString
    6. Transstati.MoveCurrentToPrevious()
    7. Transstati.MoveCurrentToNext()
    8. End If
    9. End Sub

    Die Frage ist ob das wirklich eine schöne Lösung ist gibt es da was besseres?

    Edit:
    Achja was mir noch aufgefallen ist hier wieder das Problem wenn der User die Farbe ändert dann wird das sofort übernommen ohne das es die Möglichkeit eines Cancel gibt (Speichern in die DB funktioniert nur nach drück auf Speichern aber innerhalb des laufenden Programmes ist da ein falscher Wert..)
    mfG.
    Stephan
    na, ob das so dolle ist:
    in Listing#1, zeile#10 weist du der ListCollectionView-Variable eine neue Listcollection zu.

    Aber dein Gui weiß nix davon! Das ist ja schön noch an die vorherige ListCollectionView gebunden.

    Also ich würde empfehlen, diese Zuweiserei ganz zu lassen.
    Initialisier das Viewmodel ein einziges Mal, und dann erstell keine weiteren Listen und Zeugs, sondern verwende die Listen, die da sind, und an die gebunden ist.

    Ich bin mir sicher, in meim Code habich das auch net anders gemacht.
    sry aber ich checks nicht ich habe mich an deinen Code gehalten.

    Vielleicht ist es ein Struktur Problem das ich aber nicht erkennen kann:

    Main Mainmodel:

    VB.NET-Quellcode

    1. Public Class MainModel : Inherits MainModelBase(Of MainModel)
    2. 'Datenbankverbindung:
    3. Dim dbo As New data_modelContainer
    4. Public Sub New()
    5. TransStatusLoad()
    6. End Sub
    7. #Region "Buchung:Status"
    8. #Region "Commands"
    9. Public Property cmdAddTransStatus() As New RelayCommand(Sub() Transstati.AddNewItem(New trans_status_v01() With {.bezeichnung = "<New Status>"}), Function() True)
    10. Public Property cmdSaveTransStatus() As New RelayCommand(AddressOf TransStatusSave, Function() __transstatus.Count > 0)
    11. Public Property cmdEditTransStatus() As New RelayCommand(AddressOf TransStatusEdit, Function() Transstati.CurrentPosition >= 0)
    12. Public Property cmdDeleteTransStatus() As New RelayCommand(AddressOf TransStatusDelete, Function() Transstati.CurrentPosition >= 0)
    13. Public Property cmdColorTransStatus() As New RelayCommand(AddressOf TransStatusColor, Function() __transstatus.Count > 0)
    14. #End Region
    15. Public WithEvents __transstatus As New ObservableCollection(Of trans_status_v01)()
    16. 'Public Property Transstati() As New ListCollectionView(__transstatus)
    17. Public Property Transstati() As ListCollectionView
    18. Private Sub TransStatusLoad()
    19. __transstatus.Clear()
    20. dbo.trans_status_v01.Load
    21. __transstatus = dbo.trans_status_v01.Local
    22. Transstati = New ListCollectionView(__transstatus)
    23. Transstati.MoveCurrentToFirst()
    24. Debug.Print(__transstatus.Count.ToString)
    25. Debug.Print(Transstati.Count.ToString)
    26. End Sub
    27. Private Sub TransStatusSave()
    28. Debug.Print(__transstatus.Count.ToString())
    29. dbo.SaveChanges()
    30. End Sub
    31. Private Sub TransStatusEdit()
    32. Throw New NotImplementedException
    33. End Sub
    34. Private Sub TransStatusDelete()
    35. Throw New NotImplementedException
    36. End Sub
    37. Private Sub TransStatusColor()
    38. Dim v_color As Color = get_color_from_dialog()
    39. If Not v_color = Nothing Then
    40. Dim transstatus = CType(Transstati.CurrentItem, trans_status_v01)
    41. transstatus.color = v_color.ToString
    42. Transstati.MoveCurrentToPrevious()
    43. Transstati.MoveCurrentToNext()
    44. End If
    45. End Sub
    46. Private Sub __transstatus_CollectionChanged(sender As Object, e As Specialized.NotifyCollectionChangedEventArgs) Handles __transstatus.CollectionChanged
    47. If e.Action <> Specialized.NotifyCollectionChangedAction.Remove Then Return
    48. If Not Transstati.MoveCurrentToNext Then Transstati.MoveCurrentToPrevious()
    49. End Sub
    50. #End Region
    51. End Class


    Meine vom Entity Framework erzeugte Entität trans_status_v01

    VB.NET-Quellcode

    1. Imports System
    2. Imports System.Collections.Generic
    3. Partial Public Class trans_status_v01
    4. Public Property status_id As Integer
    5. Public Property bezeichnung As String
    6. Public Property color As String = "#000000"
    7. Public Property symbol As Nullable(Of Byte)
    8. Public Property aktiv As Boolean = true
    9. Public Overridable Property nv_transstatus_trans As ICollection(Of transactions_v01) = New HashSet(Of transactions_v01)
    10. End Class


    Mehr gibt es da nicht wenn ich in der Routine TransStatusLoad nicht das "gedönse" mit .local und der Zuweisung mache habe ich keine Werte und mein .count bleibt 0 weil ja aus der DB nix geladen wird.

    Ich habe keine eigene Klasse Viewmodel für TransStatus geschrieben da diese so glaube ich zumindest ja schon in Form der Entitäten Klasse vorliegen?
    Wenn das nicht korrekt ist wüsste ich sonst nicht wie ich eine ViewModel Klasse gestalten müsste damit diese die Funktion hat die ich brauche
    Das hab ich nämlich auch schon probiert:

    VB.NET-Quellcode

    1. ''' <summary>
    2. ''' Viewmodel für die Entität TransStatus
    3. ''' </summary>
    4. Public Class TransStatusVM : Inherits NotifyPropertyChanged
    5. Private _status As trans_status_v01
    6. Public ReadOnly Property status_id() As Integer
    7. Get
    8. Return _status.status_id
    9. End Get
    10. End Property
    11. Public Property bezeichnung As String
    12. Get
    13. Return _status.bezeichnung
    14. End Get
    15. Set(value As String)
    16. ChangePropIfDifferent(value, _status.bezeichnung)
    17. End Set
    18. End Property
    19. Public Property color As String
    20. Get
    21. Return _status.color
    22. End Get
    23. Set(value As String)
    24. ChangePropIfDifferent(value, _status.color)
    25. End Set
    26. End Property
    27. Public Property symbol As Byte?
    28. Get
    29. Return _status.symbol
    30. End Get
    31. Set(value As Byte?)
    32. ChangePropIfDifferent(value, _status.symbol)
    33. End Set
    34. End Property
    35. Public Property aktiv As Boolean
    36. Get
    37. Return _status.aktiv
    38. End Get
    39. Set(value As Boolean)
    40. ChangePropIfDifferent(value, _status.aktiv)
    41. End Set
    42. End Property
    43. End Class


    Nutze ich diese allerdings fehlen mir ja die "Goodies" der Relations aus dem EF und das kann imho nicht Sinn der Sache sein.
    Sry das ich mich da gerade vielleicht blöd anstelle aber irgendwie ist der Funke noch nicht ganz gesprungen :/
    mfG.
    Stephan
    wie gesagt: in jedem Load erzeugst du eine neue ListCollectionView. Vermeide doch einfach mal das Schlüsselwort New, weil das erzeugt ja eine neue Instanz, und wenn das während des laufenden Betriebs passiert, ist höchstwahrscheinlich, dass an diese neue Instanz nichts gebunden ist, denn die Bindings sind ja bereits zur Designzeit gesetzt.

    konkret: Verschiebe zeile#30 in die Sub new des MainViewmodels.

    Aber ich weiß auch grad nicht, mit welchem Problem du dich beschäftigst, evtl. auch gleich mehrere, und die voneinander abhängen.
    Und durch 150 Zeilen Code blick ich vom Angucken her kaum durch, also auch was ich sage kann leicht falsch sein.
    Hm mir fällt grad auf das ich mich da in ein "Problem" verlaufen hab ^^
    Mit den Codeänderungen lässt sich das Observable und die Listcollection synchron halten.

    Eigentlich ist das Problem jetzt das folgende:

    Die View lädt in der Listbox die Elemente sauber rein. Bei Klick auf ein Element wird die "Detail-View" korrekt geladen.
    Ändere ich jetzt die Property "bezeichnung" als Beispiel von Text 1 in Text 2 dann wird diese Änderung nicht sofort in der Listview angezeigt und auch nicht wenn ich speichern klicke ich muss den Dialog neu öffnen.
    Änderungen in einer Textbox z.b. für die Property "bezeichnung" werden sofort zurück in die Collection geschrieben ohne das der User explizit speichern muss => speichert er nicht ist die Collection geändert die Datenbank nicht was zumindest eine gewisse Hoffnung beinhaltet.
    2. Wenn der Benutzer eine Farbe auswählt mit diesem Code hier:

    VB.NET-Quellcode

    1. Private Sub TransStatusColor()
    2. Dim v_color As Color = get_color_from_dialog()
    3. If Not v_color = Nothing Then
    4. Dim transstatus = CType(Transstati.CurrentItem, trans_status_v01)
    5. transstatus.color = v_color.ToString
    6. Transstati.MoveCurrentToPrevious()
    7. Transstati.MoveCurrentToNext()
    8. End If
    9. End Sub

    Muss ich wie gesagt die Transstati "bewegen" damit die Änderung sofort in der Textbox die an die Property color gebunden ist aktualisiert wird.
    mfG.
    Stephan

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „kaifreeman“ ()

    Ich habe eigentlich die Klasse um das NotifyPropertyChanged erweitert:

    VB.NET-Quellcode

    1. Partial Public Class trans_status_v01 : inherits NotifyPropertyChanged
    2. End Class


    Das hat aber anscheinend nicht gegriffen ich habe jetzt auf Fody und PropertyChanged.Fody zurückgegriffen:

    VB.NET-Quellcode

    1. Imports PropertyChanged
    2. 'Erweitert die EF Klassen um das NotifyPropertyChanges
    3. <ImplementPropertyChanged>
    4. Partial Public Class trans_status_v01
    5. End Class


    Jetzt klappt es bleibt mir nur mehr die Frage wie bringe ich es hin das der User auch gefragt wird ob er die Änderung wirklich übernehmen will?
    mfG.
    Stephan
    Was jetzt klappt:
    Ändern des Datensatzes in der Detail-View und automatisches Aktualisieren in der Listview.

    Wann soll die Frage kommen:
    Szenario:
    Benutzer wählt in der Listview Eintrag aus wird im DetailView angezeigt.
    User ändert eine Property z.b. bezeichnung

    Wie es derzeit ist:
    sobald Änderung der Property erfolgt ist wird die ObservableCollection geändert ob der User will oder nicht

    Wie es sein soll:
    Änderung wird nicht übernommen erst wenn "Save" Button gewählt wird oder eine Änderung in der Listview Auswahl gemacht wird kommt das speichern event bzw. bei letzterm eine Abfrage ob gespeichert werden soll.

    Derzeit arbeite ich in diesem Fall nur mit einem Windows das das komplette CRUD abdecken soll.

    Ein möglicher Ansatz wäre das mit einem IEditableObject zu machen ? Nur wie ich das mit dem MVVM zusammenbringe ist mir ein Rätsel...
    mfG.
    Stephan
    naja, ich würd sagen, das Konzept ist unausgegoren.
    Die Useability verletzt das Prinzip WhatYouSeeIsWhatYouGet.

    Also angenommen dein DetailView zeigt 55€ an.
    Dann geht der User davon aus, im Datensatz sei 55€ eingetragen.
    Kann sein, dass das richtig ist.
    Kann aber auch sein, dasses falsch ist, und es ist nur 40€ eingetragen. Zwar im Gui steht 55€, aber Save wurde noch nicht gedrückt.

    Also du kannst da jetzt mit roten und grünen Lämpchen arbeiten und blinkiblinki und so weiter, aber ich glaub, die User hätten lieber was normales, wo wenn da 55€ steht, dass dann auch 55€ im Datensatz drinne ist.

    Also mach - wie gesagt - einen Dialog.
    Den kann man öffnen, dann weiß man "aha, nun editiere ich den Datensatz". Und den kann man canceln oder übernehmen, und da kann kein Missverständnis aufkommen.

    Ok?

    Beim Dialog kann man dann auch nachdenken, wie man das mit IEditable hinkriegt - gibts da nicht vlt auch ein Fody für?
    WYSIWYG ist ja ganz gut aber mal ehrlich wenn ich bei jedem Create oder Edit einen Dialog setze kriegt der User doch die "Dialogeritis" mich persönlich (und das ist jetzt nur meine Meinung) stört es ziemlich wenn ich in Programmen arbeite und ich klicke irgendwo und plop wieder ein Dialog dann vom Dialog nochmal ein Dialog usw. IMHO ziemlich unübersichtlich.

    Das Problem in diesem Beispiel ist für mich das die trans_status Entität ja nur eine Art Nebenentität für den eigentlichen Programm Zweck ist wie gesagt bastel ich zum üben mit WPF ein Haushaltsbuch der Hauptzweck ist hierbei das verwalten der Buchungen. Das ist die Hauptentität. Die anderen Entitäten die "Status" "Typ" "Kategorie" usw. sind Nebensache.

    Das GUI ist derzeit so aufgebaut das man das Mainwindows öffnet und man übersichtlich die Buchungen eines bestimmten Kontos kriegt der derzeit programmierte Dialog (und das ist schon ein Dialog) für die Status Entität ist nur eine Art Administrationsdialog der aufgeht darum möchte ich aus einem Unterdialog eigentlich nicht noch einen Dialog öffnen.

    Darum die Überlegung das in diesem einen Fenster zu machen aber den User vor Fehleingaben retten so das er den Status wiederherstellen kann wenn er etwas unabsichtlich geändert hat.

    Um ein IEditableObject zu implementieren gibt es kein Fody (war das jetzt eigentlich sarkastisch gemeint??) für einen Dialog würde ich das direkt im ViewModel erledigen....

    Was wäre wenn ich das Binding auf Oneway setzte mit dem Klick auf den "Save" Button im ViewModel die Behandlung mache um die Datenänderung abzugreifen und dann an die View zu geben das würde doch nicht gegen das MVVM verstoßen oder?
    mfG.
    Stephan