WPF Datagrid Binding BindingList aus Klasse

  • WPF
  • .NET (FX) 4.5–4.8

Es gibt 5 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    WPF Datagrid Binding BindingList aus Klasse

    Hallo,
    ich bin auf der Suche nach einer für mich verständlichen Anleitung um eine ObservableCollection an ein Datagrid zu binden. Ich würde das Binding gerne im Windows (xaml) gerne durchführen. Über reinen Code klappt es, aber ich möchte auch Wissen wie es über die Form geht.
    Nehmen wir mal an ich habe eine Klasse Kunden die eine BindingList von Kontakt enthält.

    VB.NET-Quellcode

    1. Public Class Kunden
    2. Public Property Kontakte As New ObservableCollection(Of Kunden.Kontakt)
    3. Public Class Kontakt
    4. Private _Nachname As String
    5. Private _Vorname As String
    6. Public Property Nachname As String
    7. Get
    8. Return _Nachname
    9. End Get
    10. Set(value As String)
    11. If _Nachname = value Then
    12. Return
    13. End If
    14. _Nachname = value
    15. End Set
    16. End Property
    17. Public Property Vorname As String
    18. Get
    19. Return _Vorname
    20. End Get
    21. Set(value As String)
    22. If _Vorname = value Then
    23. Return
    24. End If
    25. _Vorname = value
    26. End Set
    27. End Property
    28. End Class
    29. End Class


    Das Windows ist wie folgt aufgebaut:
    MainWindows.xaml

    VB.NET-Quellcode

    1. <Window x:Class="MainWindow"
    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:Testerei"
    7. mc:Ignorable="d"
    8. Title="Test DataGrid binding" Height="450" Width="800">
    9. <Grid>
    10. <DataGrid x:Name="DataGridKontakte" Margin="10,10,10,33" AutoGenerateColumns="False" CanUserDeleteRows="False" CanUserAddRows="False" >
    11. <DataGrid.Columns >
    12. <DataGridTextColumn Header="Nachname" Binding="{Binding Nachname}"/>
    13. <DataGridTextColumn Header="Vorname" Binding="{Binding Vorname}"/>
    14. </DataGrid.Columns>
    15. </DataGrid>
    16. <TextBox x:Name="TextBoxSlectedNachname" Margin="10,0,10,10" TextWrapping="Wrap" Text="TextBox" Height="18" VerticalAlignment="Bottom"/>
    17. </Grid>
    18. </Window>

    Der dazugehörige Code
    MainWindows.xaml.vb

    VB.NET-Quellcode

    1. Class MainWindow
    2. Dim myKunden As New Kunden
    3. Sub New()
    4. ' Dieser Aufruf ist für den Designer erforderlich.
    5. InitializeComponent()
    6. ' Fügen Sie Initialisierungen nach dem InitializeComponent()-Aufruf hinzu.
    7. Dim XMLDataFile As String = "D:\Kunden.xml"
    8. Dim XmlReader As New System.Xml.Serialization.XmlSerializer(GetType(Kunden))
    9. Dim XmlFile As New System.IO.StreamReader(XMLDataFile)
    10. myKunden = CType(XmlReader.Deserialize(XmlFile), Kunden)
    11. XmlFile.Close()
    12. Me.DataGridKontakte.ItemsSource = myKunden.Kontakte
    13. End Sub
    14. End Class


    Meine XML Testdaten:

    XML-Quellcode

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <Kunden xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    3. <Kontakte>
    4. <Kontakt>
    5. <Nachname>Mustermann</Nachname>
    6. <Vorname>Max</Vorname>
    7. </Kontakt>
    8. <Kontakt>
    9. <Nachname>Mustermann</Nachname>
    10. <Vorname>Irene</Vorname>
    11. </Kontakt>
    12. <Kontakt>
    13. <Nachname>Wurst</Nachname>
    14. <Vorname>Hans</Vorname>
    15. </Kontakt>
    16. </Kontakte>
    17. </Kunden>



    Nun zu meinen Fragen. Wie Binden ich ObservableCollection Kontake an das DataGrid. Es soll nicht über VB passieren, so wie in diesem Beispiel.
    Sondern ich möchte gerne die Datasource im XAML des Windows definieren.
    Im zweiten Schritt würde ich gerne in der Textbox den Nachnamen des ausgewählten Datensatzes anzeigen.
    Über ejne Anregung, Hilfestellung, Hinweis, Link bin ich dankbar. Was mir nicht hilft, ist die Microsoft Hilfe. Da habe ich schon gesucht und verstehe da leider teilweise nur Bahnhof.
    Danke für eure Hilfe

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

    grundsätzlich so: Grundlagen - MVVM: "Binding-Picking" im Xaml-Editor
    Allerdings nutzt man in wpf lieber die ObservableCollection(Of T)

    Und guck dir auch dieses: Grundlagen - MVVM-Anwendungs-Struktur

    Hier kommen auch ausdrücklich DGs zum Zuge: die vier Views (in Wpf)


    Aber sie v.a. zu, dass du mit BindingPicking arbeitest.
    Bei ungünstiger Anwendungs-Struktur ist das versaut, und du machst dir für alle Zukunft die Xaml-Arbeit unnötig schwer.

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

    Vielen Dank für deine Hilfestellung. Anhand dieser habe ich das beigefügte TestProjekt erstellt. Und haben nun neue Probleme die ich in dem vorherigen Test nicht hatte. Ich würde gerne über eine Funktion der Klasse Kunden eine XML Datei einlesen und per XmlSerializer und Deserialize als Kunden zurückgeben. In anderen Testprojekten habe ich das genau so gemacht. Aber in diesem Projekt klappt es nicht. Auch wenn ich den Code in das MainViewModel hinzufüge bekomme ich den gleichen Fehler und den verstehe ich leider nicht.

    Quellcode

    1. InvalidOperationException: 'VB.NET_WPF_MVVM_Datagrid.Models.Kunden.Kontakt' kann nicht serialisiert werden, weil dafür kein parameterloser Konstruktor verfügbar ist.

    Vielleicht ist das auch der verkehrte Weg, wie ich zu meinem Ziel kommen will. Denn mit dieser Art der Programmierung tue ich mich noch sehr schwer. Ich komme halt aus der VB6 Welt und will jetzt endlich mal was moderner werden.
    Wie gesagt, für Hilfestellungen, Anregungen, Lösungen bin ich dankbar. Das hier, hat mich ca. 10 Stunden Zeit gekostet, bis ich es im Ansatz verstanden habe. Das richtige Verständnis wird sich erst später einstellen.

    Ach ja, noch etwas. Das ganze mache ich nur, damit ich dieses Thema besser verstehe.

    Für alle die sich das Projekt direkt anschauen wollen hier der Code:
    MainView.xaml, hier werden Binding definiert.

    XML-Quellcode

    1. <Window x:Class="Views.MainView"
    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:VB.NET_WPF_MVVM_Datagrid"
    7. mc:Ignorable="d"
    8. xmlns:vm="clr-namespace:VB.NET_WPF_MVVM_Datagrid.ViewModels"
    9. Title="MainView" Height="450" Width="800">
    10. <Window.DataContext>
    11. <vm:MainViewModel />
    12. </Window.DataContext>
    13. <Grid>
    14. <Grid.RowDefinitions >
    15. <RowDefinition Height="*"/>
    16. <RowDefinition Height="Auto"/>
    17. </Grid.RowDefinitions>
    18. <Grid.ColumnDefinitions >
    19. <ColumnDefinition Width="Auto"/>
    20. <ColumnDefinition Width="*"/>
    21. </Grid.ColumnDefinitions>
    22. <DataGrid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding Kontakte}" SelectedItem="{Binding SelectedKontakt}" AutoGenerateColumns="False" Margin="10,10,10,10">
    23. <DataGrid.Columns >
    24. <DataGridTextColumn Header="Nachname" Binding="{Binding Nachname}"/>
    25. <DataGridTextColumn Header="Vorname" Binding="{Binding Vorname}"/>
    26. </DataGrid.Columns>
    27. </DataGrid>
    28. <StackPanel Grid.Row="1" Grid.Column="0">
    29. <StackPanel.Resources >
    30. <Style TargetType="{x:Type Label}">
    31. <Setter Property="Margin" Value="10,0,10,10"/>
    32. <Setter Property="Height" Value="26"/>
    33. </Style>
    34. </StackPanel.Resources>
    35. <Label Content="Nachname" />
    36. <Label Content="Vorname"/>
    37. </StackPanel>
    38. <StackPanel Grid.Row="1" Grid.Column="1">
    39. <StackPanel.Resources >
    40. <Style TargetType="{x:Type TextBox}">
    41. <Setter Property="Margin" Value="10,0,10,10"/>
    42. <Setter Property="Height" Value="26"/>
    43. </Style>
    44. </StackPanel.Resources>
    45. <TextBox TextWrapping="Wrap" Text="{Binding SelectedKontakt.Vorname }" />
    46. <TextBox TextWrapping="Wrap" Text="{Binding SelectedKontakt.Nachname }" />
    47. </StackPanel>
    48. </Grid>
    49. </Window>


    MainViewModel.vb Hier werden die Daten und die Logik bereitgestellt (So würde ich das Bezeichnen)

    VB.NET-Quellcode

    1. Imports System.Collections.ObjectModel
    2. Imports VB.NET_WPF_MVVM_Datagrid.Models
    3. Imports VB.NET_WPF_MVVM_Datagrid.Utils
    4. Namespace ViewModels
    5. Public Class MainViewModel
    6. Inherits PropertyChangedBase
    7. Private _SelectedKontakt As Kunden.Kontakt
    8. Dim objKunden As New Kunden
    9. Public Property Kontakte As New ObservableCollection(Of Kunden.Kontakt)
    10. Public Property SelectedKontakt As Kunden.Kontakt
    11. Get
    12. Return _SelectedKontakt
    13. End Get
    14. Set(value As Kunden.Kontakt)
    15. If _SelectedKontakt Is value Then
    16. Return
    17. End If
    18. _SelectedKontakt = value
    19. NotifyOfPropertyChange()
    20. End Set
    21. End Property
    22. Sub New()
    23. 'objKunden = objKunden.LoadXMLKontakte("D:\Kunden.xml")
    24. 'Kontakte = objKunden.Kontakte
    25. 'Dim XMLDataFile As String = "D:\Kunden.xml"
    26. 'Dim XmlReader As New System.Xml.Serialization.XmlSerializer(GetType(Models.Kunden))
    27. 'Dim XmlFile As New System.IO.StreamReader(XMLDataFile)
    28. 'objKunden = CType(XmlReader.Deserialize(XmlFile), Kunden)
    29. 'XmlFile.Close()
    30. 'Kontakte = New ObservableCollection(Of Kunden.Kontakt)
    31. objKunden.Kontakte.Add(New Kunden.Kontakt("Mustermann", "Max"))
    32. objKunden.Kontakte.Add(New Kunden.Kontakt("Mustermann", "Irene"))
    33. objKunden.Kontakte.Add(New Kunden.Kontakt("Wurst", "Hans"))
    34. Kontakte = objKunden.Kontakte
    35. 'Dim XmlWRiter As New System.Xml.Serialization.XmlSerializer(GetType(Kunden))
    36. 'Dim XmlFileWrite As New System.IO.StreamWriter("D:\Kunden2.xml")
    37. 'XmlWRiter.Serialize(XmlFileWrite, objKunden)
    38. 'XmlFileWrite.Close()
    39. 'objKunden.SaveKontakteAsXML("D.\Kunden2.xml", objKunden)
    40. End Sub
    41. End Class
    42. End Namespace


    Und meine Klasse mit den Kontakten
    Kunden.vb

    VB.NET-Quellcode

    1. Imports System.Collections.ObjectModel
    2. Imports System.Security.Cryptography
    3. Imports System.ComponentModel
    4. Imports System.Xml.Serialization
    5. Namespace Models
    6. Public Class Kunden
    7. Public Property Kontakte As New ObservableCollection(Of Kunden.Kontakt)
    8. Public Class Kontakt
    9. Private _Nachname As String
    10. Private _Vorname As String
    11. Public Property Nachname As String
    12. Get
    13. Return _Nachname
    14. End Get
    15. Set(value As String)
    16. If _Nachname = value Then
    17. Return
    18. End If
    19. _Nachname = value
    20. End Set
    21. End Property
    22. Public Property Vorname As String
    23. Get
    24. Return _Vorname
    25. End Get
    26. Set(value As String)
    27. If _Vorname = value Then
    28. Return
    29. End If
    30. _Vorname = value
    31. End Set
    32. End Property
    33. Sub New(Optional ByVal InputNachname As String = "", Optional ByVal InputVorname As String = "")
    34. _Nachname = InputNachname
    35. _Vorname = InputVorname
    36. End Sub
    37. End Class
    38. Public Function LoadXMLKontakte(Filename As String) As Kunden
    39. Dim objKunden As New Models.Kunden
    40. Dim XmlReader As New System.Xml.Serialization.XmlSerializer(GetType(Models.Kunden))
    41. Dim XmlFile As New System.IO.StreamReader(Filename)
    42. objKunden = CType(XmlReader.Deserialize(XmlFile), Kunden)
    43. Return objKunden
    44. XmlFile.Close()
    45. End Function
    46. Public Sub SaveKontakteAsXML(Filename As String, MyKunden As Kunden)
    47. Dim XmlWRiter As New System.Xml.Serialization.XmlSerializer(GetType(Kunden))
    48. Dim XmlFileWrite As New System.IO.StreamWriter(Filename)
    49. XmlWRiter.Serialize(XmlFileWrite, MyKunden)
    50. XmlFileWrite.Close()
    51. End Sub
    52. End Class
    53. End Namespace


    Die PropertyChangedBase.vb darf natürlich nicht fehlen:
    Imports System.ComponentModel
    Imports System.Runtime.CompilerServices

    Namespace Utils

    VB.NET-Quellcode

    1. Public Class PropertyChangedBase
    2. Implements INotifyPropertyChanged
    3. Protected Sub NotifyOfPropertyChange(<CallerMemberName> Optional PropertyName As String = Nothing)
    4. RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
    5. End Sub
    6. Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    7. End Class
    8. End Namespace


    Ach ja, die XMLDatei für die Beispieldaten sind gleich geblieben. Sind aber im Projekt dabei.
    Dateien

    bahnski schrieb:

    InvalidOperationException: 'VB.NET_WPF_MVVM_Datagrid.Models.Kunden.Kontakt' kann nicht serialisiert werden, weil dafür kein parameterloser Konstruktor verfügbar ist
    Der Fehler hat nix mit Wpf oder sonstwas zu tun.
    Mach einfach, was da steht: Gib der Klasse Kontakt einen parameterlosen Konstruktor (= Sub New).

    Wenns funzt sag bescheid - und hängs an. Dann kann ich das mal vereinfachen. (ZB eine Klasse Kunden brauchst du nicht. Und der DateiZugriff sollte im MainViewmodel erfolgen - oder statisch sein.)

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