Hosten dynamischer WPF Controls in WinForms

  • WPF

Es gibt 1 Antwort in diesem Thema. Der letzte Beitrag () ist von exc-jdbi.

    Hosten dynamischer WPF Controls in WinForms

    Moin zusammen,

    ich sitze seit letzter Woche leider wieder einmal an einem Problem. Ich möchte in einer bestehenden WinForms Anwendung, die auf Basis einer Datenbank funktioniert eine TabPage zum ausfüllen einer Checkliste hinzufügen. Die Chekcliste wurde bislang händisch auf Papier ausgefüllt und soll nun ebenfalls in der Datenbank gespeichert werden. Um die Checklisten optisch möglichst gleich zu halten, habe ich mich dazu entschlossen dafür WPF Controls zu erstellen, da ich persönlich die Umsetzung in WPF leichter fand, und diese dann innerhalb eines ElementHosts ElementHost_CHECKLISTS auf der entschprechenden TabPage zu hosten.

    Links neben der eigentlichen Checkliste befindet sich eine ListBox, mit deren Hilfe der User eine Checkliste selektieren kann, die dann rechts danebene angezeigt werden soll (vgl. ChecklistControl.jpeg)
    Für jede Checkliste gibt es im oberen Teil einen Bereich, der vom Aufbau immer gleich ist. Anschließend folgt ein dynamisch erzeugter Bereich, der je nach Checkliste unterschiedlich lang ist. Konkret heßt das, nach dem fixen Teil befindet sich ein StackPanel, zu dem dynamisch weitere Controls (DepartmentControls) hinzugefügt werden.

    In jeder Zeile diese DepartmentControls gibt es Kategorien, zu der der User eine Auswahl treffen soll. Das wird über RadioButtons und entsprechenden RadioButtonGroups realisiert. Am Ende werden die Werte dann in die Datenbank geschrieben. Beim erneuten Laden der Listen passiert nun aber Folgendes:
    • Die dynamisch erzeugten DepartmentControls werden geladen und richtig dem StackPanel der ChecklistControl hinzugefügt
    • Die Kommentare in der "Additional comments" Spalte ganz rechts werden richtig geladen und richtig zugeordnet.
    • ABER: Die User Auswahl der RadioButtons wird nur für das letzte hinzugefügte DepartmentControl geladen. Alle zuvor hinzugefügten beinhalten absolut keine Auswahl.
    Zunächt der Quellcode:

    ChecklistControl.xaml:

    XML-Quellcode

    1. <UserControl>
    2. <DockPanel>
    3. <ScrollViewer>
    4. <Grid>
    5. ...
    6. <StackPanel x:Name="SP_Corners" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
    7. </StackPanel>
    8. </Grid>
    9. </ScrollViewer>
    10. </DockPanel>
    11. </UserControl>


    Dann die zu diesem StackPanel hinzugefügten DeparmentControls, nur beispielhaft die erste Zeile:

    XML-Quellcode

    1. <UserControl x:Class="CornerChecklistControl"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    6. xmlns:local="clr-namespace:RMG_Tracknote"
    7. mc:Ignorable="d"
    8. d:DesignHeight="200" d:DesignWidth="1149" Height="104"
    9. Background="White"
    10. FontSize="9pt" FontFamily="Segeo UI" Margin="0,-1,0,-1" HorizontalAlignment="Stretch">
    11. <Border BorderBrush="Black" BorderThickness="2,1,2,2" Height="104">
    12. <Grid>
    13. <Grid.RowDefinitions>
    14. <RowDefinition Height="20"/>
    15. <RowDefinition Height="20"/>
    16. <RowDefinition Height="20"/>
    17. <RowDefinition Height="20"/>
    18. <RowDefinition Height="20"/>
    19. </Grid.RowDefinitions>
    20. <Grid.ColumnDefinitions>
    21. <ColumnDefinition Width="50"/>
    22. <ColumnDefinition Width="70"/>
    23. <ColumnDefinition Width="26"/>
    24. <ColumnDefinition Width="26"/>
    25. <ColumnDefinition Width="26"/>
    26. <ColumnDefinition Width="26"/>
    27. <ColumnDefinition Width="26"/>
    28. <ColumnDefinition Width="26"/>
    29. <ColumnDefinition Width="26"/>
    30. <ColumnDefinition Width="26"/>
    31. <ColumnDefinition Width="26"/>
    32. <ColumnDefinition Width="26"/>
    33. <ColumnDefinition Width="26"/>
    34. <ColumnDefinition Width="*"/>
    35. </Grid.ColumnDefinitions>
    36. <Border Grid.Column="0" Grid.Row="0" Grid.RowSpan="5" BorderBrush="Black" BorderThickness="1" Margin="-1,-1,0,-1">
    37. <TextBlock x:Name="TB_Department" Text="#" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="12pt" FontWeight="Bold"/>
    38. </Border>
    39. <Border Grid.Column="13" Grid.Row="0" Grid.RowSpan="5" BorderBrush="Black" BorderThickness="1" Margin="0,-1,-1,-1">
    40. <TextBox x:Name="TB_Comments" Text="Comments" TextAlignment="Left" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="10,5" BorderThickness="0"/>
    41. </Border>
    42. <Border Grid.Column="2" Grid.Row="0" BorderBrush="Black" BorderThickness="1" Margin="-1,-1,0,0">
    43. <RadioButton x:Name="RB_Cat1_M5" VerticalAlignment="Center" HorizontalAlignment="Center" GroupName="Brakelock"/>
    44. </Border>
    45. <Border Grid.Column="3" Grid.Row="0" BorderBrush="Black" BorderThickness="1" Margin="-1,-1,0,0">
    46. <RadioButton x:Name="RB_Cat1_M4" VerticalAlignment="Center" HorizontalAlignment="Center" GroupName="Brakelock"/>
    47. </Border>
    48. <Border Grid.Column="4" Grid.Row="0" BorderBrush="Black" BorderThickness="1" Margin="-1,-1,0,0">
    49. <RadioButton x:Name="RB_Cat1_M3" VerticalAlignment="Center" HorizontalAlignment="Center" GroupName="Brakelock"/>
    50. </Border>
    51. <Border Grid.Column="5" Grid.Row="0" BorderBrush="Black" BorderThickness="1" Margin="-1,-1,0,0">
    52. <RadioButton x:Name="RB_Cat1_M2" VerticalAlignment="Center" HorizontalAlignment="Center" GroupName="Brakelock"/>
    53. </Border>
    54. <Border Grid.Column="6" Grid.Row="0" BorderBrush="Black" BorderThickness="1" Margin="-1,-1,0,0">
    55. <RadioButton x:Name="RB_Cat1_M1" VerticalAlignment="Center" HorizontalAlignment="Center" GroupName="Brakelock"/>
    56. </Border>
    57. <Border Grid.Column="7" Grid.Row="0" BorderBrush="Black" BorderThickness="1" Margin="-1,-1,0,0" Background="LightGray">
    58. <RadioButton x:Name="RB_Cat1_N" VerticalAlignment="Center" HorizontalAlignment="Center" GroupName="Brakelock"/>
    59. </Border>
    60. <Border Grid.Column="8" Grid.Row="0" BorderBrush="Black" BorderThickness="1" Margin="-1,-1,0,0">
    61. <RadioButton x:Name="RB_Cat1_1" VerticalAlignment="Center" HorizontalAlignment="Center" GroupName="Brakelock"/>
    62. </Border>
    63. <Border Grid.Column="9" Grid.Row="0" BorderBrush="Black" BorderThickness="1" Margin="-1,-1,0,0">
    64. <RadioButton x:Name="RB_Cat1_2" VerticalAlignment="Center" HorizontalAlignment="Center" GroupName="Brakelock"/>
    65. </Border>
    66. <Border Grid.Column="10" Grid.Row="0" BorderBrush="Black" BorderThickness="1" Margin="-1,-1,0,0">
    67. <RadioButton x:Name="RB_Cat1_3" VerticalAlignment="Center" HorizontalAlignment="Center" GroupName="Brakelock"/>
    68. </Border>
    69. <Border Grid.Column="11" Grid.Row="0" BorderBrush="Black" BorderThickness="1" Margin="-1,-1,0,0">
    70. <RadioButton x:Name="RB_Cat1_4" VerticalAlignment="Center" HorizontalAlignment="Center" GroupName="Brakelock"/>
    71. </Border>
    72. <Border Grid.Column="12" Grid.Row="0" BorderBrush="Black" BorderThickness="1" Margin="-1,-1,0,0">
    73. <RadioButton x:Name="RB_Cat1_5" VerticalAlignment="Center" HorizontalAlignment="Center" GroupName="Brakelock"/>
    74. </Border>
    75. ...
    76. </Grid>
    77. </Border>
    78. </UserControl>


    Laden der Daten aus der Datenbank und erzeugen der ChecklistControls:

    VB.NET-Quellcode

    1. Private Sub LoadChecklists()
    2. ListBox_CHECKLISTS.Items.Clear()
    3. Dim checklists As Data.DataTable = query_DataTable("SELECT ...")
    4. If checklists IsNot Nothing Then
    5. For Each list As DataRow In checklists.Rows
    6. Dim checklist As New ChecklistControl
    7. With checklist
    8. .EventName = evname
    9. .SessionName = list("stypename").ToString()
    10. .ListName = list("stypename").ToString() & " " & list("checklist_type").ToString()
    11. .Conditions = list("conditions").ToString()
    12. ....
    13. 'Fill general comment TextBlocks
    14. For i As Integer = 1 To 4
    15. If list("mlf_" & i).ToString() IsNot String.Empty And list("mlf_" & i).ToString() <> "0" Then
    16. .LimitingFactors(i - 1) = (list("mlf_" & i).ToString())
    17. End If
    18. If list("msf_" & i).ToString() IsNot String.Empty And list("msf_" & i).ToString() <> "0" Then
    19. .StrongFactors(i - 1) = (list("msf_" & i).ToString())
    20. End If
    21. Next
    22. End With
    23. Dim departments As Data.DataTable = query_DataTable("SELECT * FROM ...")
    24. If departments IsNot Nothing Then
    25. For Each department As DataRow In departments .Rows
    26. Dim rating As New DepartmentControl()
    27. With rating
    28. .DepartmentID = department("corner_no").ToString() 'Nach diesem Schritt stimmt die Auswahl des vorherigen DepartmentControls nicht mehr und alles ist IsChecked = False
    29. .Cat1= CShort(department("Cat1"))
    30. ...
    31. .Comment = department("comment").ToString()
    32. End With
    33. checklist.StackPanel_Departments.Children.Add(rating)
    34. Next
    35. End If
    36. ListBox_CHECKLISTS.Items.Add(checklist)
    37. Next
    38. End If
    39. End Sub
    40. Private Sub ListBox_CHECKLISTS_SelectedIndexChanged(sender As Object, e As EventArgs) Handles LB_CHECKLISTS.SelectedIndexChanged
    41. If ListBox_CHECKLISTS.SelectedItem IsNot Nothing Then
    42. Dim checklist As ChecklistControl = TryCast(ListBox_CHECKLISTS.SelectedItem, ChecklistControl)
    43. Me.ElementHost_CHECKLIST.Child = checklist
    44. End If
    45. End Sub


    Ich erzeuge für jede Dapertment ein neues DepartmentControl innerhalb einer For Each Schleife und füge es am Ende von jedem Durchgang dem StackPanel_Deparmentes des ChecklistControls hinzu. Meine erste Vermutung war nun, dass im nächsten Druchgang mit Dim rating As New DepartmentControl ich auch das Objekt im StackPanel neu erzeuge und dardurch alles überschrieben wird. Kann aber nicht sein, denn die Kommentare und ID's in den TextBlocks der einzenlnen DepartmentControls werden ja nicht überschrieben, sondern werden richtig zugeordnet. Wenn ich das Ganze debugge und die IsChecked Eigenschaften eines DepartmentControls überwache, stimmt zunächst alles, auch nach dem Hinzufügen zum StackPanel und nach dem Erzeugen des nächsten DepartmenControls. Erst beim Zuweisen der Werte für das nächste DepartmentControl werden die IsChecked Eigenschaften des vorherigen DepartmentControls alle auf False gesetzt.

    Grundsätzlich erfolgt innerhalb des DeparmentControls das setzten der IsChecked Eigenschaften der RadioButtons:

    VB.NET-Quellcode

    1. Public Sub SetSelection(value As Short, propertyName As String)
    2. If value >= 1 And value <= 5 Then
    3. Dim obj As System.Windows.Controls.RadioButton = TryCast(Me.FindName("RB_" & propertyName & "_" & value.ToString()), System.Windows.Controls.RadioButton)
    4. obj.IsChecked = True
    5. End If
    6. If value >= -5 And value <= -1 Then
    7. Dim obj As System.Windows.Controls.RadioButton = TryCast(Me.FindName("RB_" & propertyName & "_M" & (-1 * value).ToString()), System.Windows.Controls.RadioButton)
    8. obj.IsChecked = True
    9. End If
    10. If value = 0 Then
    11. Dim obj As System.Windows.Controls.RadioButton = TryCast(Me.FindName("RB_" & propertyName & "_N"), System.Windows.Controls.RadioButton)
    12. obj.IsChecked = True
    13. End If
    14. End Sub


    Es gibt Properties vom Typ Short um die Ratings der Kategorien zu speichern. Die passenden RadioButtons werden dann anhand des Namens identifiziert. Zum Beispiel: Wert für Kategorie 1 ist -3. Dann wird der RadioButton "RB_Cat1_M3".IsChecked = True gesetzt. Da sich der Name aus einem dynamisch erzeugten String zusammensetzt, spreche ich den RadioButton über Me.FindName(...) an.

    Was kann ich also tun, damit die IsChecked Eigenschaften nicht überschrieben werden, sondern genauso wie der Text der TextBlocks bestehen beleiben?

    Viel Grüße
    Andreas
    Bilder
    • Checklist_geladen.jpg

      642,25 kB, 1.920×1.050, 84 mal angesehen
    • ChecklistControl.jpg

      557,08 kB, 1.436×887, 77 mal angesehen
    • DepartmentControl.jpg

      84,53 kB, 1.150×105, 72 mal angesehen
    Zugegeben. Ich hab jetzt deinen Code nicht angeschaut. Aber ich weiss, dass man WPF-Controls in eine Forms Hosten kann.
    Dazu must du eine Instanz von ElementHost machen, und setzt diese in ein Panel.
    Nun eine Instanz des Wpf's-Controls und diese dann der ElementHost.Chield zuweisen.

    Sollte irgendwo hier stehen.
    red-gate.com/simple-talk/dotne…/mixing-wpf-and-winforms/

    Freundliche Grüsse

    exc-jdbi