none
wpf xaml code duplizieren RRS feed

  • Frage

  • Hallo zusammen,

    Ich programmiere gerade eine Oberfläche auf der 30 Prüfstände visualisiert werden. Hierfür hat sich ein Grid mit 10x3 Spalten/Reihen angeboten. Jedes der 30 Felder ist gleich aufgebaut, nur die Namen der Control-Element sind unterschiedlich (_P1 bis _P30 am Ende des Namens), damit ich in diese entsprechend im code-behind für eine Ausgabe von Daten zuweisen kann. (Ich arbeite erst seit August mit C# und WPF, lerne also gerade die Sprache erst kennen).

    Nun mein Problem:

    Ich suche nach einer Möglichkeit das Design nicht 30x kopieren und einfügen zu müssen, da ich sonst jede weitere Änderung 30x vornehmen muss. Mit UserControl bin ich gescheitert. Mit DataTemplate scheint es möglich zu sein, finde aber kein Beispiel an das ich mich hängen kann.

    Ich freue mich über jede Hilfe.

    Hier noch ein Auszug aus dem xaml code für einen der 30 Prüfstände, etwas gekürzt da sonst zu unübersichtlich:

    <Grid Grid.Column="1" Grid.Row="2" MouseLeftButtonUp="Grid_MouseLeftButtonUp_P2">
                <Border BorderThickness="1" BorderBrush="Black">
                    <Grid>
    
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition/>
    
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
    
                        <Label x:Name="Name_P2" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="18" FontWeight="Bold" Content="M408"/>
                        <ProgressBar Grid.Column="1" x:Name="BarP2" Minimum="0" Maximum="100" HorizontalAlignment="Center" VerticalAlignment="Center" Width="80" Height="24"/>
    
                        <Button ToolTip="Einstellungen" Grid.Column="1" Grid.Row="0" x:Name="Button_preferencesP2" VerticalAlignment="Top" HorizontalAlignment="Right" Background="Silver" Height="35" Width="32" BorderThickness="0" Grid.RowSpan="2" Click="Button_preferencesP2_Click">
                            <Image Source="Bilder\preferences.png" Width="30" Height="30" OpacityMask="Black"/>
                        </Button>
    
                        <Label Grid.Column="0" Grid.Row="1" x:Name="Bat1ID_P2"  Content="Prüfling 1" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="18"/>
                        <Label Grid.Column="1" Grid.Row="1" x:Name="Bat2ID_P2"  Content="Prüfling 2" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="18"/>
    
                        <CheckBox  Grid.Column="0" Grid.Row="2" x:Name="Bat1Status_P2"  HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        <CheckBox  Grid.Column="1" Grid.Row="2" x:Name="Bat2Status_P2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    
                        <TextBox Grid.Column="0" Grid.Row="3" x:Name="Spannung1_P2" Text="Spannung" TextAlignment="Center" HorizontalAlignment="Center" Height="24" TextWrapping="Wrap" VerticalAlignment="Center" Width="80" IsReadOnly="True" FontSize="16" Background="White" Foreground="Black"/>
                        <TextBox Grid.Column="1" Grid.Row="3" x:Name="Spannung2_P2" Text="Spannung" TextAlignment="Center" HorizontalAlignment="Center" Height="24" TextWrapping="Wrap" VerticalAlignment="Center" Width="80" IsReadOnly="True" FontSize="16" Background="White" Foreground="Black"/>
    
                        <TextBox Grid.Column="0" Grid.Row="4" x:Name="SOC1_P2" Text="SOC" TextAlignment="Center" HorizontalAlignment="Center" Height="24" TextWrapping="Wrap" VerticalAlignment="Center" Width="80" IsReadOnly="True" FontSize="16" Background="White" Foreground="Black"/>
                        <TextBox Grid.Column="1" Grid.Row="4" x:Name="SOC2_P2" Text="SOC" TextAlignment="Center" HorizontalAlignment="Center" Height="24" TextWrapping="Wrap" VerticalAlignment="Center" Width="80" IsReadOnly="True" FontSize="16" Background="White" Foreground="Black"/>
    
                        <TextBox Grid.Column="0" Grid.Row="5" x:Name="Temp1_P2" Text="Temperatur" TextAlignment="Center" HorizontalAlignment="Center" Height="25" TextWrapping="Wrap" VerticalAlignment="Center" Width="80" IsReadOnly="True" FontSize="16" Background="White" Foreground="Black"/>
                        <TextBox Grid.Column="1" Grid.Row="5" x:Name="Temp2_P2" Text="Temperatur" TextAlignment="Center" HorizontalAlignment="Center" Height="25" TextWrapping="Wrap" VerticalAlignment="Center" Width="80" IsReadOnly="True" FontSize="16" Background="White" Foreground="Black"/>
    

    Mittwoch, 28. November 2018 11:08

Alle Antworten

  • Hallo Josia,

    was genau klappt denn nicht, wenn Du ein Steuerelement erstellst. Genau dafür sind die eigentlich da!?


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET (2001-2018)
    https://www.asp-solutions.de/ - IT Beratung, Softwareentwicklung, Remotesupport

    Mittwoch, 28. November 2018 11:33
    Moderator
  • Hallo Josia,

    was genau klappt denn nicht, wenn Du ein Steuerelement erstellst. Genau dafür sind die eigentlich da!?


    Gruß, Stefan

    Hallo Stefan, vielen Dank für deine Antwort.

    ich hatte immer Probleme mit den Verweisen. Wie sich gerade für mich rausgestellt hat, habe ich es mir komplizierter als nötig gemacht, da ich ein "Fenster (WPF)" anstelle von einem "Benutzersteuerelement (WPF)" erstellt habe. Bei einem neuen Anlauf mit einem Benutzersteuerelement als Basis funktioniert es zumindest visuell wie gewünscht.

    Jetzt ergibt sich nur das Problem mit der Vergabe von Namen. Alle Textboxen/Label/etc. aller Prüfstände werden einer Liste zugewiesen, damit ich über einen Index den jeweiligen Prüfstand ansteuern kann. Sieht dann so aus:

    data.Tb0 = Spannung1_P1;

    data[i].Tb0.Text = DatenXY; Über i lässt sich dann Prüfstand 1-30 festlegen.

    In meinem UserControl ist der Name jetzt fest (bsp. Spannung1_P1). Beim debuggen stellt sich raus, das eine Zuweisung in die Liste nicht möglich ist (CS0103), da diese nicht mehr im aktuellen Kontext vorhanden sind. Im nächsten Schritt wird wohl ein Problem mit der Vergabe von gleichen Namen auftreten, sobald ich das UserControl mehrmals anwende.

    Über einen Ansatz wie ich hier weiter vorgehen sollte, wäre ich sehr dankbar!


    Mittwoch, 28. November 2018 13:33
  • Hallo Josia,

    ohne zu wissen, was Du da wo und wie in deine "Liste" stellst und wo genau Du dann wie darauf zugreifen willst, kann man nicht viel sagen.

    CS0103 besagt, dass dort, wo Du auf das Element zugreifen willst, da da Element in diesem Gültigkeitsbereich nicht existiert.

    Am einfachsten wäre es für uns, wenn Du mal ein Minimalbeispiel erstellst (also eine Liste, ein Control mit ein, zwei Eigenschaften und deinem Code, der dann darauf zugreift und den Fehler verursacht) und uns das hier zur Verfügung stellst.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET (2001-2018)
    https://www.asp-solutions.de/ - IT Beratung, Softwareentwicklung, Remotesupport

    Mittwoch, 28. November 2018 14:14
    Moderator
  • Hi Josia,
    die Vergabe fester Namen, die mit Eigenschaften mehrerer Datenobjekte (=Prüfstände) korrespondieren, ist unhandlich, aufwändig und fehleranfällig.

    Du kannst das gesamte Konzept so ändern, dass die Werte eines jeden Prüfstandes in einem separaten Datenobjekt abgelegt sind. Für die Darstellung eines Prüfstandes gibst dann ein UserControl, welches als DataContext das Datenobjekt bekommt und im UserControl dann die Eigenschaften dieses Datenobjektes (Prüfstand) gebunden werden. Alle Prüfstände (=Datenobjekte)werden dann in einer Liste gehalten, so dass eine wiederholte Darstellung mehrerer Prüfstände möglich ist, z.B. in einem DataGrid anstelle einem Grid wie in Deinem Codeauszug.


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

    Donnerstag, 29. November 2018 08:15
  • Hallo Peter und Stefan,

    danke für eure Hilfe. Ich versuche einmal mein Programm gekürzt darzustellen, um das Problem deutlicher zu machen. Ich denke aber der Vorschlag von Peter geht in die richtige Richtung.


    public partial class MainWindow : Window
    
    public List<CAN> data = new List<CAN>();
    
    public MainWindow()
            {
                InitializeComponent();
                
                Init_data();
            ...
            }
    public void Init_data()
            {
    
                CAN temp = new CAN();   //Temporäres Objekt anlegen um Liste zu füllen.
    	    //Der erste CAN-Ethernet Umsetzer
                temp.Tb0 = Spannung1_P1;
                temp.Tb1 = Spannung2_P1;
                temp.Tb2 = Temp1_P1; 
                temp.Tb3 = Temp2_P1; 
                data.Add(temp);
                //Der zweite CAN-Ethernet Umsetzer
                temp = new CAN();
                temp.Tb0 = Spannung1_P2;
                temp.Tb1 = Spannung2_P2;
                temp.Tb2 = Temp1_P2;
                data.Add(temp); //usw...
             }
    
            private void Ausgabe(VSCAN_MSG[] msgs, UInt32 read, int k)
            {
                                data[k].Tb0.Text = ((ZweiByte(msgs[i].Data[0], msgs[i].Data[1]) / 10).ToString() + " V");
                                data[k].Tb1.Text = ((ZweiByte(msgs[i].Data[2], msgs[i].Data[3]) / 10).ToString() + " V");
            }

    Wie die zugehörige xaml Datei aussieht, hatte ich oben ja schon gezeigt. Dort ist dann die fortlaufende Nummerierung der Textboxen mit _P1 - _P30. Z.b. Spannung1_P1 und Spannung1_P2.

    (Hier war ein Bild der Oberfläche, das ich leider nicht einfügen durfte. Schade, es verdeutlicht das Programm nochmals.)

    Für ein DataGrid müsste ich die Oberfläche neu gestalten, was ich sehr gerne vermeiden würde.

    Kann ich in das UserControl ein Binding für den Namen der Textbox schreiben, der direkt auf die List "data" verweist? Also per this.DataContext = data[k] die Liste hinzufügen und dann ein Binding in das Feld x:Name=""der Textboxen schreiben? Die Idee kommt mir gerade spontan, während dem verfassen dieses Beitrages.

    Freue mich über Rückmeldungen!

    EDIT: Wo muss ich "DataContext" einfügen? Ich nehme an in der UserControl xaml.cs Datei. Hier habe ich dann nur das Problem mit dem index k, der mir den Prüfstand angibt. Wie schaffe ich es, beim Aufrufen des UserControl in der MainWindows.xaml Datei, eine Information über k(also welcher Prüfstand) mitzugeben?
    Donnerstag, 29. November 2018 09:37