none
ComboBox ComplexBinding RRS feed

  • Frage

  • Sorry das ich die Datenbindung noch nicht so recht verstanden habe. Die Combobox unter "Windows.Forms" hatte 4 Eigenschaften die man Binden konnte um den aktuellen Wert anzuzeigen und eine Liste möglicher Werte bereit zustellen.

    bei "Windows.Forms" sah das meist so aus:

    Combobox.DataSource = "Quelle für die Liste der möglichen Werte"
    Combobox.DisplayMember = "Das Feld für die Anzeigewert"
    Combobox.ValueMember = "Das Feld für die ID des Anzeigewertes welcher den Anzeigewert referenziert"
    Combobox.DataBinding = "Quelle für den aktuellen Datensatz welcher zur Zeit aktuell ist."

    Meine Daten stelle ich über ein DataSet bereit was vorher gefüllt wird. In diesem DataSet gibt es eine Tabelle für Referenzwerte mit einer Spalte "versand_id" und "versand_text". Und eine Tabelle mit den Detaildaten die nur einen Datensatz hat (da er schon vorher gefiltert aus der Datenbank abgerufen wurde) mit Spalten "name" ...... "versand" = ID für die Referenztabelle 

    Die Daten des Detaildatensatz habe ich dem DatenContext der GroupBox zugewiesen somit kann ich alle Textfelder an die entsprechenden Spalten binden. Die Daten der Referenzdaten habe ich dem DatenContext der Combobox zugewiesen. Die Liste der möglichen Werte sind in der Combobox vorhanden, aber leider funktioniert die Zuweisung an den Detaildatensatz nicht.

    Private Sub Window_Initialized(sender As Object, e As EventArgs) Handles Me.Initialized

    Me.gbStatus.DataContext = dataset.Tables("nwe_anschreiben").Rows(0) Me.cboVersand.DataContext = dataset.Tables("nwe_versand"), String.Empty, "versand_id ASC", System.Data.DataViewRowState.CurrentRows)
    End Sub


    <ComboBox x:Name="cboVersand" ... ItemsSource="{Binding}" DisplayMemberPath="versand_text" SelectedValuePath="versand_id" SelectedValue="{Binding Path=[versand], ElementName=gbStatus}"/>

    Kann mir da jemand helfen?

    Gruß Thomas

    Mittwoch, 17. Mai 2017 11:26

Antworten

  • Hi Thomas,
    die Combobox benötigt genau wie in Windows Forms zwei Datenquellen: eine Datenquelle für Liste und Referenz-ID und eine Datenquelle für Fremdschlüsselfeld. Wenn Du den DataContext nur auf die Datenquelle für Liste und Referenz-ID setzt, wie soll dann auf den Fremdschlüsselwert zugegriffen werden?

    Hier mal eine Demo. Dazu der XAML-Code:

    <Window x:Class="Window04"
            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:WpfApp1"
            mc:Ignorable="d"
            Title="Window04" Height="300" Width="300">
      <Window.Resources>
        <local:Window04VM x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}">
        <Grid.ColumnDefinitions>
          <ColumnDefinition/>
          <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <DataGrid Grid.Column="0"  ItemsSource="{Binding View1}"/>
        <Grid Grid.Column="1" DataContext="{Binding DetailView1}">
          <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Label Grid.Row="0" Content="{Binding ID}"/>
          <TextBox Grid.Row="1" Text="{Binding Info}"/>
          <ComboBox Grid.Row="2"
                    ItemsSource="{Binding View2, Source={StaticResource vm}}"
                    DisplayMemberPath="MasterInfo"
                    SelectedValuePath="ID"
                    SelectedValue="{Binding FK}"/>
        </Grid>
      </Grid>
    </Window>
    

    Dazu der ViewModel:

    Imports System.Collections.ObjectModel
    Imports System.ComponentModel
    Imports System.Data
    Imports System.Runtime.CompilerServices
    
    Public Class Window04VM
      Implements INotifyPropertyChanged
    
      Public Sub New()
        ds = GetData()
        cvs1.Source = ds.Tables("Child")
        AddHandler cvs1.View.CurrentChanged, Sub()
                                               DetailView1 = cvs1.View.CurrentItem
                                             End Sub
        cvs2.Source = ds.Tables("Master")
      End Sub
    
      Private rnd As New Random
      Private ds As DataSet
      Private cvs1 As New CollectionViewSource
      Private cvs2 As New CollectionViewSource
    
      Public ReadOnly Property View1 As ICollectionView
        Get
          Return cvs1.View
        End Get
      End Property
    
      Private _detailView1 As Object
      Public Property DetailView1 As Object
        Get
          Return Me._detailView1
        End Get
        Set(value As Object)
          Me._detailView1 = value
          OnPropertyChanged()
        End Set
      End Property
    
      Public ReadOnly Property View2 As ICollectionView
        Get
          Return cvs2.View
        End Get
      End Property
    
      Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
      Friend Sub OnPropertyChanged(<CallerMemberName> Optional propName As String = "")
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
      End Sub
    
      Private Function GetData() As DataSet
        Dim dtMaster As New DataTable("Master")
        With dtMaster
          With .Columns
            With .Add("ID", GetType(Integer))
              .AutoIncrement = True
              .AutoIncrementSeed = 1
              .AutoIncrementStep = 1
            End With
            .Add("MasterInfo", GetType(String))
          End With
          For i = 1 To 9
            Dim r = .NewRow
            r("MasterInfo") = $"Master {i}"
            .Rows.Add(r)
          Next
        End With
        Dim dtChild As New DataTable("Child")
        With dtChild
          With .Columns
            With .Add("ID", GetType(Integer))
              .AutoIncrement = True
              .AutoIncrementSeed = 1
              .AutoIncrementStep = 1
            End With
            .Add("FK", GetType(Integer))
            .Add("Info", GetType(String))
          End With
          For i = 1 To 100
            Dim r = .NewRow
            r("FK") = rnd.Next(1, 10)
            r("Info") = $"Zeile {i}"
            .Rows.Add(r)
          Next
        End With
        Dim ds As New DataSet
        ds.Tables.Add(dtMaster)
        ds.Tables.Add(dtChild)
        ds.AcceptChanges()
        Return ds
      End Function
    
    End Class


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    • Als Antwort markiert tommytom73 Donnerstag, 18. Mai 2017 07:20
    Mittwoch, 17. Mai 2017 20:14

