Entwickler-Ecke

WPF / Silverlight - TextBoxen im Code erstellen


csharpuser1 - Di 22.11.16 15:40
Titel: TextBoxen im Code erstellen
Guten Tag,

ist es möglich, eine Textbox / Checkbox / DropDown Menus bzw Comboboxen im Code zu generieren? Ich bekomme von einer Klasse eine int Variable Anzahl übergeben und in abhängigkeit dieser Variable möchte ich eine Reihe von TextBoxen übereinander erstellen.

Als Test einfach im init geschrieben


C#-Quelltext
1:
2:
3:
4:
5:
TextBlock txt = new TextBlock();
            txt.Name = "txtt";
            txt.TextWrapping = TextWrapping.Wrap;
            txt.Text = "fdsafdsa";
            txt.Margin = new Thickness(303000);



Aber dann erscheint keine Textbox :(.

EDIT: füge ich this.AddChild(txt); hinzu erhalte ihc einen error

EDIT2: Okay, eine Lösung dei ich gefunden habe ist ein StackPanel einzufügen und dann panel.Children.Add(txt) die Box hinzufügen, funktioneirt soweit, gibt es noch schönere Methoden?

Im Prinzip müsste ich folgende Elemente zeilenweise einfügen :

Textbox Checkbox DropDownMenu/Combobox


EDIT3:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
private void Button_Click(object sender, RoutedEventArgs e)
        {
            
            TextBlock txt = new TextBlock();
            txt.Name = "txtt";
            txt.TextWrapping = TextWrapping.Wrap;
            txt.Text = "fdsafdsa";
            txt.Margin = new Thickness(303000);

            CheckBox box = new CheckBox();
            box.IsChecked = true;
            box.Margin = new Thickness(603000);

            panel.Children.Add(box);
            panel.Children.Add(txt);
        }


Mit dem Code fügt er die Textbox und die Checkbox nicht nebeneinander ein, sondern übereinander, was wohl am stackpanel liegt. Außerdem hätte ich gerne, dass es wie bei einer Liste falls es zu viele Zeilen sind, man an der seite scrollen könnte. Die Checkbox soll die Funktion haben, das DropDown Menu/ die Combobox zu aktivieren oder nicht. Das Ausgewählte ELement aus dem Drop Down müsste ich am Ende abspeichjern können.
Ich könnte mehrere StackPanels nebeneinander anordnen, aber ads wäre keine schöne Lösung oder?
Grüße


Csharp-programmierer - Di 22.11.16 16:09

Wie wäre es, wenn du den TextBoxen in dem Code die Location bzw. Size verrätst. Woher sollen die wissen, wo sie sich anordnen soll?
Und woher soll die Form wissen, dass sie dieses Control annehmen soll, wenn du ihr das nicht sagst?

Probier mal das

C#-Quelltext
1:
this.Controls.Add(meineTextbox);                    


Delete - Di 22.11.16 16:26

- Nachträglich durch die Entwickler-Ecke gelöscht -


csharpuser1 - Di 22.11.16 16:26

wie genau meinst du das?
txt.location gibt es nicht.

Also so hätte ich ein StackPanel mit einem ScrollViewer.


XML-Daten
1:
2:
3:
4:
5:
6:
<ScrollViewer>
        <StackPanel Width="743" Height="373">
         
        </StackPanel>
        
    </ScrollViewer>


Das hilft mir allerdings nicht wirklich weiter, weil wenn ich jetzt mehrere StackPanels nebeneinander anrbinbgen würde, müsste ich alle mit dem selben ScrollViewer scrollen können. Wenn ich ein weiteres StackPanel in den Scrollviewer einfüge, bekomme ich aber eine Fehlermeldung.


wenn ich this.Controls.Add(txt) gibt es auch nicht. Hier mal alles.

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace Test
{
    /// <summary>
    /// Interaktionslogik für PWM.xaml
    /// </summary>
    public partial class Tests : Window
    {
        public Tests()
        {
            InitializeComponent();
            
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            
            TextBlock txt = new TextBlock();
            txt.Name = "txtt";
            txt.TextWrapping = TextWrapping.Wrap;
            txt.Text = "fdsafdsa";
            txt.Margin = new Thickness(303000);
            

            CheckBox box = new CheckBox();
            box.IsChecked = true;
            box.Margin = new Thickness(603000);

            this.Controls.Add(txt);
        }

    }
}


csharpuser1 - Di 22.11.16 16:28

Textbox1 Checkbox1 Combobox1
Textbox2 Checkbox2 Combobox2
..


Delete - Di 22.11.16 16:35

- Nachträglich durch die Entwickler-Ecke gelöscht -


csharpuser1 - Di 22.11.16 16:37

Genau, ein StackPanel war nur eine Idee, aber scheinbar keine sehr gute.


Delete - Di 22.11.16 16:44

- Nachträglich durch die Entwickler-Ecke gelöscht -


csharpuser1 - Di 22.11.16 16:47

Danke, dann bis heute Abend!


Delete - Di 22.11.16 21:05

- Nachträglich durch die Entwickler-Ecke gelöscht -


jfheins - Di 22.11.16 21:52

Zunächst mal: Ich hoffe, ich habe das Problem gerade richtig verstanden. Du möchtest eine Liste von Objekten in Listenform darstellen, sodass man einzelne Sachen editieren kann?
Ich würde das in einer WPF-Anwendung mit einer Listbox und Databinding lösen. Ich habe da gerade mal was vorbereitet :)

