Entity Framework - Treeview Self Relation und 1:n Relation

  • WPF

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

    Entity Framework - Treeview Self Relation und 1:n Relation

    Schönen guten Abend,

    Ich habe mich jetzt näher mit dem Entity Framework befasst und soweit klappt alles schon recht gut aber derzeit stehe ich komplett an.

    Ich habe folgendes Datenmodell:

    Wie man sieht habe ich eine Self-Relation in der Tabelle folder_v01 sowie eine 1:n Relation auf die Tabelle konto_v01

    Derzeit lade ich die Folder in einen Treeview:

    VB.NET-Quellcode

    1. Dim db As New template_dbEntities
    2. Private Function get_all_folders(Optional aktiv_only As Boolean = True) As Boolean
    3. Select Case aktiv_only
    4. Case True
    5. trv_elements.ItemsSource = db.folder_v01.Where(Function(x) x.aktiv = aktiv_only And x.folder_v01_fol_id Is Nothing).ToList
    6. Case False
    7. trv_elements.ItemsSource = db.folder_v01.Where(Function(x) x.folder_v01_fol_id Is Nothing).ToList
    8. End Select
    9. Return True
    10. End Function


    Dazu habe ich einen Converter gebaut:

    VB.NET-Quellcode

    1. Imports System.Globalization
    2. Public Class folder_converter
    3. Implements IValueConverter
    4. Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
    5. Dim node = TryCast(value, folder_v01)
    6. Return node.folder_v011.Where(Function(i) i.folder_v01_fol_id = node.fol_id).ToList
    7. End Function
    8. Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
    9. Throw New NotImplementedException()
    10. End Function
    11. End Class


    im XAML schaut es so aus:

    XML-Quellcode

    1. <local:folder_converter x:Key="FolderConverter" />
    2. <HierarchicalDataTemplate x:Key="SelfRefTemplate" ItemsSource="{Binding Converter={StaticResource FolderConverter}}">
    3. <WrapPanel>
    4. <Image Height="16" Width="16" Margin="0,0,5,0" Source="{Binding symbol}" />
    5. <TextBlock Text="{Binding name}" Style="{DynamicResource text_style}">
    6. <TextBlock.Resources>
    7. <Style x:Key="text_style" TargetType="{x:Type TextBlock}">
    8. <Style.Triggers>
    9. <DataTrigger Binding="{Binding aktiv}" Value="True">
    10. <Setter Property="Foreground" Value="{Binding color}"/>
    11. </DataTrigger>
    12. <DataTrigger Binding="{Binding aktiv}" Value="False">
    13. <Setter Property="Foreground" Value="#FFB8B8B8"/>
    14. <Setter Property="FontStyle" Value="Italic"/>
    15. <Setter Property="FontWeight" Value="Thin" />
    16. </DataTrigger>
    17. </Style.Triggers>
    18. </Style>
    19. </TextBlock.Resources>
    20. </TextBlock>
    21. </WrapPanel>
    22. </HierarchicalDataTemplate>


    Mein Problem ist die Anbindung der Datensätze aus der Tabelle konto_v01 ich habe versucht den Konverter zu erweitern:

    VB.NET-Quellcode

    1. Dim node = TryCast(value, folder_v01)
    2. If DirectCast(value, folder_v01).folder_v01_fol_id IsNot Nothing Then
    3. Return node.konto_v01.Where(Function(i) i.folder_v01_fol_id = node.fol_id).ToList
    4. Else
    5. Return node.folder_v011.Where(Function(i) i.folder_v01_fol_id = node.fol_id).ToList
    6. End If


    Das hilft leider nicht wirklich kann mir da jemanden einen Tipp geben?

    gleichzeitig gleich die 2. Frage wie muss ich mein HierarchicalDataTemplate erweitern?

    Danke!
    mfG.
    Stephan
    ich kapier nix von deim Code, imo brauchst du nix dergleichen.
    Ein Treeview kann dank des HierarchicalTemplates eine Baumstruktur darstellen, eine Methode GetAllFolders oder gar einen Converter braucht man nicht.
    MVVM-Pattern, DataContext und DataTemplates im Treeview
    konzentrier dich da aufs Xaml - wie das Viewmodel gestrickt ist, wird bei dir ja ganz anders sein.
    Ausserdem empfehle ich immer, Bindings aus dem PropertyFenster zu picken - vermutlich wird dein Binding da nicht funzigen.
    ach, beim Tut zum Binding-Picking gibts ja auch einen Treeview:
    MVVM: "Binding-Picking" im Xaml-Editor

    Ich muss allerdings zugeben, dass ich mir nicht viel Mühe gemacht habe, dein Code zu verstehen, also ich zeige dir nur grundsätzlich auf, wie man einen tv verknüppert, und hoffe, ist was dabei, was dir aushilft.

    Ah, was vlt. meine Lösung ausmacht: Ich habe einfach 2 Templates in die TV-Resources gelegt, und zwar ohne Key, sondern die identifizieren sich anhand ihres DataTypes:
    Für den Folder-Typ ein HierarchicalTemplate, und für den File-Type ein normals DataTemplate.
    Da die in den TV-Resourcen liegen, wendet der Tv die selbständig richtig an.
    Ich habe also kein explizites ItemTemplate festgelegt.

    Hmm - nächste Idee: also ich find dein Converter ja eigenartig - der soll die Childrenn eines Nodes heranschaffen? Aber EF bietet doch dafür NavigationsEigenschaften - ich denke folderv_011 oder folderv_012 müssten die Children abliefern (und daran wäre die HirTmpl.ItemsSource zu binden).

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

    Erstmal danke für deine Antwort.
    Also die GetAllFolders Methode und den Converter habe ich gelöscht die brauche ich wirklich nicht.

    Ich glaube aber jetzt zu wissen das ich ein grundsätzliches Problem habe.

    Wenn ich mir jetzt die Entität folder_v01 schnappe:

    VB.NET-Quellcode

    1. Dim _folder As folder_v01 = New folder_v01


    Dann ist lt. Definition folgendes gegeben:

    XML-Quellcode

    1. Imports System
    2. Imports System.Collections.Generic
    3. Imports System.Collections
    4. Partial Public Class folder_v01
    5. Public Property fol_id As Integer
    6. Public Property name As String
    7. Public Property color As String
    8. Public Property symbol As Byte()
    9. Public Property aktiv As Boolean
    10. Public Property folder_v01_fol_id As Nullable(Of Integer)
    11. Public Overridable Property konto_v01 As ICollection(Of konto_v01)
    12. Public Overridable Property folder_v011 As ICollection(Of folder_v01)
    13. Public Overridable Property folder_v012 As folder_v01
    14. End Class


    also müssten meine Navigation Props wie du schon beschrieben hast die Childrens abliefern das tun sie aber nicht weil ich ja mit "new folder_v01" ein neues Element geschaffen habe das keine Daten beinhaltet.

    Darum dachte ich mir ich mache folgendes:

    VB.NET-Quellcode

    1. dim _folder as folder_v01 = db.folder_v01


    geht natürlich nicht:
    Severity Code Description Project File Line Suppression State
    Error BC30311 Value of type 'DbSet(Of folder_v01)' cannot be converted to 'folder_v01'

    auch ein .tolist geht nicht:
    Severity Code Description Project File Line Suppression State
    Error BC30311 Value of type 'List(Of folder_v01)' cannot be converted to 'folder_v01'.

    Ist natürlich auch klar also bastel ich mir eine Observable Klasse geht aber auch nicht.....

    Ganz offen ich drehe mich irgendwie gerade im Kreis entweder check ich das Modell nicht richtig aber.
    mfG.
    Stephan

    kaifreeman schrieb:

    also müssten meine Navigation Props wie du schon beschrieben hast die Childrens abliefern das tun sie aber nicht ...
    Dochdoch, das tun sie. Nur die Anzahl der Children ist dummerweise 0, aber das ist von einem new folder_v01 auch wohl nicht anders zu erwarten.

    probierma

    VB.NET-Quellcode

    1. dim _folder as DbSet(Of folder_v01)= db.folder_v01
    2. 'oder gleich
    3. dim _folder= db.folder_v01

    oder willst du einen einzelnen Folder?

    VB.NET-Quellcode

    1. dim _folder as folder_v01 = db.folder_v01(0)
    2. 'oder gleich
    3. dim _folder = db.folder_v01(0)


    Auf die Idee das ich die _folder Variable auf DbSet deklariere oder einfach mal blind dime bin ich nicht gekommen... :|

    Leider stolpert er mir noch immer beim Laden der Konten Entität:

    VB.NET-Quellcode

    1. Dim db As New template_dbEntities
    2. Dim _folder As DbSet(Of folder_v01) = db.folder_v01
    3. Public Sub New()
    4. ' This call is required by the designer.
    5. InitializeComponent()
    6. ' Add any initialization after the InitializeComponent() call.
    7. DataContext = _folder
    8. trv_elements.ItemsSource = _folder.Where(Function(x) x.aktiv = True).ToList
    9. End Sub


    XML-Quellcode

    1. <TreeView x:Name="trv_elements" Grid.Row="1" Grid.Column="0" Grid.RowSpan="2">
    2. <TreeView.Resources>
    3. <HierarchicalDataTemplate DataType="{x:Type local:folder_v01}" ItemsSource="{Binding folder_v011 }">
    4. <WrapPanel>
    5. <Image Height="16" Width="16" Margin="0,0,5,0" Source="{Binding symbol}" />
    6. <TextBlock Text="{Binding name}" Style="{DynamicResource text_style}">
    7. <TextBlock.Resources>
    8. <Style x:Key="text_style" TargetType="{x:Type TextBlock}">
    9. <Style.Triggers>
    10. <DataTrigger Binding="{Binding aktiv}" Value="True">
    11. <Setter Property="Foreground" Value="{Binding color}"/>
    12. </DataTrigger>
    13. <DataTrigger Binding="{Binding aktiv}" Value="False">
    14. <Setter Property="Foreground" Value="#FFB8B8B8"/>
    15. <Setter Property="FontStyle" Value="Italic"/>
    16. <Setter Property="FontWeight" Value="Thin" />
    17. </DataTrigger>
    18. </Style.Triggers>
    19. </Style>
    20. </TextBlock.Resources>
    21. </TextBlock>
    22. </WrapPanel>
    23. </HierarchicalDataTemplate>
    24. <DataTemplate DataType="{x:Type local:konto_v01}">
    25. <WrapPanel>
    26. <Image Height="16" Width="16" Margin="0,0,5,0" Source="{Binding symbol}" />
    27. <TextBlock Text="{Binding bezeichnung}" Style="{DynamicResource text_style}">
    28. <TextBlock.Resources>
    29. <Style x:Key="text_style" TargetType="{x:Type TextBlock}">
    30. <Style.Triggers>
    31. <DataTrigger Binding="{Binding archiv}" Value="True">
    32. <Setter Property="Foreground" Value="{Binding color}"/>
    33. </DataTrigger>
    34. <DataTrigger Binding="{Binding archiv}" Value="False">
    35. <Setter Property="Foreground" Value="#FFB8B8B8"/>
    36. <Setter Property="FontStyle" Value="Italic"/>
    37. <Setter Property="FontWeight" Value="Thin" />
    38. </DataTrigger>
    39. </Style.Triggers>
    40. </Style>
    41. </TextBlock.Resources>
    42. </TextBlock>
    43. </WrapPanel>
    44. </DataTemplate>
    45. </TreeView.Resources>
    46. </TreeView>


    In dem Fall lädt der Ordner und Unterordner aber nicht die konto_v01 Typen. Ändere ich die Itemsource des HierarchicalDataTemplate auf x:Type local:konto_v01 lädt er mir die Hauptordner und die untergeordneten Konten
    Dürfte er aber eigentlich schon aufgrund der Tatsache nicht schaffen da im Typ konto_v01 das Feld name nicht vorkommt sondern bezeichnung heißt....
    mfG.
    Stephan
    ist das ühaupt wünschenswert, folder + konten in einen TV zu laden?
    Wenn ich etwa den DateiBrowser angucke, dann hat der Treeview nur folder.

    Allerdings bei meine eigenen Übungen hab ich ja beides im Treeview, und das geht deshalb, weil bei mir Folder und Files voneinander erben.
    Ist in deim Datenmodell aber nicht so.
    Da muss man dann rum-murksen, und eine zusätzliche MixedChildren-Property erfinden, wo dann jede Art von Child drin ist, sowohl folder als auch konten.
    Lässt sich vermutlich mittels partialer Klasse lösen, die diese Entity entsprechend erweitert.
    Hallo,

    Ja es ist in diesem Fall wünschenswert die Folder sind quasi container für die Konten die alle sauber in einem Treeview stehen soll je nach dem welches Konto aktiviert wird wird später dann ein Datagridview gefüllt mit einem Object Typ transactions_v01 soweit der Plan....

    Auch wenn mein Wissen da mehr als eingeschränkt ist so glaube ich das das nicht ganz stimmt, die Entitäten sind ja sehr wohl miteinander verbunden:

    VB.NET-Quellcode

    1. Imports System
    2. Imports System.Collections.Generic
    3. Imports System.Collections
    4. Partial Public Class folder_v01
    5. Public Property fol_id As Integer
    6. Public Property name As String
    7. Public Property color As String
    8. Public Property symbol As Byte()
    9. Public Property aktiv As Boolean
    10. Public Property folder_v01_fol_id As Nullable(Of Integer)
    11. Public Overridable Property folder_v011 As ICollection(Of folder_v01)
    12. Public Overridable Property konto_v01 As ICollection(Of konto_v01)
    13. Public Overridable Property folder_v012 As folder_v01
    14. End Class


    Im Datamodell ist ja die Property konto_v01 as ICollection(of konto_v01) vorhanden diese liefert ja auch Daten zurück, ich bin bei Stackoverflow auf diesen Beitrag gestoßen:
    stackoverflow.com/questions/27…rk-entities-to-a-treeview
    funktioniert bei mir zwar nicht wirklich mir ist hier nur der Unterschied aufgefallen das mein Modell mit ICollections arbeitet während der Beitrag sich auf Observable Collections bezieht.

    Ich werd mir das mit den MixedChildrens mal ansehen vielleicht kann ich da was basteln....
    mfG.
    Stephan

    kaifreeman schrieb:

    Auch wenn mein Wissen da mehr als eingeschränkt ist so glaube ich das das nicht ganz stimmt, die Entitäten sind ja sehr wohl miteinander verbunden
    naja, "verbinden" kann viel bedeuten, und in diesem Falle doch nix bringen.

    Eine Treeview-Darstellung erfordert halt eine Child-Collections, wo alle Childs drinne sind. Und das hast du derzeit nicht, sondern die eine Art Childs ist in der Folder-ChildCollection, und die andere Art Childs ist in der Konto-ChildCollection.

    Und nu musste eine MixedCollection basteln, die beide zusammenschmeisst, denn das HierarchicalTemplate hat numal nur eine ItemsSource-Property zum dran binden.

    Und die MixedCollection muss minimal vom Typ IEnumerable sein, das ist auch ganz gut leistbar:

    VB.NET-Quellcode

    1. '... Property folder_v01.MixedChildren As IEnumerable - Get
    2. Return folder_v011.Cast(Of Object).Concat(konto_v01.Cast(Of Object))
    Kann man da übrigens was mitte Benamung machen?
    folder_v01, konto_v01, folder_v011, folder_v012 - das ist ziemlich anfällig für Verwechselungen.

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

    Hallo
    Jetzt funktioniert es endlich. Danke dir für deine Hilfe.

    Das Datenmodell hab ich umgeschrieben die Bezeichnungen sind wirklich nicht optimal falls es noch jemand braucht die Erweiterungsklasse die bei mir die Lösung brachte:

    VB.NET-Quellcode

    1. Partial Public Class folder_v01
    2. 'Erweiterung der Partiellen folder_v01 Klasse um eine Property MixedChildren zur Anzeige im Treeview
    3. Public ReadOnly Property MixedChildren As IEnumerable
    4. Get
    5. If konto_v01 Is Nothing Or nv_subfolders Is Nothing Then
    6. Return Nothing
    7. Else
    8. Return nv_subfolders.Cast(Of Object).Concat(konto_v01.Cast(Of Object))
    9. End If
    10. End Get
    11. End Property
    12. End Class


    die Abfrage für nothing is notwendig da er ansonsten abbricht wenn ein Folder keine Konten hat oder auch keinen Subfolder.

    und so sieht der Treeview aus:

    XML-Quellcode

    1. <TreeView x:Name="trv_elements" Grid.Row="1" Grid.Column="0" Grid.RowSpan="2">
    2. <TreeView.ItemContainerStyle>
    3. <Style TargetType="{x:Type TreeViewItem }">
    4. <Setter Property="IsExpanded" Value="True"/>
    5. </Style>
    6. </TreeView.ItemContainerStyle>
    7. <TreeView.Resources>
    8. <HierarchicalDataTemplate DataType="{x:Type local:folder_v01}" ItemsSource="{Binding MixedChildren }">
    9. <WrapPanel>
    10. <Image Height="16" Width="16" Margin="0,0,5,0" Source="{Binding symbol}" />
    11. <TextBlock Text="{Binding bezeichnung}" Style="{DynamicResource text_style}">
    12. <TextBlock.Resources>
    13. <Style x:Key="text_style" TargetType="{x:Type TextBlock}">
    14. <Style.Triggers>
    15. <DataTrigger Binding="{Binding aktiv}" Value="True">
    16. <Setter Property="Foreground" Value="{Binding color}"/>
    17. </DataTrigger>
    18. <DataTrigger Binding="{Binding aktiv}" Value="False">
    19. <Setter Property="Foreground" Value="#FFB8B8B8"/>
    20. <Setter Property="FontStyle" Value="Italic"/>
    21. <Setter Property="FontWeight" Value="Thin" />
    22. </DataTrigger>
    23. </Style.Triggers>
    24. </Style>
    25. </TextBlock.Resources>
    26. </TextBlock>
    27. </WrapPanel>
    28. </HierarchicalDataTemplate>
    29. <DataTemplate DataType="{x:Type local:konto_v01}">
    30. <WrapPanel>
    31. <Image Height="16" Width="16" Margin="0,0,5,0" Source="{Binding symbol}" />
    32. <TextBlock Text="{Binding bezeichnung}" Style="{DynamicResource text_style}">
    33. <TextBlock.Resources>
    34. <Style x:Key="text_style" TargetType="{x:Type TextBlock}">
    35. <Style.Triggers>
    36. <DataTrigger Binding="{Binding archiv}" Value="False">
    37. <Setter Property="Foreground" Value="{Binding color}"/>
    38. </DataTrigger>
    39. <DataTrigger Binding="{Binding archiv}" Value="True">
    40. <Setter Property="Foreground" Value="#FFB8B8B8"/>
    41. <Setter Property="FontStyle" Value="Italic"/>
    42. <Setter Property="FontWeight" Value="Thin" />
    43. </DataTrigger>
    44. </Style.Triggers>
    45. </Style>
    46. </TextBlock.Resources>
    47. </TextBlock>
    48. </WrapPanel>
    49. </DataTemplate>
    50. </TreeView.Resources>
    51. </TreeView>
    mfG.
    Stephan

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