none
TreeView unter WPF mit VB.NET 2010 RRS feed

  • Frage

  • Hallo ich habe folgendes Problem: 

    Von einer Datenbank bekomme ich eine Tabelle mit diesem Inhalt: 
    Ort        Name 
    Berlin        BBSVC01 
    Berlin        BBSVC02 
    Berlin        BBSZC01 
    Braunschweig        BWGHC01 
    Braunschweig        BWGHP01 
    Braunschweig        BWSZC01 
    Essen        ESSZP01 
    Essen        ESSZH01 

    Ich möchte diese Daten bei einer gegebenen WPF Oberfläche in eine TreeView einfügen, etwa so: 
    Berlin 
    --- BWGDC01 
    --- BWVFP01 
    --- BWWSHC01 
    Braunschweig 
    --- BGGDC01 
    --- BGVFP01 
    --- BGWSHC01 
    Essen 
    --- EMVFP01 
    --- EMWSHC01 

    Unter anderen habe ich folgendes habe ich versucht: 
    Dim reader As SqlDataReader = Server.TreeView("TreeView")
    Dim parent As New TreeViewItem
    Dim child As New TreeViewItem
    Dim buffer As String = ""
    
    'Fill treeview
    trvServer.UpdateLayout()
    trvServer.Items.Clear()
    
    While reader.Read
        If buffer <> reader(0) Then
            buffer = reader(0)
            'parent.Header = reader(0)
            'trvServer.Items.Add(parent)
            parent.Header = trvServer.Items.Add(reader(0))
        End If
        'child.Header = parent.Items.Add(reader(1))
        child.Header = reader(1)
        parent.Items.Add(child)
    End While
    
    reader.Close() 



    Leider ohne Erfolg. Es werden nur die Elternknoten (Orte) erzeugt. Die Kindknoten (Name) fehlen. Was mache ich falsch? 
    Freitag, 12. September 2014 17:24

Antworten

  • Hallo,
    ich würde dir empfehlen die Daten per LINQ in Gruppen zu sortieren:
    Dim groups = reader.AsParallel().Cast(Of IDataRecord).GroupBy(Function(record) record("Ort"))
    Dies eGruppen können dann in einer Schleife durchlaufen werden um jeweils die oberen Elemente zu generieren. Die Unteren Elemente sind in den Gruppen jeweils enthalten und alssen sich so ebenfalls hinzufügen:
    For Each group In groups
        Dim tvi As New TreeViewItem
        tvi.Header = group.First()("Ort")
        For Each subRow In group
            Dim tviSub As New TreeViewItem
            tviSub.Header = subRow("Name")
            tvi.Items.Add(tviSub)
        Next
        trvServer.Items.Add(tvi)
    Next

    Besser wäre es aber wenn du die TreeView über ihre ItemsSOurce-Eigenschaft befüllst. Hierfür musst du jedoch die Daten in eine Hierarchische Form bringen und entsprechend Templates im XAML erzeugen. EIn Beispiel in C# findest du unter Simplifying the WPF TreeView by Using the ViewModel Pattern. In meiner Signatur steht ein Konverter, falls du den C# Code nicht verstehst.

    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Freitag, 12. September 2014 18:32
    Moderator
  • Hi,
    ergänzend zu Toms Beitrag nachfolgend ein Beispiel mit Datenbindung.

    XAML:

    <Window x:Class="Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300" Loaded="Window_Loaded"
            xmlns:local="clr-namespace:WpfApplication2VB">
      <Window.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:Anzeigedaten}" ItemsSource="{Binding Path=SubItems}">
          <Grid>
            <TextBlock Text="{Binding Text}" />
          </Grid>
        </HierarchicalDataTemplate>
      </Window.Resources>
      <Grid>
        <TreeView ItemsSource="{Binding View}" />
      </Grid>
    </Window>

    Und dazu der CodeBehind

    Imports System.ComponentModel
    Imports System.Collections.ObjectModel
    Imports System.Data.SqlClient
    
    Public Class Window1
    
      Private cvs As CollectionViewSource
      Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
        Me.DataContext = Me
      End Sub
    
      Public ReadOnly Property View As ICollectionView
        Get
          If cvs Is Nothing OrElse cvs.View Is Nothing Then LoadData()
          Return cvs.View
        End Get
      End Property
    
      Private Sub LoadData()
        '
        cvs = New CollectionViewSource
        Dim liste As New ObservableCollection(Of Anzeigedaten)
        cvs.Source = liste
        '
        Using cn As New SqlConnection("Data Source=.;Initial Catalog=TestDB;Integrated Security=True")
          Using cmd As New SqlCommand("SELECT Hauptknoten, Unterknoten FROM bbredl", cn)
            cn.Open()
            Using reader As SqlDataReader = cmd.ExecuteReader
              While reader.Read
                Dim data As Anzeigedaten = (From itm In liste Where itm.Text = reader(0).ToString).FirstOrDefault
                If data Is Nothing Then
                  data = New Anzeigedaten With {.Text = reader(0).ToString}
                  liste.Add(data)
                End If
                data.SubItems.Add(New Anzeigedaten With {.Text = reader(1).ToString})
              End While
            End Using
          End Using
        End Using
      End Sub
    
    End Class
    
    Public Class Anzeigedaten
      Public Property Text As String
      Public Property SubItems As New ObservableCollection(Of Anzeigedaten)
    End Class
    

    --
    Peter Fleischer
    Meine Homepage mit Tipps und Tricks

    Samstag, 13. September 2014 04:39

