In the past, I had to resort to using DataSets and DataTables, as doing it with Binding still eludes me...
Top Level: I created a series of classes in VB 2012, annotated them, and crated the EF model in EF6 using Code First. The idea is to represent router configs, with sub-sections of the config being children to the major sections. Very simple idea.
Rationale: With a simple WPF Treeview, illustrate the config sections and subsections as they appear (logically) in the router.
I have started very simply with these classes, intending later to use 2-way binding to update each way, etc. Here are the first 2 top classes (snipped for brevity):

This is standard EF CodeFirst fare, and the DBContext is laid out by EF as you'd expect. This is the layout of the top 2 level classes (Access_Group & Access_List):

...and all is well in EF-Land...
Because it will likely be important, here is the actual Access_Group class:
Public Class Access_Group
Inherits EntityTypeConfiguration(Of Access_Group)
Implements IAccess_Group
<Key>
Public Property Access_GroupID As Integer Implements IAccess_Group.Access_GroupID
Public Property Name As String Implements IAccess_Group.Name
Public Property LastUpdated As Date Implements IAccess_Group.LastUpdated
Public Property Active As Boolean Implements IAccess_Group.Active
'------------------------------------------------------------------
Public Property Access_Lists As ObservableCollection(Of Access_List) Implements IAccess_Group.Access_Lists
Public Sub New()
Me.Access_Lists = New ObservableCollection(Of Access_List)
End Sub
End Class
There are many event-based components that I have not added yet, because I just want the basics to work (display hierarchically in a Treeview) before I add the bells & whistles...
So this is how the classes are created in code, that populated the database (SQL 2012) in the first place:
[Window Class contd.]
Private Sub AddData()
Try
ctx = New entitiesContext
Dim d As Date = Now
'--------------------------------
Dim al As New Access_List
' lower classes not needed to be shown...
With al
.Active = True
.Checked = True
.LastUpdated = d
.Name = "some access-list at " & d.ToLongTimeString
End With
'--------------------------------
Dim ag As Access_Group = New Access_Group
With ag
.Access_Lists.Add(al)
.Active = True
.LastUpdated = d
.Name = "some access-group at " & d.ToLongTimeString
End With
'
ctx.Access_Groups.Add(ag)
'
Dim i As Integer = ctx.SaveChanges()
Console.WriteLine("Seed complete! -> " & i)
Catch ex As Exception
Dim exText As String = "Seed Failed "
Console.WriteLine(exText & "(Message): " & ex.Message)
Console.WriteLine(exText & "(ToString): " & ex.ToString)
Console.WriteLine(exText & "(StackTrace): " & ex.StackTrace)
Console.WriteLine("EntityValidationErrors: ")
For Each eve As System.Data.Entity.Validation.DbEntityValidationResult In ctx.GetValidationErrors()
Console.WriteLine("eve: OK? " & eve.IsValid & " - " & eve.Entry.ToString)
For Each devr As System.Data.Entity.Validation.DbValidationError In eve.ValidationErrors
Console.WriteLine("devr invalid property: " & devr.PropertyName)
Console.WriteLine("devr error message : " & devr.ErrorMessage)
Next
Next
End Try
End Sub
You see Access_List referred to above as the 2nd level down, and this is that class:
Public Class Access_List
Inherits EntityTypeConfiguration(Of Access_Group)
Implements toag.entities.IAccess_List
<Key>
Public Property Access_ListID As Integer Implements IAccess_List.Access_ListID
Public Property Name As String Implements IAccess_List.Name
Public Property LastUpdated As Date Implements IAccess_List.LastUpdated
Public Property Active As Boolean Implements IAccess_List.Active
Public Property Checked As Boolean Implements IAccess_List.Checked
Public Property Object_Groups As ObservableCollection(Of Object_Group) Implements IAccess_List.Object_Groups
Public Sub New()
Me.Object_Groups = New ObservableCollection(Of Object_Group)
End Sub
End Class
If I can figure out how to get these 2 classes to behave, I can template & get the rest to do so as well...
I have tried HUNDREDS of code & XAML combinations, so I'll settle down with one that at least shows something on the Treeview:
<TreeView Grid.Column="0"
x:Name="ACLTreeView"
HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
ItemsSource="{Binding Access_Group}">
</TreeView>
When this XAML is combined with this code-behind:
Public Class ConfigWindow
Property ctx As entitiesContext
Public Access_Group_List As IEnumerable(Of Access_Group)
Sub New()
' This call is required by the designer.
InitializeComponent()
Startup()
End Sub
Public Sub Startup()
Try
ctx = New vASAContext
Me.Access_Group_List = From ag In ctx.Access_Groups Select ag
Me.ACLTreeView.ItemsSource = Access_Group_List.ToList
Catch ex As Exception
Debug.Print("ex: " & ex.Message)
End Try
End Sub
End Class
Will yield the following:

