Matrixdarstellung in WPF DataGrid mit unbekannten Dimensionen und Namen

  • WPF

Es gibt 12 Antworten in diesem Thema. Der letzte Beitrag () ist von mpmichael.

    Matrixdarstellung in WPF DataGrid mit unbekannten Dimensionen und Namen

    Hallo,

    ich habe ein Problem mit der Darstellung eines Datagrids bei dem ich die Daten vorher nicht in einer Klasse ablegen kann, weil sich die Elemente erst zur Laufzeit ergeben.
    Als Beispiel:
    Ich habe eine Menge an Usern und ein User kann in verschiedenen Gruppen sein - die User werden erst zur Laufzeit eingelesen
    Dann habe ich eine Menge an Gruppen - diese werden auch zur Laufzeit eigelesen und die Anzahl ist unterschiedlich.
    Nun möchte ich das Ergebnis - also welche User ist Mitglied in welchen Gruppen in einem DataGrid anzeigen.
    Ich bekomme es nur nicht hin die Inhalte auf die Columns zu verteilen...

    Vielleicht ist es ja ganz einfach - aber das glaube ich nicht. Ich habe es in einem Beispiel zusammengefasst:

    XML-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:MatrixProb"
    7. mc:Ignorable="d"
    8. Title="MainWindow" Height="450" Width="800">
    9. <Grid>
    10. <DataGrid x:Name="DG_Matrix" HorizontalAlignment="Left" Height="325" Margin="10,55,0,0" VerticalAlignment="Top" Width="575" FontFamily="Consolas" FontSize="9" />
    11. <Button x:Name="BT_Load_Data" Click="Fill_Click" Content="Load" HorizontalAlignment="Left" Margin="10,20,0,0" VerticalAlignment="Top" Width="75" />
    12. <Button x:Name="BT_Auswertung" Click="Auswertung_Click" Content="Auswertung" HorizontalAlignment="Left" Margin="110,20,0,0" VerticalAlignment="Top" Width="75" />
    13. </Grid>
    14. </Window>


    Hier der Code:

    Visual Basic-Quellcode

    1. Class MainWindow
    2. Public Class Users
    3. Public Property SamAccountName As String
    4. Public Property DisplayName As String
    5. Public Property GroupMember As New List(Of String)
    6. End Class
    7. Public Class Groups
    8. Public Property GroupName As String
    9. Public Property SamAccountName As String
    10. End Class
    11. Dim TheUsers As New List(Of Users)
    12. Dim TheGroups As New List(Of Groups)
    13. Private Sub Fill_Click()
    14. Dim UserObj As New Users
    15. UserObj.DisplayName = "Mike Meyers"
    16. UserObj.SamAccountName = "MM"
    17. UserObj.GroupMember.Add("Intern")
    18. UserObj.GroupMember.Add("Empfang")
    19. UserObj.GroupMember.Add("Allgemeines")
    20. TheUsers.Add(UserObj)
    21. Dim UserObj2 As New Users
    22. UserObj2.DisplayName = "Frank Nobody"
    23. UserObj2.SamAccountName = "FN"
    24. UserObj2.GroupMember.Add("Intern")
    25. UserObj2.GroupMember.Add("Chef")
    26. UserObj2.GroupMember.Add("Allgemeines")
    27. TheUsers.Add(UserObj2)
    28. Dim UserObj3 As New Users
    29. UserObj3.DisplayName = "Polly Pocket"
    30. UserObj3.SamAccountName = "PP"
    31. UserObj3.GroupMember.Add("Intern")
    32. UserObj3.GroupMember.Add("Buchhaltung")
    33. UserObj3.GroupMember.Add("Allgemeines")
    34. TheUsers.Add(UserObj3)
    35. Dim GrpObj1 As New Groups
    36. GrpObj1.GroupName = "Interne Mitarbeiter"
    37. GrpObj1.SamAccountName = "Intern"
    38. TheGroups.Add(GrpObj1)
    39. Dim GrpObj2 As New Groups
    40. GrpObj2.GroupName = "Abteilung Empfang"
    41. GrpObj2.SamAccountName = "Empfang"
    42. TheGroups.Add(GrpObj2)
    43. Dim GrpObj3 As New Groups
    44. GrpObj3.GroupName = "Chefs"
    45. GrpObj3.SamAccountName = "Chef"
    46. TheGroups.Add(GrpObj3)
    47. Dim GrpObj4 As New Groups
    48. GrpObj4.GroupName = "Abteilung Buchhaltung"
    49. GrpObj4.SamAccountName = "Buchhaltung"
    50. TheGroups.Add(GrpObj4)
    51. Dim GrpObj5 As New Groups
    52. GrpObj5.GroupName = "Für Alle"
    53. GrpObj5.SamAccountName = "Allgemeines"
    54. TheGroups.Add(GrpObj5)
    55. BT_Load_Data.Background = Brushes.Green
    56. End Sub
    57. Private Sub Auswertung_Click()
    58. DG_Matrix.Columns.Add(New DataGridTextColumn With {.Header = "User", .Binding = New Binding(".")})
    59. For i = 0 To TheGroups.Count - 1
    60. Dim NCol As New DataGridTextColumn With {
    61. .Header = TheGroups(i).GroupName,
    62. .Binding = New Binding(".")
    63. }
    64. DG_Matrix.Columns.Add(NCol)
    65. Next
    66. For i = 0 To TheUsers.Count - 1
    67. Dim NRow As New DataGridRow
    68. Dim Grp As String = ""
    69. For Each GroupItem In TheGroups
    70. If Not TheUsers(i).GroupMember.FindIndex(Function(x) x.Equals(GroupItem.SamAccountName)) = -1 Then
    71. 'Match
    72. Grp += ",X"
    73. Else
    74. 'NoMatch
    75. Grp += ",O"
    76. End If
    77. Next
    78. NRow.Item = TheUsers(i).DisplayName & Grp
    79. DG_Matrix.Items.Add(NRow)
    80. Next
    81. End Sub
    82. End Class


    Ich vermute mein Fehler liegt im Bereich Zeile 85...

    Würde mich freuen, wenn jemand helfen könnte.
    Dateien
    • MatrixProb.zip

      (74,36 kB, 72 mal heruntergeladen, zuletzt: )
    Hallo,

    ich konnte es jetzt lösen - aber irgendwie finde ich es nicht besonders elegant. Vielleicht hat ja jemand einen Tipp wie man es besser machen könnte. Für alle die, die ein Ähnliches Problem haben und zumindest einen Ansatz brauchen - hier meine Momentane Lösung:

    XML-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:MatrixProb"
    7. mc:Ignorable="d"
    8. Title="MainWindow" Height="450" Width="800">
    9. <Grid>
    10. <DataGrid x:Name="DG_Matrix" HorizontalAlignment="Left" Height="325" Margin="10,55,0,0" VerticalAlignment="Top" Width="575" FontFamily="Consolas" FontSize="9" />
    11. <Button x:Name="BT_Load_Data" Click="Fill_Click" Content="Load" HorizontalAlignment="Left" Margin="10,20,0,0" VerticalAlignment="Top" Width="75" />
    12. <Button x:Name="BT_Auswertung" Click="Auswertung_Click" Content="Auswertung" HorizontalAlignment="Left" Margin="110,20,0,0" VerticalAlignment="Top" Width="75" />
    13. </Grid>
    14. </Window>


    Und der Code

    VB.NET-Quellcode

    1. ​Class MainWindow
    2. Public Class Mrtx
    3. Public Property UserName As String
    4. Public Property InGroup As Boolean()
    5. End Class
    6. Public Class Users
    7. Public Property SamAccountName As String
    8. Public Property DisplayName As String
    9. Public Property GroupMember As New List(Of String)
    10. End Class
    11. Public Class Groups
    12. Public Property GroupName As String
    13. Public Property SamAccountName As String
    14. End Class
    15. Dim TheUsers As New List(Of Users)
    16. Dim TheGroups As New List(Of Groups)
    17. Private Sub Fill_Click() 'Nur zu Demo - diese Daten werden normalerweise geladen
    18. Dim UserObj As New Users
    19. UserObj.DisplayName = "Mike Meyers"
    20. UserObj.SamAccountName = "MM"
    21. UserObj.GroupMember.Add("Intern")
    22. UserObj.GroupMember.Add("Empfang")
    23. UserObj.GroupMember.Add("Allgemeines")
    24. TheUsers.Add(UserObj)
    25. Dim UserObj2 As New Users
    26. UserObj2.DisplayName = "Frank Nobody"
    27. UserObj2.SamAccountName = "FN"
    28. UserObj2.GroupMember.Add("Intern")
    29. UserObj2.GroupMember.Add("Chef")
    30. UserObj2.GroupMember.Add("Allgemeines")
    31. TheUsers.Add(UserObj2)
    32. Dim UserObj3 As New Users
    33. UserObj3.DisplayName = "Polly Pocket"
    34. UserObj3.SamAccountName = "PP"
    35. UserObj3.GroupMember.Add("Intern")
    36. UserObj3.GroupMember.Add("Buchhaltung")
    37. UserObj3.GroupMember.Add("Allgemeines")
    38. TheUsers.Add(UserObj3)
    39. Dim GrpObj1 As New Groups
    40. GrpObj1.GroupName = "Interne Mitarbeiter"
    41. GrpObj1.SamAccountName = "Intern"
    42. TheGroups.Add(GrpObj1)
    43. Dim GrpObj2 As New Groups
    44. GrpObj2.GroupName = "Abteilung Empfang"
    45. GrpObj2.SamAccountName = "Empfang"
    46. TheGroups.Add(GrpObj2)
    47. Dim GrpObj3 As New Groups
    48. GrpObj3.GroupName = "Chefs"
    49. GrpObj3.SamAccountName = "Chef"
    50. TheGroups.Add(GrpObj3)
    51. Dim GrpObj4 As New Groups
    52. GrpObj4.GroupName = "Abteilung Buchhaltung"
    53. GrpObj4.SamAccountName = "Buchhaltung"
    54. TheGroups.Add(GrpObj4)
    55. Dim GrpObj5 As New Groups
    56. GrpObj5.GroupName = "Für Alle"
    57. GrpObj5.SamAccountName = "Allgemeines"
    58. TheGroups.Add(GrpObj5)
    59. BT_Load_Data.Background = Brushes.Green
    60. End Sub
    61. Private Sub Auswertung_Click()
    62. Dim MyMatrix As New List(Of Mrtx)
    63. DG_Matrix.Columns.Add(New DataGridTextColumn With {.Header = "User", .Binding = New Binding("UserName")})
    64. For i = 0 To TheGroups.Count - 1
    65. Dim NCol As New DataGridCheckBoxColumn With {
    66. .Header = TheGroups(i).GroupName,
    67. .Binding = New Binding("InGroup[" & i & "]")
    68. }
    69. DG_Matrix.Columns.Add(NCol)
    70. Next
    71. For i = 0 To TheUsers.Count - 1
    72. Dim NRow As New Mrtx With {
    73. .UserName = TheUsers(i).DisplayName
    74. }
    75. Dim GrpBools() As Boolean = New Boolean(TheGroups.Count) {}
    76. For j = 0 To TheGroups.Count - 1
    77. Dim Idx As Integer = j
    78. If Not TheUsers(i).GroupMember.FindIndex(Function(x) x.Equals(TheGroups(Idx).SamAccountName)) = -1 Then
    79. 'Match
    80. GrpBools(j) = True
    81. Else
    82. 'NoMatch
    83. GrpBools(j) = False
    84. End If
    85. Next
    86. NRow.InGroup = GrpBools
    87. MyMatrix.Add(NRow)
    88. Next
    89. DG_Matrix.AutoGenerateColumns = False
    90. DG_Matrix.CanUserAddRows = False
    91. DG_Matrix.ItemsSource = MyMatrix
    92. End Sub
    93. End Class


    Beste Grüße
    Martin
    Hallo in die Runde,

    jetzt habe ich noch eine Frage - aber vielleicht kann mir hier einer Profis (@'asusdk' oder @Nofear23m) einen guten Tipp geben?
    Nach dem ich mein Beispiel nun funktionsfähig bekommen habe - würde ich gerne die Zellen einfärben - z. B. grün, die "true" sind.

    Wie kann ich das - quasi dynamisch bei der Spaltenerstellung mitgeben?

    Vielen Dank
    Martin
    Probier mal so.
    Die 4 XXXX musst du durch deine Property ersetzen.

    XML-Quellcode

    1. <DataGrid x:Name="DG_Matrix" HorizontalAlignment="Left" Height="325" Margin="10,55,0,0" VerticalAlignment="Top" Width="575" FontFamily="Consolas" FontSize="9">
    2. <DataGrid.RowStyle>
    3. <Style TargetType="{x:Type DataGridRow}">
    4. <Style.Triggers>
    5. <DataTrigger Binding="{Binding XXXX}" Value="true">
    6. <Setter Property="Background" Value="Green"/>
    7. </DataTrigger>
    8. </Style.Triggers>
    9. </Style>
    10. </DataGrid.RowStyle>
    11. </DataGrid>
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.
    Hallo,

    @Akanel - super vielen Dank. Diese Option war mir so noch nicht bekannt. Vielleicht dazu noch folgende Fragen:

    1. Lassen sich diese Style Trigger auch in den Code bei der Dynamischen Erstellung der Spalten erzeugen
    2. oder - was noch besser wäre - kann ich dass auf Cell Ebene für das ganze Blatt setzen - denn eigentlich soll ja nur die Zeller eingefärbt werden und nicht eine ganze Zeile

    Beste Grüße
    Martin
    Hallo

    ich habe es jetzt mal hiermit versucht - hat aber leider keinen Effekt...Warum - verstehe ich absolut nicht.

    XML-Quellcode

    1. <DataGrid.CellStyle>
    2. <Style TargetType="{x:Type DataGridCell}">
    3. <Style.Triggers>
    4. <Trigger Property="Content" Value="True">
    5. <Setter Property="Background" Value="SeaGreen" />
    6. </Trigger>
    7. </Style.Triggers>
    8. </Style>
    9. </DataGrid.CellStyle>
    Hallo,

    vielen Dank. Hoffe mit dem Umbau klappt alles!

    Mir geht es noch immer um die "Matrix" die ich aufbaue. Ich möchte eigentlich nur die Felder (aber nicht die ganze Zeile) in der die CheckBox "true" ist grün hinterlegen.

    VB.NET-Quellcode

    1. Dim NCol As New DataGridCheckBoxColumn With {
    2. .Header = TheGroups(i).GroupName,
    3. .Binding = New Binding("InGroup[" & i & "]")
    4. }


    Da ich im Vorhinein ja nicht weiß wie viele Spalten erzeugt werden tu ich mich auch mit der Addressierung schwer.
    Ich vermute - aber auch das ist mir bisher nicht gelungen - würde ich bei der Spaltendefinition bereits den Style Trigger mitgeben - wäre die Sache vermutlich auch schon gelöst.
    Ich finde bei Microsoft alles mögliche aber nicht die Keywords die ich brauche.
    Hallo!

    Ich weiß nicht ob ich wirklich zu Deinem Problem was beitragen kann... Ich hatte vor kurzem ein Ähnliches Problem. Da ging es auch darum, dass die Anzahl der Zeilen bekannt war, die anzahl der Spalten erst zur Laufzeit berechnet wurden. Erschwerend hatte ich noch den Fall, dass die Spalten aus verschiedenen Klassen bestanden.

    Ich bin so vorgegangen, dass ich je Typ eine Klasse erstellt habe. Diese Klasse hat eine Eigenschaft bekommen die auf das dargestellt Element aus der Datenbank zeigt.

    Dann hab ich noch eine Funktion hinzugefügt, welche als Parameter das Grid, in welchem das Element angezeigt werden soll übergeben bekommt. Per For-Schleife habe ich für jede Zeile und jede Spalte die Elemente durchlaufen und jedes Element an der entsprechenden Zeile/Spalte im Grid hinzugefügt. Dazu war nötig bei jeder neuen spalte erstmal eine neue GridColumn hinzuzufügen, die Überschrift zu setzen und dann für jede Zeile das Element hinzuzufügen. In der Funktion werden per Code die erforderlichen UIElemente erzeugt und deren Eigenschaften gesetzt.

    Erstmal ist meine Lösung ein ziemliches Gebastel, aber es funktioniert.... Habe ich z. B. einen Boolschen Wert wird ein Checkbox-Element dargestellt. Das Click-Event wird beim Erstellen auch mit verknüpft und beim draufklicken wird der Ereignishandler aufgerufen. Da die ID des Elements in den Eigenschaften hinterlegt ist, wird der Wert in die Datenbank geschrieben.

    Vielleicht hilft Dir das
    Der Beitrag wurde aus 100% wiederverwendbaren Elektronen erstellt!
    Hallo und guten Morgen,

    ja danke - ist auch ein Überlegung! Wobei ich fast denke, dass es auch viel einfacher gehen müsste. Ich sehe da zwei Ansätze - aber um ehrlich zu sein glaube ich, dass es mir da dan am praktischen know-how fehlt und ich einfach die richtige Kobi aus den Schlüsselworten nicht kenne. Wenn ich nicht falsch liege, dann müsste es zwei mögliche - einfache Wege geben.

    1. Über den <datagrid.CellStyle> der wirkt sich ja auf die ganze Tabelle aus - da müsste ich nur noch wissen, wie ich dem Trigger beibringen kann, dass wen. die ClickBox=isChecked dann mache das Feld Grün
    2. Direkt beim Anlegen der Spalte dass man hier die Eigenschaft für die Spalte mitgibt - aber auch da denke ich, fehlt mir noch der richtige Syntax oder Schlüsselwort

    Akanel war mit seinem Beispiel schon echt super - nur das in seinem Beispiel die Bindung statisch ist. Das wäre natürlich bei Anlegen der Spalte auch (so glaube ich zumindest) möglich einen direkten Bezug zu schaffen - aber auch hier war ich "lost in syntax".
    LG
    Martin


    Beste Grüße
    Martin
    Hallo!
    Ja... Auf die beiden Möglichkeiten bin ich auch in ähnlicher Art und Weise gestoßen... Wobei es für den CellStyle noch die Möglichkeit gäbe einen template selector zu verwenden um je nach Datentyp eine andere Darstellung zu erreichen...

    Gescheitert bin ich aber momentan auch am Zeitmangel sowie Code Hürden.
    Der Beitrag wurde aus 100% wiederverwendbaren Elektronen erstellt!
    Hallo,

    ich habe mir deinen Code mal angesehen.
    An welche Property soll denn eigentlich gebunden werden und welche Spalten sollen sich dann einfärben?

    Das DataGrid hat die Eigenschaft AutoGenerateColums welche dir bei geeigneter Klasse alles generiert.
    Bei dir sind es momentan nur 2 Porpertys an die man Binden kann, aber der Rest vom Code Hebelt das Binding wieder aus weil du alles Fix vorgibst.

    Mir scheint du müsstest dir eine geeignete Klasse bauen die alle Daten vorhält und an diese kannst du dann Binden.

    Ich versuche dir mal ein kleines Beispiel zu Basteln.
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.
    Hallo,

    ich würde es ja tatsächlich gerne an eine Eigenschaft wie z. B. "isChecked" binden wollen. Ich habe im Netz etwas gefunden - aber leider geht es da um TextBoxen - aber da bezieht sich der Schreiber auf die Inhalte.
    Mein Gedanke war das zu Adaptieren und oder im Code unterzubringen. Hier das Beispiel aus dem Netz:


    XML-Quellcode

    1. <DataGridTextColumn Binding="{Binding Name}">
    2. <DataGridTextColumn.ElementStyle>
    3. <Style TargetType="{x:Type TextBlock}">
    4. <Style.Triggers>
    5. <Trigger Property="Text" Value="John">
    6. <Setter Property="Background" Value="LightGreen"/>
    7. </Trigger>
    8. </Style.Triggers>
    9. </Style>
    10. </DataGridTextColumn.ElementStyle>
    11. </DataGridTextColumn>