Erstmal das Ergebnis:
demo

Um das zu erreichen, erstmal das Viewmodel:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
namespace WpfTest_1
{
    public class Auto : BindableBase
    {
        public int Baujahr { get; set; }
        public bool IsCool { get; set; }
        public string Name { get; set; }
        public Color Farbe { get; set; }
    }

    class MainViewModel : BindableBase
    {
        public ObservableCollection<Auto> Autos { get; set; }

        public MainViewModel()
        {
            Autos = new ObservableCollection<Auto>();
            Autos.Add(new Auto { Baujahr = 1990, Name = "Lucy", Farbe = Colors.Blue, IsCool = false });
            Autos.Add(new Auto { Baujahr = 2015, Name = "Hurracan", Farbe = Colors.DarkOrange, IsCool = true });
        }
    }
}

ich habe das jetzt für dieses Beispiel mal alles in eine Datei und einen Namespace getan.
Das XAML in dem Fenster macht dann die Hauptarbeit:

XML-Daten
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
<Window x:Class="WpfTest_1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfTest_1"
        xmlns:ViewModels="clr-namespace:WpfTest_1"
      xmlns:util="clr-namespace:Common.util"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <util:ColorToBrushConverter x:Key="ColorToBrushConverter" />
        <DataTemplate x:Key="ListItemTemplate" DataType="ViewModels:Auto">
            <Border Width="Auto" BorderThickness="10,1,1,1" CornerRadius="10" Margin="0,3" Background="White" BorderBrush="{Binding Farbe, Converter={StaticResource ColorToBrushConverter}}">
                <Grid Margin="7,0,5,0">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="20" />
                        <RowDefinition Height="20" />
                    </Grid.RowDefinitions>
                    <StackPanel Grid.Row="0" Orientation="Horizontal" >
                        <TextBlock Text="Name  " />
                        <TextBox Text="{Binding Name}" />
                    </StackPanel>
                    <StackPanel Grid.Row="1" Orientation="Horizontal" >
                        <TextBlock Text="Baujahr: " />
                        <TextBlock Text="{Binding Baujahr}" />
                        <TextBlock Text="  Cool? " />
                        <CheckBox IsChecked="{Binding Path=IsCool, Mode=TwoWay}" />
                    </StackPanel>
                </Grid>
            </Border>
        </DataTemplate>
    </Window.Resources>
    <Window.DataContext>
        <ViewModels:MainViewModel />
    </Window.DataContext>
    <Grid>
        <Button x:Name="button" Content="Mehr" HorizontalAlignment="Left" Margin="227,60,0,0" VerticalAlignment="Top" Width="75"/>
        <ListBox x:Name="listBox" HorizontalAlignment="Left" Height="299" Margin="10,10,0,0" VerticalAlignment="Top" Width="200" HorizontalContentAlignment="Stretch"
                 ItemsSource="{Binding Autos}" ItemTemplate="{DynamicResource ListItemTemplate}"/>
    </Grid>