Alle Antworten

  • Hallo,
    ich würde dir empfehlen die Daten per LINQ in Gruppen zu sortieren:
    Dim groups = reader.AsParallel().Cast(Of IDataRecord).GroupBy(Function(record) record("Ort"))
    Dies eGruppen können dann in einer Schleife durchlaufen werden um jeweils die oberen Elemente zu generieren. Die Unteren Elemente sind in den Gruppen jeweils enthalten und alssen sich so ebenfalls hinzufügen:
    For Each group In groups
        Dim tvi As New TreeViewItem
        tvi.Header = group.First()("Ort")
        For Each subRow In group
            Dim tviSub As New TreeViewItem
            tviSub.Header = subRow("Name")
            tvi.Items.Add(tviSub)
        Next
        trvServer.Items.Add(tvi)
    Next

    Besser wäre es aber wenn du die TreeView über ihre ItemsSOurce-Eigenschaft befüllst. Hierfür musst du jedoch die Daten in eine Hierarchische Form bringen und entsprechend Templates im XAML erzeugen. EIn Beispiel in C# findest du unter Simplifying the WPF TreeView by Using the ViewModel Pattern. In meiner Signatur steht ein Konverter, falls du den C# Code nicht verstehst.

    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Freitag, 12. September 2014 18:32
    Moderator
  • Hi,
    ergänzend zu Toms Beitrag nachfolgend ein Beispiel mit Datenbindung.

    XAML:

    <Window x:Class="Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300" Loaded="Window_Loaded"
            xmlns:local="clr-namespace:WpfApplication2VB">
      <Window.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:Anzeigedaten}" ItemsSource="{Binding Path=SubItems}">
          <Grid>
            <TextBlock Text="{Binding Text}" />
          </Grid>
        </HierarchicalDataTemplate>
      </Window.Resources>
      <Grid>
        <TreeView ItemsSource="{Binding View}" />
      </Grid>
    </Window>

    Und dazu der CodeBehind

    Imports System.ComponentModel
    Imports System.Collections.ObjectModel
    Imports System.Data.SqlClient
    
    Public Class Window1
    
      Private cvs As CollectionViewSource
      Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
        Me.DataContext = Me
      End Sub
    
      Public ReadOnly Property View As ICollectionView
        Get
          If cvs Is Nothing OrElse cvs.View Is Nothing Then LoadData()
          Return cvs.View
        End Get
      End Property
    
      Private Sub LoadData()
        '
        cvs = New CollectionViewSource
        Dim liste As New ObservableCollection(Of Anzeigedaten)
        cvs.Source = liste
        '
        Using cn As New SqlConnection("Data Source=.;Initial Catalog=TestDB;Integrated Security=True")
          Using cmd As New SqlCommand("SELECT Hauptknoten, Unterknoten FROM bbredl", cn)
            cn.Open()
            Using reader As SqlDataReader = cmd.ExecuteReader
              While reader.Read
                Dim data As Anzeigedaten = (From itm In liste Where itm.Text = reader(0).ToString).FirstOrDefault
                If data Is Nothing Then
                  data = New Anzeigedaten With {.Text = reader(0).ToString}
                  liste.Add(data)
                End If
                data.SubItems.Add(New Anzeigedaten With {.Text = reader(1).ToString})
              End While
            End Using
          End Using
        End Using
      End Sub
    
    End Class
    
    Public Class Anzeigedaten
      Public Property Text As String
      Public Property SubItems As New ObservableCollection(Of Anzeigedaten)
    End Class
    

    --
    Peter Fleischer
    Meine Homepage mit Tipps und Tricks

    Samstag, 13. September 2014 04:39