(sorry about having to obfuscate the namespace...) Which is fine, as there is no HierarchicalTemplate or even TreeViewItem in XAML.
Here is modified XAML:
...which will show the Name property of the Access_Group entity instead of it's class name [can't add a screenshot of it when editing a post, so you may have to trust me on this one! :)]
But there is another sub in the Window class that points to a problem with the hierarchy possibly not being recognized. Could it be that I've been trying examples that were correct, and my EF classes weren't set up properly? This sub should show all the elements and their children:
Public Sub PrintDebug(TheList As IEnumerable(Of Access_Group))
For Each ag As Access_Group In TheList
Console.WriteLine("=======================================")
Console.WriteLine("ag: " & ag.Name & " has " & ag.Access_Lists.Count & " Access_List entries")
For Each al As Access_List In ag.Access_Lists
Console.WriteLine("ag -> al: " & al.Name & " has " & al.Object_Groups.Count & " Object_Group entries")
For Each og As Object_Group In al.Object_Groups
Console.WriteLine("ag -> al -> og: " & og.Name & " has " & og.Network_Objects.Count & " Network_Object entries")
'...
Next
Next
Console.WriteLine("=======================================")
Next
End Sub
But this is what that debug class puts out:
=======================================
ag: some access-group at 5:00:49 PM has 0 Access_List entries
=======================================
=======================================
ag: some access-group at 5:08:56 PM has 0 Access_List entries
=======================================
=======================================
ag: some access-group at 5:09:14 PM has 0 Access_List entries
=======================================
=======================================
ag: some access-group at 5:12:31 PM has 0 Access_List entries
=======================================
[...]
? Does this mean that my Treeview doesn't have a chance? But, but... the data is correct in the DB:

All of those keys were populated by EF when I used the above code (only saving the top level class (Access_Group) after populating it's ObservableCollections...)
???
I've tried every combination of HierarchicalTemplate, in grid/window resources, nested, etc. And I'm back to square 1 after 3 days... :) Yes, all kinds of LINQ queries, too... And now I'm contemplating SQL (GASP) or JOINs in either LINQ/SQL, but then I may as well go all the way back to DataSets & DataTables if I'm ready to really give up...
Any help appreciated... I just can't move on until I can get these entities to bind correctly...
I've created sample object model of your entities (AccessGroup, AccessList and ObjectGroup) and this code might help you:
Code-behind
Imports System.Collections.ObjectModel
Class MainWindow
Property AccessGroups As New ObservableCollection(Of AccessGroup)
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.DataContext = Me
Dim ag1 = New AccessGroup With {.Name = "AG1"}
Dim ag2 = New AccessGroup With {.Name = "AG2"}
Dim al1 = New AccessList With {.Name = "AL1"}
Dim al2 = New AccessList With {.Name = "AL2"}
Dim al3 = New AccessList With {.Name = "AL3"}
Dim og1 = New ObjectGroup With {.Name = "OG1"}
Dim og2 = New ObjectGroup With {.Name = "OG2"}
al1.ObjectGroups = New List(Of ObjectGroup) From {og1}
al2.ObjectGroups = New List(Of ObjectGroup) From {og2}
ag1.AccessList = New List(Of AccessList) From {al1, al2}
ag2.AccessList = New List(Of AccessList) From {al3}
AccessGroups.Add(ag1)
AccessGroups.Add(ag2)
End Sub
End Class
Public Class AccessGroup
Property Name As String
Property AccessList As IEnumerable(Of AccessList)
End Class
Public Class AccessList
Property Name As String
Property ObjectGroups As IEnumerable(Of ObjectGroup)
End Class
Public Class ObjectGroup
Property Name As String
End Class
XAML
<TreeView ItemsSource="{Binding AccessGroups}">
<!-- AccessGroup template -->
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding AccessList}">
<TextBlock Text="{Binding Name}" />
<!-- AccessList template -->
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding ObjectGroups}">
<TextBlock Text="{Binding Name}" />
<!-- ObjectGroup template -->
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
On the picture bellow you can see the result

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With