</Window>

Das sieht vielleicht erst mal beängstiend aus, aber XAML ist einfach immer ein bisschen viel Text für das bewirkte. Dafür hat man die guten sprechenden Bezeichner ;-)
BindableBase bekommst du hier: http://www.entwickler-ecke.de/viewtopic.php?p=699585#699585 und der Converter für die Farbe sieht so aus:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
namespace Common.util
{
  [ValueConversion(typeof(Color), typeof(SolidColorBrush))]
  public class ColorToBrushConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
      if (value is Color)
        return new SolidColorBrush((Color)value);
      else
        return new SolidColorBrush();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
      throw new NotImplementedException();
    }
  }
}


Sofern dein ViewModel schon Beispieldaten im Constructor erstellt, bekommst du bei dem ganzen Zeug auch wunderbare Unterstützung im XAML Designer oder Blend.

Das DataTemplate kann man jetzt natürlich noch beliebig verschönern oder besser Layouten. Mir kam es jetzt zunächst auf die Basics an.
Falls du den Schnickschnack mit Selektion und so nicht brauchst, kannst du auch ein ItemsControl in einem ScrollViewer statt der Listbox verwenden.


csharpuser1 - Mi 23.11.16 00:48

Danke, ich schau mir das morgen mal an ob das mein Problem löst!


csharpuser1 - Mi 23.11.16 10:55

Soo, zum testen habe ich das erstmal so übernommen, allerdings bekomme ich die Folgenden Errors:

"Der Name MainViewModel ist im namespace "clr-namespace:WpfTest_1" nicht vorhanden.

Den Selben Fehler für ColorToBrushConverter.

Habe echt alles versucht. Im MainWindow.xaml.cs ist namespace WpfTest_1 und dadrin ist auch die class MainViewModel:BindableBase deswegen versteh ich nicht warum das nicht funktioniert. Hast du da eine Idee? Habe eigentlich nur alles kopiert soweit, die Klasse BindableBase hinzugefügt und eine Klasse Color.cs mit dem Code für die Farben

Ich kann es sogar ausführen, das macht ja eher wenig Sinn. Die Fehler werden trotzdem angezeigt. Das Bild wie du angegeben hast erscheint auch und ich kann die Checkboxen checken oder unchecken :D

Aber in der .xaml Datei bekome ich "ungültiges Markup", wenn ich in der Zeile "<util:ColorToBrushConverter x:Key="ColorToBrushConverter" />" anfange zu schreiben mit <util: wird sogar der ColorToBrushConverter vorgeschlagen, dass ich ihn vrewenden kann, aber wenn ich die Zeile zu ende geschrieben hab kommt wieder der Fehler von oben

Noch eine Frage: Gibt es die Möglichkeit, den SelectionMOde zu deaktiviern?


csharpuser1 - Mi 23.11.16 12:50

Ich habe nun ein weiteres Problem, in der Liste in jeder Zeile habe ich ja eine Combobox. Wie bekomme ich nun den Wert dieser Combobox raus? bzw. wie kann ich auf diesen zugreifen? Ich möchte z.B. wenn meine Checkbox_Checked ist die Combobox deaktivieren.