Alle Antworten

  • Hi Thomas,
    die Combobox benötigt genau wie in Windows Forms zwei Datenquellen: eine Datenquelle für Liste und Referenz-ID und eine Datenquelle für Fremdschlüsselfeld. Wenn Du den DataContext nur auf die Datenquelle für Liste und Referenz-ID setzt, wie soll dann auf den Fremdschlüsselwert zugegriffen werden?

    Hier mal eine Demo. Dazu der XAML-Code:

    <Window x:Class="Window04"
            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:WpfApp1"
            mc:Ignorable="d"
            Title="Window04" Height="300" Width="300">
      <Window.Resources>
        <local:Window04VM x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}">
        <Grid.ColumnDefinitions>
          <ColumnDefinition/>
          <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <DataGrid Grid.Column="0"  ItemsSource="{Binding View1}"/>
        <Grid Grid.Column="1" DataContext="{Binding DetailView1}">
          <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Label Grid.Row="0" Content="{Binding ID}"/>
          <TextBox Grid.Row="1" Text="{Binding Info}"/>
          <ComboBox Grid.Row="2"
                    ItemsSource="{Binding View2, Source={StaticResource vm}}"
                    DisplayMemberPath="MasterInfo"
                    SelectedValuePath="ID"
                    SelectedValue="{Binding FK}"/>
        </Grid>
      </Grid>
    </Window>
    

    Dazu der ViewModel:

    Imports System.Collections.ObjectModel
    Imports System.ComponentModel
    Imports System.Data
    Imports System.Runtime.CompilerServices
    
    Public Class Window04VM
      Implements INotifyPropertyChanged
    
      Public Sub New()
        ds = GetData()
        cvs1.Source = ds.Tables("Child")
        AddHandler cvs1.View.CurrentChanged, Sub()
                                               DetailView1 = cvs1.View.CurrentItem
                                             End Sub
        cvs2.Source = ds.Tables("Master")
      End Sub
    
      Private rnd As New Random
      Private ds As DataSet
      Private cvs1 As New CollectionViewSource
      Private cvs2 As New CollectionViewSource
    
      Public ReadOnly Property View1 As ICollectionView
        Get
          Return cvs1.View
        End Get
      End Property
    
      Private _detailView1 As Object
      Public Property DetailView1 As Object
        Get
          Return Me._detailView1
        End Get
        Set(value As Object)
          Me._detailView1 = value
          OnPropertyChanged()
        End Set
      End Property
    
      Public ReadOnly Property View2 As ICollectionView
        Get
          Return cvs2.View
        End Get
      End Property
    
      Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
      Friend Sub OnPropertyChanged(<CallerMemberName> Optional propName As String = "")
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
      End Sub
    
      Private Function GetData() As DataSet
        Dim dtMaster As New DataTable("Master")
        With dtMaster
          With .Columns
            With .Add("ID", GetType(Integer))
              .AutoIncrement = True
              .AutoIncrementSeed = 1
              .AutoIncrementStep = 1
            End With
            .Add("MasterInfo", GetType(String))
          End With
          For i = 1 To 9
            Dim r = .NewRow
            r("MasterInfo") = $"Master {i}"
            .Rows.Add(r)
          Next
        End With
        Dim dtChild As New DataTable("Child")
        With dtChild
          With .Columns
            With .Add("ID", GetType(Integer))
              .AutoIncrement = True
              .AutoIncrementSeed = 1
              .AutoIncrementStep = 1
            End With
            .Add("FK", GetType(Integer))
            .Add("Info", GetType(String))
          End With
          For i = 1 To 100
            Dim r = .NewRow
            r("FK") = rnd.Next(1, 10)
            r("Info") = $"Zeile {i}"
            .Rows.Add(r)
          Next
        End With
        Dim ds As New DataSet
        ds.Tables.Add(dtMaster)
        ds.Tables.Add(dtChild)
        ds.AcceptChanges()
        Return ds
      End Function
    
    End Class


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    • Als Antwort markiert tommytom73 Donnerstag, 18. Mai 2017 07:20
    Mittwoch, 17. Mai 2017 20:14
  • Hallo Peter

    Schön mal wieder was von Dir zu lesen :-) Danke für Deine Ausführung die immer noch ein paar Fragezeichen in meinem Kopf erzeugen.

    Ich weiß das die Combobox für die Komplexe Bindung 4 Eigenschaften braucht. Die Frage ist für mich im Moment Hauptsächlich was es für Möglichkeiten gibt die Bindung über den DataContext anzugeben.

    Die Problemstellung die ich gerade hatte war ja relativ einfach gehalten, da ich den Detaildatensatz schon soweit gefiltert hatte das nur noch ein Datensatz über ist und ich die eine Datenzeile über eine Eigenschaft (Property) im Window verfügbar gemacht habe.

     Public ReadOnly Property GetAnschreiben As Data.DataView
      Get
       If Not IsNothing(Me.dbObjekt) Then
        Return New Data.DataView(Me.dbObjekt.GetData.Tables("nwe_anschreiben"))
       Else
        Return New Data.DataView
       End If
      End Get
     End Property

    Die Quelle der Auswahlobjekte für die Combobox ist auch über eine Eigenschaft (Property) im Window verfügbar.

    Public ReadOnly Property GetStatus As Data.DataView
      Get
       If Not IsNothing(Me.dbObjekt) Then
         Return New Data.DataView(Me.dbObjekt.GetData.Tables("nwe_status"), String.Empty, "status_id ASC", System.Data.DataViewRowState.CurrentRows)
       Else
         Return New Data.DataView
       End If
      End Get
    End Property

    Den anderen Code über die Implementierung von "ComponentModel.INotifyPropertyChanged" spare ich mir hier jetzt. Nur soviel noch, nachdem das Dataset mit Daten befüllt ist, wird dem DataContext der Combobox die Eigenschaft "GetStatus" zugewiesen.

    Me.cboStatus.DataContext = Me.GetStatus

    Ich habe gestern Abend noch ein wenig rumgespielt und es sogar hinbekommen so wie ich es haben wollte.

    <ComboBox x:Name="cboStatus" ... ItemsSource="{Binding}" DisplayMemberPath="status_text" SelectedValuePath="status_id" SelectedValue="{Binding Path=GetAnschreiben[0][status], ElementName=window, Mode=TwoWay}" SelectionChanged="ComboBoxStatusSelectedValueChanged"/>

    Was ich nicht so ganz verstehe und auch noch keine wirklich Informative Doku gefunden habe ist die Angabe der Bindung im XAML was es da für Möglichkeiten gibt irgend was anzugeben. Ich meine wie funktioniert die Angabe einer Relation z.B. oder wenn die Quelle ein Selektierter Eintrag einer ListBox wäre.

    Falls du dahingehen ein paar Links hättest oder ein Buch was du empfehlen könntest wäre ich sehr dankbar.

    Die Gestaltung von Dialogen in XAML ist ein Segen und eine echte Verbesserung gegenüber Windows.Forms, aber bei der Datenbindung ist der Groschen bei mir noch nicht ganz gerutscht.

    Viele Grüße
    Thomas

    Donnerstag, 18. Mai 2017 07:20