Laden...

Wie kann ich dynamisch Controls zu einem StackPanel hinzufügen?

Erstellt von GeneVorph vor 4 Jahren Letzter Beitrag vor 4 Jahren 1.770 Views
G
GeneVorph Themenstarter:in
180 Beiträge seit 2015
vor 4 Jahren
Wie kann ich dynamisch Controls zu einem StackPanel hinzufügen?

Hallo,

in einem wpf-Project möchte ich eine vorher definierbare Anzahl an TextBlocks einem StackPanel hinzufügen.

Für Testzwecke habe ich drei Objekte erstellt [Typ <Paths> s. unten] deren Property PathMessage in jeweils drei TextBlocks angezeigt werden soll. Es wird aber immer nur der erste TextBlock mit Text angezeigt.

Mein Code:


 public class Path
 {
     public string PathMessage { get; set; }
 }

Die Klasse Path-Model:


public class PathModel
    {
        public ObservableCollection<Path> Paths { get; set; }

        public PathModel()
        {
            Paths = new ObservableCollection<Path>();            
        }
            
    }

Programmeinstieg:


 public partial class MainWindow : Window
    {
        PathModel pModel { get; set; }

        public MainWindow()
        {
            InitializeComponent();

            PathModel PathModel = new PathModel();

            pModel = PathModel;

            MyStack.DataContext = pModel.Paths;

            FillMyStack();

        }        

        private void FillMyStack()
        {
            Path p1 = new Path();
            Path p2 = new Path();
            Path p3 = new Path();

            p1.PathMessage = "Ich bin Pfad Nummer eins";

            p2.PathMessage = "Ich bin der zweite Pfad";

            p3.PathMessage = "Pfad Nummer drei: das bin ich";

            pModel.Paths.Add(p1);
            pModel.Paths.Add(p2);
            pModel.Paths.Add(p3);
        }
    }

Und mein XAML-Code: Ich vermute hier liegt der Fehler - das Binding findet zwar statt, aber wie gesagt: es wird nur der erste Eintrag meiner ObservableCollection angezeigt:


        <StackPanel x:Name="MyStackl">
            <ItemsControl ItemsSource="{Binding Paths}">
                <ItemsControl.Template>
                    <ControlTemplate>
                        <TextBlock Text="{Binding PathMessage}"/>
                    </ControlTemplate>
                </ItemsControl.Template>
            </ItemsControl>            
        </StackPanel>

Vielen Dank schon mal im Voraus - wahrscheinlich sehe ich den Wald vor lauter Bäumen nicht...

Gruß
Vorph

C
132 Beiträge seit 2008
vor 4 Jahren

Du versuchst das ControlTemplate zu ändern. Was du hier brauchst ist das ItemTemplate. Nur damit kannst du das Aussehen der Items ändern.

5.658 Beiträge seit 2006
vor 4 Jahren

Hier gibt es ein Beispiel dafür: [Artikel] MVVM und DataBinding, Strg-F "ItemTemplate"

Weeks of programming can save you hours of planning

G
GeneVorph Themenstarter:in
180 Beiträge seit 2015
vor 4 Jahren

Ah - ich hab's geahnt; der Wald und die Bäume und so 😁

ItemTemplate - kaum macht man's richtig, schon funktioniert's! Danke auch für den Link!
Vielen Dank,
Gruß
Vorph

4.938 Beiträge seit 2008
vor 4 Jahren

Falsches Unterforum 😉

Hinweis von MrSparkle vor 4 Jahren

Habs verschoben, danke für den Hinweis

G
GeneVorph Themenstarter:in
180 Beiträge seit 2015
vor 4 Jahren

Ups - sorry. Ich dachte ich hätte es bei wpf und silverlight eingestellt.

G
GeneVorph Themenstarter:in
180 Beiträge seit 2015
vor 4 Jahren

OK, nun noch eine Anschlussfrage (bin mir nicht sicher, ob das in einen neuen Thread sollte, aber es passt ja zu meiner Ausgangsfrage):

Ich habe das jetzt auch mit anderen Controls probiert und habe dazu eine Frage zu TextBoxen. Wenn ich diese dynamisch generiere, kann ich de dann im Code ansprechen? Über eine Liste, mit Indizes?

Ein super simples Beispiel:


<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <StackPanel>
            <uc:UserControl_A x:Name="UC1" Grid.Column="2" Grid.Row="2"></uc:UserControl_A>
        </StackPanel>

    </Grid>

Hier mein User-Control:


<ItemsControl ItemsSource="{Binding Directives}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBox Margin="15,0">
                            <TextBox.Style>
                                <Style TargetType="TextBox">
                                    <Setter Property="Foreground" Value="Black" />
                                    <Setter Property="Text" Value="{Binding DirectiveMessage}"/>
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding DirectiveState}" Value="hasMessage">
                                            <Setter Property="Foreground" Value="Red" />
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </TextBox.Style>
                        </TextBox>                       
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

Die Datenobjekt-Klasse:


public class Directive
    {
        public string DirectiveMessage { get; set; }
        public DirectiveState DirectiveState {get;set;}
    }

    public enum DirectiveState
    {
        hasMessage = 0,
        hasNoMessage = 1
    }

Daten-Objekt-View


 public class MyView
    {
        public ObservableCollection<Directive> Directives { get; set; } = new ObservableCollection<Directive>();

        public MyView()
        {
            CreateDirectives();
        }

        private void CreateDirectives()
        {
            Directive d0 = new Directive();
            Directive d1 = new Directive();
            Directive d2 = new Directive();

            d0.DirectiveMessage = "abc";
            d0.DirectiveState = DirectiveState.hasMessage;

            d1.DirectiveMessage = "";
            d1.DirectiveState = DirectiveState.hasNoMessage;

            d2.DirectiveMessage = "def";
            d2.DirectiveState = DirectiveState.hasMessage;

            Directives.Add(d0);
            Directives.Add(d1);
            Directives.Add(d2);
        }

Uuuuuund MainWuindow.xaml.cs

MyView ViewTest { get; set; } = new MyView();

        public MainWindow()
        {
            InitializeComponent();

            DataContext = ViewTest;
        }

Das klappt soweit prima - aufgrund der DirectiveState-Property wird Text formatiert und über die ItemsControl des UserControl werden je nachdem wie viele Directives sich in der ObservableCollection "Directives" befinden, eine entsprechende Anzahl von TExtBoxen generiert. Wie würde man das jetzt angehen, wenn man z. B. in die 2. Textbox Text eingeben möchte und diesen dem DAtenobjekt wieder zuführen möchte; wie kann ich auf den Text dieser Textbox zugreifen? Nach welchen Begriffen müsste ich suchen, welche Stolperfallen ggf. umgehen?

Vielen Dank,
Gruß
Vorph

5.658 Beiträge seit 2006
vor 4 Jahren

wie kann ich auf den Text dieser Textbox zugreifen?

Genau dafür gibt es doch DataBinding. Ich empfehle, dir mal den Artikel dazu anzuschauen, denn dein Code ist alles andere als optimal. Hier noch ein paar Hinweise:

Du verwendest ein UserControl, wo ein einfaches DataTemplate ausreicht. UserControls sollten nur atomare Daten binden, d.h. bspw. keine Auflistungen von ViewModels. Siehe dazu z.B. DependencyProperties.

Deine Directive-Klasse wird als ViewModel verwendet, implementiert aber nicht das INotifyPropertyChanged-Interface

[Artikel] C#: Richtlinien für die Namensvergabe

[Artikel] MVVM und DataBinding

Weeks of programming can save you hours of planning

G
GeneVorph Themenstarter:in
180 Beiträge seit 2015
vor 4 Jahren

@Mr.Sparkle: Danke, für die Links zu den Artikeln - das wird etwas dauern, bis ich das alles "verdaut" habe. Mir würde es gerade sehr helfen, wenn ich wüsste wo genau mein Code suboptimal ist. Hättest du Tipps für einen Hobbyisten, wie ich mich der Thematik sinnvoll nähern kann (also womit sich zuerst beschäftigen, um dann darauf sinnvoll aufzubauen?)?

Die Idee der UserControl ist so auch nicht von mir - ich hatte dazu einen englischsprachigen Block gelesen, in dem es um UserControl, bzw. CustomControls ging. Dort was zu lesen, dass UserControls immer dann zum Einsatz kommen, wenn man ein Element sucht, bei dem einfache Objekte zusammengefasst werden und diese an verschiedenen Stellen gebraucht werden. Stimmt das so nicht?

Genau dafür gibt es doch DataBinding.

OK, sinnhaft besprochen, ich binde die Textbox an ein Model? Ich glaube, dann hätte ich zumindest so eine vage Ahnung, wo ich ansetzen müsste.

Einstweilen vielen Dank,
Gruß
Vorph

5.658 Beiträge seit 2006
vor 4 Jahren

Der Artikel zu MVVM und DataBinding wäre ein guter Einstieg. Es gibt dazu natürlich auch andere Artikel oder Videos im Netz, aber das würde die meisten deiner Fragen schonmal beantworten.

Über UserControls brauchst du erstmal nicht nachzudenken, du wirst schon merken, wenn du sowas brauchst.

Weeks of programming can save you hours of planning