XML-Daten
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
<Window x:Class="Test_Datei_auswählen.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
        xmlns:local="clr-namespace:Test_Datei_auswählen"    
        xmlns:ViewModels="clr-namespace:Test_Datei_auswählen"
        xmlns:util="clr-namespace:Common.util"   
        mc:Ignorable="d" 
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <util:ColorToBrushConverter x:Key="ColorToBrushConverter" />
        <DataTemplate x:Key="ListItemTemplate" DataType="ViewModels:Timer_Unit">
            <Border Width="Auto" BorderThickness="10,1,1,1" CornerRadius="10" Margin="0,3" Background="White" BorderBrush="{Binding Farbe, Converter={StaticResource ColorToBrushConverter}}">
                <Grid Margin="7,0,5,0">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="20" />
                        <RowDefinition Height="20" />
                    </Grid.RowDefinitions>
                    <StackPanel Grid.Row="0" Orientation="Horizontal" >
                        <TextBlock Text="{Binding Name}" />
                        <CheckBox IsChecked="{Binding Path=IsDefault, Mode=TwoWay}" Checked="CheckBox_Checked" />
                        <CheckBox IsChecked="{Binding Path=IsMaster, Mode=TwoWay}"/>
                        <ComboBox x:Name="ComboBox_Test">
                            <ComboBoxItem Content="test0"/>
                            <ComboBoxItem Content="test1"/>
                            <ComboBoxItem Content="test2"/>
                            <ComboBoxItem Content="test3"/>
                        </ComboBox>
                        <TextBox Width="50"/>
                        <Slider x:Name="mySlider" Width="100" Minimum="0" Maximum="100" IsSnapToTickEnabled="True"/>
                        <TextBox x:Name="myTextBox" TextWrapping="NoWrap" Width="25" Text="{Binding ElementName=mySlider,Path=Value}"/>
                    </StackPanel>
                </Grid>
            </Border>
        </DataTemplate>
    </Window.Resources>
    <Window.DataContext>
        <ViewModels:MainViewModel />
    </Window.DataContext>
    <Grid>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="TestName"/>
            <TextBlock Text=" Default?"/>
            <TextBlock Text=" isMaster?"/>
            <TextBlock Text=" Channel"/>
            <TextBlock Text=" Test_ComboBox"/>
            <TextBlock Text=" Test_TextBox"/>
            <TextBlock Text=" Test_Slider_TextBox"/>
        </StackPanel>
        <ListView x:Name="listBox" HorizontalAlignment="Left" Height="299" Margin="10,20,0,0" VerticalAlignment="Top" Width="400" HorizontalContentAlignment="Stretch"    ItemsSource="{Binding Timer_Units}" ItemTemplate="{DynamicResource ListItemTemplate}"/>
    </Grid>
</Window>


Delete - Mi 23.11.16 13:09

- Nachträglich durch die Entwickler-Ecke gelöscht -


csharpuser1 - Mi 23.11.16 13:13

Ja, das problem ist das funktioniert so leider nicht. Die Combobox ist ja ein Bestandteil des Stackpanels innerhalb der Listbox.


Delete - Mi 23.11.16 13:18

- Nachträglich durch die Entwickler-Ecke gelöscht -


csharpuser1 - Mi 23.11.16 13:25

