Controls
Übersicht
Controls dienen als Elemente in XAML-Dateien. XAML-Dateien sind die
Dateien, die WPF verwendet. Hier ist eine Liste der wichtigsten und
gebrächlichsten WPF-Controls. Die Properties, die normalerweise gebindet
werden (mehr dazu bei Binding) sind unterstrichen.
Control |
spezifische Properties |
Beschreibung |
Label |
Content |
erzeugt eine Beschriftung für z.B.: TextBox |
TextBox |
Text,
TextAlignment, TextWrapping, TextTrimming, AcceptsReturn
|
erzeugt eine Box, dessen Text man bearbeiten kann |
TextBlock |
Text,
TextAlignment, TextWrapping, TextTrimming
|
erzeugt eine Box, dessen Text nicht bearbeitet werden kann
|
Slider |
Minimum, Maximum,
Value,
TickFrequency, TickPlacement
|
erzeugt einen Schieberegler |
RadioButton |
Content,
IsChecked,
GroupName
|
erzeugt erzeugt einen runden Button mit Text, wo man einen
auswählen kann
|
CheckBox |
Content, IsChecked
|
erzeugt ein Kästchen mit Text, welches man anhäckelchen kann
|
Button |
Content, Command
|
erzeugt einen anklickbare Box, die eine Aktion durchführt
|
DatePicker |
Text |
erzeugt eine TextBox mit auswählbarem Datum |
DataGrid |
ItemsSource,
SelectedItem
|
erzeugt eine Art Tabelle bzw. Liste, die bearbeitet und
sortiert werden kann
|
ListView |
ItemsSource,
SelectedItem,
DisplayMemberPath
|
erzeugt eine Liste |
ListBox |
erzeugt eine Liste |
ComboBox |
erzeugt eine DropBox, wo man Items auswählen kann |
Canvas |
|
erzeugt einen Bereich, wo Controls frei platziert werden
können
|
StackPanel |
Orientation |
erzeugt einen Bereich, wo standardmäßig untereinander
Controls hineinkommen
|
WrapPanel |
Orientation |
erzeugt einen Bereich, wo standardmäßig nebeneinander
Controls hineinkommen
|
DockPanel |
|
erzeugt einen Bereich, wo Controls relativ zu den vier
Rändern platziert werden können
|
Grid |
|
erzeugt ein Raster, welches frei konfiguriert werden kann
|
Label
Ein Label dient als Beschriftung für eine TextBox oder einen TextBlock,
einen DatePicker oder irgendein anderes Control.
<Label Content="Passwort: " Width="100"/>
TextBox
In eine TextBox kann der User etwas hineinschreiben, was dann
verarbeitet werden kann.
<TextBox Text="" Width="200"/>
TextBlock
Ein TextBlock ist ein Feld mit Text, den den User nicht bearbeiten,
sondern nur lesen kann.
<TextBox Text="" Width="200"/>
Slider
Ein Slider ist ein Schieberegler, den der User hin und her schieben
kann, um bestimmte Einstellungen zu verändern. Meistens benötigt man
einen Converter für einen Slider, damit die Position der Sliderauswahl
auch in einen Wert umgewandelt werden kann.
<Slider Value="50" Minimum="0" Maximum="100" TickFrequency="1"
TickPlacement="Top/Bottom"/>
Ein RadioButton kommt selten alleine. Mehrere RadioButtons ermöglich
eine Auswahl von mehreren Angeboten, sodass der User zum Beispiel sein
Lieblingsessen auswählen kann.
<RadioButton Content="Italienisch" IsChecked="False"
GroupName="FavouriteFood"/>
<RadioButton Content="Österreichisch" IsChecked="True"
GroupName="FavouriteFood"/>
<RadioButton Content="Chinesisch" IsChecked="False"
GroupName="FavouriteFood"/>
<RadioButton Content="Griechisch" IsChecked="False"
GroupName="FavouriteFood"/>
CheckBox
Ein CheckBox kommt selten alleine. Mehrere CheckBoxes ermöglich eine
Mehrfachauswahl von mehreren möglichen Wahr/Falsch-Aussagen, sodass der
User zum Beispiel Besitztümer ankreuzen kann.
<CheckBox Content="Computer" IsChecked="True"/>
<CheckBox Content="Laptop" IsChecked="False"/>
<CheckBox Content="Handy" IsChecked="True"/>
<CheckBox Content="SmartWatch" IsChecked="True"/>
Ein Button ist ein klickbares Element, das mithilfe von ControlBinding
eine bestimmte Aktion durchführen kann.
<Button Content="Add" Command="{Binding AddCmd}"/>
DatePicker
Mithilfe eines DatePicker kann man eine TextBox mit einem
Datumsauswählfeld erstellen. Wenn man daraufklickt, öffnet sich ein
kleines Fenster mit einem Kalender.
<DatePicker SelectedDate="18/03/2022"/>
Tabellen
DataGrid
ListView
ListBox
ComboBox
Panel
Mithilfe von Panels kann man andere XAML-Controls den Wünschen
entsprechend anordnen.
StackPanel
Das StackPanel ordnet jedes Kindelement vertikal an.
<StackPanel Orientation="Vertical">
</StackPanel>
Die Orientation bestimmt, ob die Elemente vertikal oder horizontal
angeordnet werden sollen.
WrapPanel
Das WrapPanel ordnet jedes Kindelement horizontal an.
<WrapPanel Orientation="Horizontal">
</WrapPanel>
Die Orientation bestimmt, ob die Elemente vertikal oder horizontal
angeordnet werden sollen.
DockPanel
Das DockPanel erlaubt das kleben an den vier Seiten.
<DockPanel DockPanel.Dock="Left">
</DockPanel>
Das DockPanel.Dock="" Property bestimmt, wo die Elemente kleben
sollen.
Grid
Das Grid erzeigt eine Tabelle, die sich der Größe des Fensters
anpasst.
<Grid Margin="15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="3*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
</Grid>
ColumnDefinition bestimmt, wie die Spalten aufgeteilt sein sollen.
In diesem Fall ist die zweite Spalte halb so breit, wie die erste
und dritte Spalte.
RowDefinition bestimmt, wie die Reihen augeteilt sein sollen. In
diesem Fall ist die erste Reihe 1,5x so hoch, wie die zweite Reihe.
Sowohl Reihen als auch Spalten ändern sich somit relativ zur
Gesamtbreite des Grid.
Canvas
Der Canvas ist eine Art Zeichenfläche, wo man Elemente beliebig hin-
und herziehen kann.
<Canvas>
<Label Canvas.Left="15" Canvas.Top="20">MyLabel</Label>
</Canvas>
Mit Canvas.Left / .Right / .Top / .Bottom kann man jedes Element
frei ausrichten.
Properties
Die meisten Controls haben eigene spezifische Properties, zum Beispiel hat
ein Slider ein Value-Property, welches eine TextBox nicht hat. Es gibt
allerdings auch einige Properties, die jedes Control in WPF hat.
Property |
Beschreibung |
Name |
definiert den Namen des Controls |
Width |
bestimmt die Breite des Elements / Controls |
Height |
bestimmt die Höhe des Elements / Controls |
Margin |
bestimmt den Abstand außerhalb des Borders (zwischen Border und
Umgebung)
|
Padding |
bestimmt den Abstand innerhalb des Borders (zwischen Text und
Border)
|
Background |
bestimmt die Farbe, den Gradianten oder das Bild des
Hintergrunds bzw., welches im Hintergrund angezeigt werden soll
|
Foreground |
bestimmt die Farbe des Vordergrundes |
MVVM - Modell
Das Model-View-ViewModel- Modell hat in WPF sehr viel sinn, weil man dadurch
DataBinding, ControlBinding und Converter programmieren kann. Die View ist
hierbei das, was der User sehen, anklicken und bearbeiten kann. Das Model
sind Klassen, die zum Beispiel Werte (Properties) speichern. Zum Beispiel
eine Person. Das ViewModel ist der Teil, der die View mit dem Model
verbindet.
Wenn man in WPF etwas bindet, sprechen Programmierer automatisch von diesem
Modell. Man kann sich das so vorstellen, dass das ViewModel direkt hinter
der View klebt und alle Eingaben, Klicks usw. dem Model weiterleitet oder
selber managed.
DataBinding
In der Tabelle oben, sind all diejenigen Properties unterstrichen, die
gebindet werden können bzw. normalerweise gebindet werden (man kann auch
andere Properties von Controls binden...). Als Beispiel nehme ich einfach
Text als das Property, das gebindet werden soll.
Name ist hier eine Variable aus der VM-Klasse.
<TextBox Text="{Binding Name}"/>
Damit man diesen Name auch verwenden kann, benötigt man einen Verweis auf
die VM-Klasse.
<Window.DataContext>
<local:PersonVM/>
</Window.DataContext>
In C# benötigt man etwas mehr Code. Diese Klasse ermöglicht zum Beispiel
jeder erbenden Klasse das Nutzen der OnPropertyChanged(); - Methode. Diese
Methode benachrichtigt die View, falls sich in der ViewModel-Klasse ein Wert
verändert hat, sodass sich diese updated.
public class PropertyChangedClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyname = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
Die Klasse Person sieht so aus (mit drei Properties und ToString()- und
ToCSV()-Methoden):
public class Person
{
public string Name { get; set; }
public string Password { get; set; }
public bool IsCool { get; set; }
public override string ToString()
{
string iscool = (IsCool) ? "ist cool" : "ist nicht cool";
return String.Format($"{Name} hat {Password} als Passwort und {iscool}!");
}
public string ToCSV()
{
return String.Format($"{Name};{Password};{IsCool}");
}
}
Die VM-Klasse sieht dann so aus. Hier sieht man, dass beim Setten eines
Properties die OnPropertyChanged()-Methode aufgerufen wird, sodass alle
Listener benachrichtigt werden, dass sich dieses Property geändert hat.
Da stellt man sich vielleicht nun die Frage: Woher weiß die Methode denn,
welches Property sich verändert hat, wenn gar kein Parameter übergeben wird?
Einfach Antwort: Darum kümmert sich das [CallerMemberName] aus der
PropertyChangedClass.
public class PersonVM : PropertyChangedClass
{
private Person _person;
public PersonVM()
{
_person = new Person();
}
public string Name
{
get { return _person.Name; }
set { _person.Name = value; OnPropertyChanged(); }
}
public string Password
{
get { return _person.Password; }
set { _person.Password = value; OnPropertyChanged(); }
}
public int IsCool
{
get { return _person.IsCool; }
set { _person.IsCool = value; OnPropertyChanged(); }
}
}
ObservableCollection
Wenn man eine Liste mit Elementen, wie zum Beispiel DataGrid haben will, die
sich mit TextBoxen und anderen Controls gemeinsam updated, benötigt man eine
ObservableCollection.
public class PersonsVM : PropertyChangedClass
{
public PersonsVM()
{
AllPersons = new ObservableCollection<PersonVM>();
AllPersons.Add(new PersonVM() { Name = "Clemens", Password = "asdfjklö" });
AllPersons.Add(new PersonVM() { Name = "Felix", Password = "ölkjfdsa" });
AllPersons.Add(new PersonVM() { Name = "Anton", Password = "asdfölkj"});
AllPersons.Add(new PersonVM() { Name = "Lorenz", Password = "1234" });
SelectedPerson = AllPersons.First();
}
ObservableCollection<PersonVM> _allpersons;
public ObservableCollection<PersonVM> AllPersons
{
get { return _allpersons; }
set { _allpersons = value; }
}
private PersonVM _selectedperson;
public PersonVM SelectedPerson
{
get { return _selectedperson; }
set { _selectedperson = value; OnPropertyChanged(); }
}
}
Dies wird dann folgendermaßen mit der XAML-Datei verknüpft:
<Window.DataContext>
<local:PersonsVM/>
</Window.DataContext>
<DataGrid ItemsSource="{Binding AllPersons}" SelectedItem="{Binding
SelectedPerson}" Width="300"/>
ControlBinding
Will man die Aktion eines Buttons kontrollieren, benötigt man
ControlBinding.
public class PersonsVM : PropertyChangedClass
{
public AddCmd AddCmd { get; set; }
public LoginCmd LoginCmd { get; set; }
public PersonsVM()
{
AllPersons = new ObservableCollection<PersonVM>();
AllPersons.Add(new PersonVM() { Name = "Clemens", Password = "asdfjklö" });
AllPersons.Add(new PersonVM() { Name = "Felix", Password = "ölkjfdsa" });
AllPersons.Add(new PersonVM() { Name = "Anton", Password = "asdfölkj"});
AllPersons.Add(new PersonVM() { Name = "Lorenz", Password = "1234" });
SelectedPerson = AllPersons.First();
AddCmd = new AddCmd(this);
LoginCmd = new LoginCmd(this);
}
ObservableCollection<PersonVM> _allpersons;
public ObservableCollection<PersonVM> AllPersons
{
get { return _allpersons; }
set { _allpersons = value; }
}
private PersonVM _selectedperson;
public PersonVM SelectedPerson
{
get { return _selectedperson; }
set { _selectedperson = value; OnPropertyChanged(); }
}
}
public class LoginCmd : ICommand
{
private PersonsVM _personsVM;
public LoginCmd(PersonsVM PersonsVM)
{
_personsVM = PersonsVM;
}
public bool CanExecute(object parameter)
{
return (_personsVM.SelectedPerson.Name != null &&
_personsVM.SelectedPerson.Password != null &&
_personsVM.SelectedPerson.Name != "" &&
_personsVM.SelectedPerson.Password != "");
}
public void Execute(object parameter)
{
System.Windows.MessageBox.Show(_personsVM.SelectedPerson.Name +
" is cool", "Coolnesslevel");
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
public class AddCmd : ICommand
{
private PersonsVM _personsVM;
public AddCmd(PersonsVM PersonsVM)
{
_personsVM = PersonsVM;
}
public bool CanExecute(object parameter)
{
return (_personsVM.SelectedPerson.Name != null &&
_personsVM.SelectedPerson.Password != null &&
_personsVM.SelectedPerson.Name != "" &&
_personsVM.SelectedPerson.Password != "");
}
public void Execute(object parameter)
{
_personsVM.AllPersons.Add(new PersonVM()
{
Name = _personsVM.SelectedPerson.Name,
Password = _personsVM.SelectedPerson.Password
});
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
Converter
Wie vorhin schon erwähnt benötigt man einen Converter meistens für Slider,
muss aber nicht sein. Will man zum Beispiel einen Integer (1-5, wie
Schulnoten) in den dazupassenden String ("Sehr gut" - "Nicht genügend")
convertieren, kann man das so lösen:
public class IntToMarkConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
switch (System.Convert.ToInt32(value))
{
case 1: return "Sehr gut";
case 2: return "Gut";
case 3: return "Befriedigend";
case 4: return "Genügend";
default: return "Nicht genügend";
}
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
// macht keinen Sinn, lol
throw new NotImplementedException();
}
}
<Window.Resources>
<local:IntToMarkConverter x:Key="IntMark"/>
</Window.Resources>
<TextBox Text="{Binding Note}"/>
<TextBlock Text="{Binding Note, Converter={StaticResource IntMark}}"/>