Youtube: WPF layout
Vensters
Een nieuw WPF project heeft standaard een 800x450 Window (met daarin een Grid control):
<Window x:Class="WpfTest.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</Window>
Properties en methodes
| Property | Omschrijving |
|---|---|
Height |
hoogte van het venster |
MaxHeight |
maximumhoogte van het venster (indien herschaalbaar) |
MaxWidth |
maximumbreedte van het venster (indien herschaalbaar) |
MinHeight |
minimumhoogte van het venster (indien herschaalbaar) |
MinWidth |
minimumbreedte van het venster (indien herschaalbaar) |
ResizeMode |
hoe kan het venster herschalen, b.v. niet herschaalbaar: NoResize, CanResize... |
Title |
titel in de titelbalk bovenaan |
Width |
breedte van het venster |
WindowStyle |
stijl van het venster, b.v. vereenvoudigd venster: ToolWindow |
| Property | Omschrijving |
|---|---|
Open() |
open een venster |
Close() |
sluit een venster |
Voorbeelden
Minimum en maximum afmetingen
Beperk de afmetingen met Width, Height, MinWidth, MinHeight, MaxWidth en MaxHeight:
<Window x:Class="WpfTest.MainWindow"
...
Title="Test Window" Height="250" Width="400" MinHeight="250" MinWidth="400" MaxHeight="450" MaxWidth="800">
<Grid>
</Grid>
</Window>
Niet herschaalbaar maken
Niet herschaalbaar venster met ResizeMode="NoResize":
<Window x:Class="WpfTest.MainWindow"
...
Title="Test Window" Height="250" Width="400" ResizeMode="NoResize">
<Grid>
</Grid>
</Window>
Vereenvoudigd
Maak een vereenvoudigd venster zonder icoon linksboven of minimize knoppen rechtsboven met WindowStyle="ToolWindow":
<Window x:Class="WpfTest.MainWindow"
...
Title="Test Window" Height="250" Width="400" Height="250" Width="400" WindowStyle="ToolWindow">
<Grid>
</Grid>
</Window>
Nieuw openen
Een nieuw venster openen kan met de Show() methode. Nemen we als voorbeeld een PopupWindow; voeg het toe aan je project via rechtermuisknop add, Window...:

Voeg dan b.v. een Button toe aan MainWindow, waaromee je de PopupWindow opent:
<Window x:Class="WpfTest.MainWindow" ...>
<Grid>
<Button Padding="10,5" Content="open popup" Click="BtnOpenPopup_Click" .../>
</Grid>
</Window>
De event handler in code-behind:
private void BtnOpenPopup_Click(object sender, RoutedEventArgs e)
{
new MyPopupWindow().Show();
}
Voor de popup window is een niet herschaalbaar vereenvoudigd venster ideaal:
<Window x:Class="WpfTest.PopupWindow" Height="150" Width="300" WindowStyle="ToolWindow" ResizeMode="NoResize" ...>
<Grid>
</Grid>
</Window>
Je kan bij het openen gegevens als b.v. gebruikersnaam meegeven van MainWindow naar PopupWindow door de constructor van die laatste uit te breiden met een parameter.
In PopupWindow.xaml.cs:
public MyPopupWindow(string name) // breid uit met "name" parameter
{
InitializeComponent();
lblWelcome.Content = $"hello {name}";
}
In MainWindow.xaml.cs:
private void BtnOpenPopup_Click(object sender, RoutedEventArgs e) {
new MyPopupWindow("Rogier").Show(); // geef waarde voor "name" mee
}
Sluiten
Maak b.v. een button om het venster te sluiten
<Button Content="sluiten" Click="BtnClose_Click" .../>
De event handler in code-behind:
private void BtnClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
Positionering van controls
Margin en Padding
Slepen we in designer een Button control op de grid, en voegen we wat Padding toe:
<Grid>
<Button Content="Button" Margin="35,48,10,20" Padding="10,5,10,5" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Grid>
- de
Paddingbepaalt de ruimte binnen de control, dus tussen tekst en rand - de
Marginbepaalt de ruimte buiten de control, hier links en boven tot de<Grid>
VerticalAlignment en HorizontalAlignment
De attributen VerticalAlignment en HorizontalAlignment bepalen hoe de control gepositioneerd wordt t.o.v. de parent. Als afstanden worden de marges genomen. Stel je ze b.v. in op Right resp. Bottom, dan worden de rechter- en onderwaarden van Margin genomen:
<Grid>
<Button Content="Button" Margin="35,48,10,20" Padding="10,5" HorizontalAlignment="Right" VerticalAlignment="Bottom" />
</Grid>
De standaardwaarden voor VerticalAlignment en HorizontalAlignment zijn Stretch, dus als je het weglaat wordt de control uitgerokken, b.v. VerticalAlignment weglaten rekt verticaal uit:
<Grid>
<Button Content="Button" Margin="35,48,10,20" Padding="10,5" HorizontalAlignment="Left" />
</Grid>
VerticalAlignment en HorizontalAlignment voor elke control!
VerticalContentAlignment en HorizontalContentAlignment
De attributen VerticalContentAlignment en HorizontalContentAlignment bepalen de alignering van de content van de control. De XAML voor twee tekstvakken, verticaal gecentreerd en horizontaal links resp. rechts:
<TextBox Text="tekst links..." HorizontalContentAlignment="Left" VerticalContentAlignment="Center" ... />
<TextBox Text="tekst rechts..." HorizontalContentAlignment="Right" VerticalContentAlignment="Center"... />
WPF panels
overzicht
| Control | Omschrijving | Voorbeeld |
|---|---|---|
| Canvas | positionering met coördinaten, al dan niet overlappend | ![]() |
| DockPanel | control waarbinnen panels aan de vier zijden gedocked ("gekleefd") kunnen worden | ![]() |
| Grid |
rijen en kolommen waarbinnen controls geplaatst worden goede controle over breedtes/hoogtes rijen en kolommen goede controle over positionering items binnen de cellen |
![]() |
| StackPanel |
eenvoudige stapeling per rij of kolom zonder wrapping alle items worden tegen elkaar gestapeld, dus "spreiden" is niet mogelijk |
![]() |
| WrapPanel | als StackPanel, maar met wrapping |
![]() |
Canvas
Canvas wordt hoofdzakelijk gebruikt om (vooral grafische) elementen een vaste plaats te geven. De positie bepaal je met de properties Canvas.Left, Canvas.Top enz...; voor de stapelvolgorde gebruik je Canvas.ZIndex. Een voorbeeld:
<Canvas Name="cvCanvas">
<Ellipse Fill="Gainsboro" Canvas.Left="25" Canvas.Top="25" Width="200" Height="200" Panel.ZIndex="1" />
<Rectangle Fill="LightBlue" Canvas.Left="87" Canvas.Top="40" Width="50" Height="50" Panel.ZIndex="3" />
<Rectangle Fill="LightCoral" Canvas.Left="50" Canvas.Top="50" Width="50" Height="50" Panel.ZIndex="4" />
<Rectangle Fill="LightCyan" Canvas.Left="74" Canvas.Top="75" Width="100" Height="100" Panel.ZIndex="2" />
</Canvas>
DockPanel
Docking is een term gebruikt voor waar en hoe child elements “plakken” in een venster dat van grootte kan veranderen. Een DockPanel wordt typisch gebruikt voor de globale layout van een Windows venster. Een voorbeeld:
<DockPanel LastChildFill="True"> <!-- LastChildFill: laatste element vult resterende middenruimte -->
<TextBox DockPanel.Dock="Top" Text="Top" />
<TextBox DockPanel.Dock="Bottom" Text="Bottom" />
<TextBox DockPanel.Dock="Left" Text="Left1" />
<TextBox DockPanel.Dock="Left" Text="Left2" />
<TextBox DockPanel.Dock="Right" Text="Right" />
<TextBox Text="Center" AcceptsReturn="True" TextWrapping="Wrap" />
</DockPanel>
Grid
Bij Grid kan je een grid van rijen en kolommen definiëren met Grid.ColumnDefintions en Grid.RowDefinitions. Daarna plaats je elementen in de juiste rij/kolom met Grid.Row en Grid.Column.
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label>Name:</Label>
<TextBox Grid.Column="1" Margin="0,5,0,10" />
<Label Grid.Row="1">E-mail:</Label>
<TextBox Grid.Row="1" Grid.Column="1" Margin="0,5,0,10" />
<Label Grid.Row="2">Comment:</Label>
<TextBox Grid.Row="2" Grid.Column="1" Margin="0,5,0,10" AcceptsReturn="True" />
</Grid>
De Width waarden van kolommen worden als volgt verwerkt (hetzelfde principe geldt voor Height waarden van rijen):
- teken eerst kolommen met vaste breedtes
- teken dan kolommen met breedte
Auto, waarbij ze niet meer ruimte innemen dan hun content nodig heeft - verdeel de resterende waarden proportioneel; als er b.v. twee kolommen
2*en3*zijn, krijgt de eerste kolom 2/5 en de tweede 3/5
| Voorbeeld | Betekenis | Anders gezegd... |
|---|---|---|
Width="80" |
exact 80 px breed | vaste waarde |
Width="Auto" |
neem niet meer ruimte dan nodig | fit content |
Width="3*" |
neemt een gewicht 3 | resterende ruimte |
Width="*" (standaardwaarde) |
is hetzelfde als Width="1*" |
resterende ruimte |
StackPanel
Bij StackPanel stapel je controls in één rij of kolom:
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">1</TextBlock>
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">2</TextBlock>
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">3</TextBlock>
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">4</TextBlock>
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">5</TextBlock>
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">6</TextBlock>
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">...</TextBlock>
</StackPanel>
Om verticaal te stapelen gebruik je Orientation="Vertical":
<StackPanel Orientation="Vertical">
...
</StackPanel>
WrapPanel
Bij WrapPanel stapel je elementen in meerdere rijen of kolommen:
<WrapPanel>
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">1</TextBlock>
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">2</TextBlock>
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">3</TextBlock>
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">4</TextBlock>
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">5</TextBlock>
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">6</TextBlock>
<TextBlock FontSize="48" Width="100" Height="70" TextAlignment="Center" Background="LightGray" Margin="10,10,10,10">...</TextBlock>
</WrapPanel>
Je kan ook stapelen van rechts naar links met FlowDirection="RightToLeft", maar dat zijn de enige aligneermogelijkheden. Items centraal plaatsen of ver uit elkaar zetten kan bijvoorbeeld niet, zoals met CSS FlexBox wel kan. Voor complexere layouts gebruik je best altijd Grid.
Om verticaal te stapelen gebruik je Orientation="Vertical":
<WrapPanel Orientation="Vertical">
...
</WrapPanel>
UI componenten
overzicht
| Control | Omschrijving | Voorbeeld |
|---|---|---|
| Menu | een klassiek Windows menu | ![]() |
| ScrollViewer | voeg scrollbars toe aan panels en andere controls | ![]() |
| StatusBar | een statusbar onderaan het venster | ![]() |
| Tabcontrol | een control met tabs | ![]() |
Menu
Een menu bouw je met Menu en MenuItem controls, typisch in combinatie met een DockPanel:
<DockPanel LastChildFill="True">
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Header="_Open..." />
<MenuItem Header="_Save" />
<MenuItem Header="Save _As..." />
<Separator />
<MenuItem Header="E_xit" Click="ExitItem_Click" />
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="_About" />
</MenuItem>
</Menu>
<Grid></Grid>
</DockPanel>
→ de underscore in de XAML duidt aan welke letter als shortcut gebruikt wordt
Voorbeeld van een event handler voor het exit menu item:
private void ExitItem_Click(object sender, RoutedEventArgs e)
{
// 0 wilt zeggen dat er niets is fout gelopen
Environment.Exit(0);
}
ScrollViewer
Sommige controls als ListBox hebben van zichzelf scrollbars, sommige controls als TextBox of panels niet. Ze scrollbars geven doe je — misschien wat vreemd — niet met een property, maar met een ScrollViewer control:
- wrap de control in een
<ScrollViewer> ... </ScrollViewer> - verhuis layout properties als
Height,HorizontalAlignment,Margin,VerticalAlignment,Widthenz... van de control naar deScrollViewer
Een TextBox zonder scrollbars:
<TextBox
x:Name="txt1"
AcceptsReturn="True"
Height="150"
HorizontalAlignment="Left"
Margin="35,39,0,0"
Padding="20,10,20,30"
Text="Bacon ipsum dolor amet prosciutto tail fatback, pancetta sausage meatball swine rump salami pig alcatra ..."
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="200"
/>
Dezelfde TextBox met scrollbars:
<ScrollViewer
Height="150"
HorizontalAlignment="Left"
Margin="35,39,0,0"
VerticalAlignment="Top"
Width="200">
<TextBox
x:Name="txt1"
AcceptsReturn="True"
Padding="20,10,20,30"
Text="Bacon ipsum dolor amet prosciutto tail fatback, pancetta sausage meatball swine rump salami pig alcatra ..."
TextWrapping="Wrap"
/>
</ScrollViewer>
StatusBar
Een StatusBar gebruik je typisch in combinatie met een DockPanel, onderaan gedocked:
<DockPanel LastChildFill="True">
...
<StatusBar DockPanel.Dock="Bottom">
<StatusBarItem Padding="10,5">
<TextBlock Text="all ready" FontSize="10" />
</StatusBarItem>
</StatusBar>
<Grid></Grid>
</DockPanel>
TabControl
Een TabControl voorbeeld:
<TabControl SelectedIndex="2" >
<TabItem Header="Tab 1" Margin="0" Padding="10,5">
<TextBox Padding="10">tab 1 content here...</TextBox>
</TabItem>
<TabItem Header="Tab 2" Margin="0" Padding="10,5">
<TextBox Padding="10">tab 2 content here...</TextBox>
</TabItem>
<TabItem Header="Tab 3" Margin="0" Padding="10,5">
<TextBox Padding="10">tab 3 content here...</TextBox>
</TabItem>
<TabItem Header="Tab 4" Margin="0" Padding="10,5">
<TextBox Padding="10">tab 4 content here...</TextBox>
</TabItem>
</TabControl>
Page/Frame
In WPF kan je werken met meerdere “schermen” zonder meerdere vensters te openen.
Dat doe je met een Frame in je MainWindow, waarin je verschillende
Page-objecten laadt.
- Window = het hoofdvenster van je applicatie
- Frame = een container die pagina’s kan tonen
- Page = een scherm binnen een Frame
Typische toepassingen: adminpanelen, wizards, ...
Download de volledige code van de uitleg hieronder: SlnDemoFrame.zip
Frame control in MainWindow
Plaats in MainWindow een Frame; dat is de “placeholder”
waarin pagina’s geladen worden. We voegen meteen navigatiebuttons en click event handlers toe:
<Grid Background="#FFF3EBEB">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Frame x:Name="frmMain" Grid.Column="2" NavigationUIVisibility="Hidden" Margin="10" Background="White"/>
<StackPanel Margin="20,10">
<Button x:Name="btnHome" Content="🏠 Home" Padding="5" Margin="0,0,0,20" Click="BtnHome_Click" />
<Button x:Name="btnProducts" Content="🛒 Producten" Padding="5" Margin="0,0,0,20" Click="BtnProducts_Click" />
<Button x:Name="btnAbout" Content="💡Over" Padding="5" Margin="0,0,0,20" Click="BtnAbout_Click" />
</StackPanel>
</Grid>
Met NavigationUIVisibility="Hidden" verberg je de standaard navigatiebalk, die er wat ouderwets uitziet.
Pages toevoegen aan project
Voeg elke pagina toe via Add, Page (WPF)..., bijvoorbeeld in een nieuwe submap Pages:
Doen we dit met HomePage, ProductsPage en AboutPage:
Page heeft een eigen .xaml en code-behindOm de code georganiseerd te houden, gebruik je best een submap Pages en geef je elke Page de suffix “Page”, dus b.v. HomePage, ProductsPage en AboutPage.
XAML code van ProductPage (de andere pagina's zijn gelijkaardig):
<Page x:Class="WpfDemoFram.Pages.ProductsPage"
...
Title="ProductsPage">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBlock FontSize="28" FontWeight="Bold" Margin="0,0,0,20">Productpagina</TextBlock>
<TextBlock Grid.Row="1">
Productpagina content hier...
</TextBlock>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="btnPrev" Padding="10,5" Margin="20,10,0,10" Click="btnPrev_Click"><< vorige</Button>
<Button x:Name="btnNext" Padding="10,5" Margin="20,10,0,10" Click="btnNext_Click">volgende >></Button>
</StackPanel>
</Grid>
</Page>
Navigatie naar een Page
Vanuit MainWindow gebruik Content property van Frame:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
frmMain.Content = new HomePage();
}
private void BtnHome_Click(object sender, RoutedEventArgs e)
{
frmMain.Content = new HomePage();
}
private void BtnProducts_Click(object sender, RoutedEventArgs e)
{
frmMain.Content = new ProductsPage();
}
private void BtnAbout_Click(object sender, RoutedEventArgs e)
{
frmMain.Content = new AboutPage();
}
}
Vanuit een Page, bv ProductsPage, gebruik navigationService.Navigate(...):
public partial class ProductsPage : Page
{
public ProductsPage()
{
InitializeComponent();
}
private void btnPrev_Click(object sender, RoutedEventArgs e)
{
this.NavigationService.Navigate(new HomePage());
}
private void btnNext_Click(object sender, RoutedEventArgs e)
{
this.NavigationService.Navigate(new AboutPage());
}
}
Gegevens delen tussen Pages
Er zijn veel manieren om gegevens te delen tussen Pages en Windows, maar de eenvoudigste manier is door de constructor van Page uit te breiden met een parameter. Aanpassing van de code-behind van MainWindow:
public partial class MainWindow : Window
{
protected const string Username = "Stan"; // dit wordt gedeeld met de pages
public MainWindow()
{
InitializeComponent();
frmMain.Content = new HomePage(Username); // geef mee met de constructor van de page
}
...
}
Aanpassing van de code-behind van b.v. de HomePage:
public partial class HomePage : Page
{
private string userName; // lokale hulpvariabele
public HomePage(string uName) // breid constructor uit met een parameter
{
InitializeComponent();
this.userName = uName; // kopieer lokaal
txtContent.Text = $"Hallo {this.userName}! Dit is de home pagina.";
}
...
}
Eindresultaat
Animatie van het eindresultaat:
Download de volledige code: SlnDemoFrame.zip