Ich kann sie leider nirgens erreichen :(.

im xml Code hat sie einen Namen:


XML-Daten
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
<Window x:Class="WpfTest_1.MainWindow"  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"     
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   
        xmlns:local="clr-namespace:WpfTest_1" 
        xmlns:ViewModels="clr-namespace:WpfTest_1"    
        xmlns:util="clr-namespace:Common.util"     
        mc:Ignorable="d"    
        Title="MainWindow" 
        Height="350" Width="525">
    <Window.Resources>
        <util:ColorToBrushConverter x:Key="ColorToBrushConverter" />
        <DataTemplate x:Key="ListItemTemplate" DataType="ViewModels:Auto">
            <Border Width="Auto" BorderThickness="10,1,1,1" CornerRadius="10" Margin="0,3" Background="White" BorderBrush="{Binding Farbe, Converter={StaticResource ColorToBrushConverter}}">
                <Grid Margin="7,0,5,0">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="20" />
                        <RowDefinition Height="20" />
                    </Grid.RowDefinitions>
                    <StackPanel Grid.Row="0" Orientation="Horizontal" x:Name="Panel_Test" >
                        <CheckBox x:Name="CheckBox_Test" IsChecked="{Binding Path=IsCool, Mode=TwoWay}" Checked="CheckBox_Checked"/>
                        <ComboBox x:Name="ComboBox_Test" ItemsSource="{Binding Path=Tests}" IsEnabled="False"/>
                    </StackPanel>
                </Grid>
            </Border>
        </DataTemplate>
    </Window.Resources>
    <Window.DataContext>
        <ViewModels:MainViewModel />
    </Window.DataContext>
    <Grid>
        <ListBox x:Name="listBox" HorizontalAlignment="Left" Height="299" Margin="10,10,0,0" VerticalAlignment="Top" Width="400" HorizontalContentAlignment="Stretch"                 ItemsSource="{Binding Autos}" ItemTemplate="{DynamicResource ListItemTemplate}"/>
    </Grid>
</Window>


Und in meiner Datei versuche ich dann in der Methode is_Checked auf diese zuzugreifen, er kennt sie allerdings nicht


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Common.util;
using System.Collections.ObjectModel;


namespace WpfTest_1 {

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        
        
        private void CheckBox_Checked(object sender, RoutedEventArgs e)
        {
           ComboBox_Test.isEnabled = true;
        }
    }
    public class Auto : BindableBase 
    { 
        public bool IsCool { get; set; }
        public List<string> Tests { get; set; }
    }

    class MainViewModel : BindableBase
    {
        public ObservableCollection<Auto> Autos { get; set; } 
        public MainViewModel()
        { 
            List<string> tests = new List<string>();
            tests.Add("test1");
            tests.Add("test2");
            tests.Add("test3");
            tests.Add("test4");
            Autos = new ObservableCollection<Auto>();
            Autos.Add(new Auto {IsCool = false, Tests=tests }); 
            
        } 
    }
    
}


Fehler: Fehler 85 Der Name 'ComboBox_Test' ist im aktuellen Kontext nicht vorhanden.


Delete - Mi 23.11.16 13:33

- Nachträglich durch die Entwickler-Ecke gelöscht -


csharpuser1 - Mi 23.11.16 13:41

In Zeile 23


Th69 - Mi 23.11.16 13:43

Doch ist sie, aber als Teil des DataTemplate (und darauf kann man nicht direkt von außen zugreifen, weil es sich eben nur um eine Vorlage handelt) - WPF funktioniert ein bißchen anders als WinForms.

@csharpuser1: du solltest dich mal in MVVM einlesen - insbesondere DataBinding.

In deinem Fall also:

XML-Daten
1:
<ComboBox x:Name="ComboBox_Test" ItemsSource="{Binding Path=Tests}" IsEnabled="{Binding Path=IsComboBoxEnabled}"/>                    

und im ViewModel eine entsprechende Eigenschaft bereitstellen, welche dann wiederum auf die an IsChecked gebundene Eigenschaft zugreift:

C#-Quelltext
1:
2:
3:
4:
public bool IsComboBoxEnabled
{
  get { return !IsCheckBoxChecked; }
}

Das direkte Benutzen von UI-Ereignissen wird dabei vermieden.


Delete - Mi 23.11.16 13:47

- Nachträglich durch die Entwickler-Ecke gelöscht -


csharpuser1 - Mi 23.11.16 15:05

Jau das mit dem DataBinding war mein Fehler!