|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Services
Chapters
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionSome of you may have come across some of my other articles that I have published here on The Code Project. And if you have, you may have noticed that I have been writing a few on WPF/LINQ/Silverlight/WCF of late. Generally I try and have fun when I write my articles, and I try to make them interesting for myself, now sometimes that means that they end up being quite complex, which I like. However I remember not so long ago I had published what I deemed to be an intermediate level WPF article, which due to The Code Project upgrade, got classified as beginner. It was never intended to be a beginner's article, so it got some stick from some folk, for being too complicated, and not beginner-esque. Now I don't mind getting stick, but this time it was due to an upgrade problem, not something I had done. It did however get me thinking about what one of the forum entries stated: "Where are all the beginner's articles on WPF?" So I thought about this, and did a search here on The Code Project, and found that generally, WPF writers on The Code Project (me included) are pretty much showing off. Now, don't get me wrong this is a good thing, I can't tell you how much I've learnt from some of the more showy-offy type articles. But I also thought that there wasn't that much here for beginners with not much time on their hands... so I thought, yeah perhaps a beginner's series in WPF would be nice. I know Josh Smith did a bang up job with his series A Guided Tour of WPF, which I personally recommend you all read if you haven't already. And there are always books, but I've got 3 books on WPF and they all have differing information. So I thought, Ok maybe this beginner's series may not be so bad after all. So I decided to give it a crack. I don't know how many articles I'll end up doing for this series, but it will probably be something like:
Like I say, I think Josh did an excellent job covering a lot of this, but why then did I buy 3 WPF books, was one not enough? Sometimes it's just nice to have a different point of view. For example, Josh is a WPF master (A condor if you like... or some equally majestic bird... huge black out in the sun, wing spanned bird), and I am a fledging chick just learning to fly (with tiny little wings) in the WPF world. So I figure it may do some people some good, to hear things from a slightly different angle (AKA crazy fledging chick). I will of course be covering some stuff that you could lift straight out of a book, but I hope there will also be some new stuff that I have picked up whilst taking my own WPF journey. As this series will be quite a commitment, all I ask is that if you find this article useful, please leave a comment at the bottom and a vote, so I know whether its worth doing the rest of the series. You know there are 100s of other articles in my reserve (I have a big list), where I could go mad, but I feel that this may be a useful series (basically tell me in the article's forum that you want more of this), and if that is the case I will be more than happy to commit myself to making this series as enjoyable as I can. So without further ado, I guess we should start looking into what we need to get to grips with in this article. As it is the first article in this (proposed, if people want more of this) series, it really has to start with layout. LayoutLayout is one of the most important parts of any WPF project, the following sub sections will introduce you to the new layout options at your disposal. The Importance of LayoutThe way I see it WPF can be used in one of 2 ways, it can be used in a browser (partial trust) - these are known as XBap or a full blown application, basically an executable (*.exe). Whatever format is chosen is not important for this article's content, as layout is equally important to both formats. What I mean is that layout is a fundamental building block used when writing any WPF, no matter whether it's an XBap or an application. Using the layout controls in WPF allows developers/designers to create very complex arrangements of pages/controls. Without layout, we probably couldn't achieve anything, apart from a mess. So if you are looking for a mess, just quit reading right here. If however you want to know how to use the new layout options in WPF, read on. What's New in WPFIf you are a Web developer, probably most (but not all) of what this article is going to cover will be new to you. If you are a WinForms developer you have undoubtedly come across Within WPF (at least the current version), Microsoft has provided a few Layout controls for developers/designers to use, most of which will be new ground to most of you, I imagine. These new layout controls will be the main focus of this article. You are of course free to author your own layout controls, if one of the pre-built controls doesn't suit your needs. We will see more on this later. For the purpose of this article, we will be looking at the following:
Please note that I will only be covering the basics of these controls, there are many more resources available for being clever with these controls, should you wish to research that. I however, consider the more advanced usages of these controls to be outside the scope of this article. Remember it's a beginner's series, so I want to keep it at a beginner's level. A Brief Detour into the Importance of MarginOne thing that you simply must know, is how important the CanvasThe
With these four properties in place, the control will be positioned using these values within the parent What else do we need to know about a Let's see an example of this, shall we? The following picture shows a
So how does this look in code? Well in XAML, it is as follows: <Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WPF_Tour_Beginners_Layout.CanvasDEMO"
x:Name="Window"
Title="CanvasDEMO"
Width="640" Height="480">
<Canvas Margin="0,0,0,0" Background="White">
<Rectangle Fill="Blue"
Stroke="Blue"
Width="145"
Height="126"
Canvas.Left="124" Canvas.Top="122"/>
<Ellipse Fill="Green"
Stroke="Green"
Width="121" Height="100"
Panel.ZIndex="1"
Canvas.Left="195" Canvas.Top="191"/>
</Canvas>
</Window>
And in C#, this would be as follows: Canvas canv = new Canvas();
//add the Canvas as sole child of Window
this.Content = canv;
canv.Margin = new Thickness(0, 0, 0, 0);
canv.Background = new SolidColorBrush(Colors.White);
//The Rectangle
Rectangle r = new Rectangle();
r.Fill = new SolidColorBrush(Colors.Blue);
r.Stroke = new SolidColorBrush(Colors.Blue);
r.Width = 145;
r.Height = 126;
r.SetValue(Canvas.LeftProperty, (double)124);
r.SetValue(Canvas.TopProperty, (double)122);
canv.Children.Add(r);
//The Ellipse
Ellipse el = new Ellipse();
el.Fill = new SolidColorBrush(Colors.Green);
el.Stroke = new SolidColorBrush(Colors.Green);
el.Width = 121;
el.Height = 100;
el.SetValue(Canvas.ZIndexProperty, 1);
el.SetValue(Canvas.LeftProperty, (double)195);
el.SetValue(Canvas.TopProperty, (double)191);
canv.Children.Add(el);
Whilst in VB.NET, this would be: Dim canv As New Canvas()
'add the Canvas as sole child of Window
Me.Content = canv
canv.Margin = New Thickness(0, 0, 0, 0)
canv.Background = New SolidColorBrush(Colors.White)
'The Rectangle
Dim r As New Rectangle()
r.Fill = New SolidColorBrush(Colors.Blue)
r.Stroke = New SolidColorBrush(Colors.Blue)
r.Width = 145
r.Height = 126
r.SetValue(Canvas.LeftProperty, CDbl(124))
r.SetValue(Canvas.TopProperty, CDbl(122))
canv.Children.Add(r)
'The Ellipse
Dim el As New Ellipse()
el.Fill = New SolidColorBrush(Colors.Green)
el.Stroke = New SolidColorBrush(Colors.Green)
el.Width = 121
el.Height = 100
el.SetValue(Canvas.ZIndexProperty, 1)
el.SetValue(Canvas.LeftProperty, CDbl(195))
el.SetValue(Canvas.TopProperty, CDbl(191))
canv.Children.Add(el)
And that's about all there is to basic StackPanelThe Let's see an example of this, shall we? The following picture shows a
So how does this look in code? Well in XAML, it is as follows: <Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WPF_Tour_Beginners_Layout.StackPanelDEMO"
x:Name="Window"
Title="StackPanelDEMO"
WindowStartupLocation="CenterScreen"
Width="640" Height="480">
<StackPanel Margin="0,0,0,0" Background="White" Orientation="Vertical">
<Button Content="Im Top of Stack"/>
<Button Content="Im Bottom Of Stack"/>
</StackPanel>
</Window>
And in C#, this would be as follows: StackPanel sp = new StackPanel();
//add the StackPanel as sole child of Window
this.Content = sp;
sp.Margin = new Thickness(0, 0, 0, 0);
sp.Background = new SolidColorBrush(Colors.White);
sp.Orientation = Orientation.Vertical;
//Button1
Button b1 = new Button();
b1.Content = "Im Top of Stack";
sp.Children.Add(b1);
//Button2
Button b2 = new Button();
b2.Content = "Im Bottom of Stack";
sp.Children.Add(b2);
Whilst in VB.NET, this would be: Dim sp As New StackPanel()
'add the StackPanel as sole child of Window
Me.Content = sp
sp.Margin = New Thickness(0, 0, 0, 0)
sp.Background = New SolidColorBrush(Colors.White)
sp.Orientation = Orientation.Vertical
'Button1
Dim b1 As New Button()
b1.Content = "Im Top of Stack"
sp.Children.Add(b1)
'Button2
Dim b2 As New Button()
b2.Content = "Im Bottom of Stack"
sp.Children.Add(b2)
And that's about all there is to basic WrapPanelThe Let's see an example of this, shall we? The following picture shows a
So how does this look in code? Well in XAML, it is as follows: <Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WPF_Tour_Beginners_Layout.WrapPanelDEMO"
x:Name="Window"
Title="WrapPanelDEMO"
WindowStartupLocation="CenterScreen"
Width="640" Height="480">
<WrapPanel Margin="0,0,0,0" Background="White">
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
</WrapPanel>
</Window>
And in C#, this would be as follows: WrapPanel wp = new WrapPanel();
//add the WrapPanel as sole child of Window
this.Content = wp;
wp.Margin = new Thickness(0, 0, 0, 0);
wp.Background = new SolidColorBrush(Colors.White);
//Add Rectangles
Rectangle r;
for (int i = 0; i <= 10; i++)
{
r = new Rectangle();
r.Fill = new SolidColorBrush(Colors.Blue);
r.Margin = new Thickness(10, 10, 10, 10);
r.Width = 60;
r.Height = 60;
wp.Children.Add(r);
}
Whilst in VB.NET, this would be: Dim wp As New WrapPanel()
'add the WrapPanel as sole child of Window
Me.Content = wp
wp.Margin = New Thickness(0, 0, 0, 0)
wp.Background = New SolidColorBrush(Colors.White)
'Add Rectangles
Dim r As Rectangle
For i As Integer = 0 To 10
r = New Rectangle()
r.Fill = New SolidColorBrush(Colors.Blue)
r.Margin = New Thickness(10, 10, 10, 10)
r.Width = 60
r.Height = 60
wp.Children.Add(r)
Next
And that's about all there is to basic DockPanelThe
This property may be set to Left/Right/Top or Bottom. There is one further property exposed as a normal CLR property on the Let's see an example of this, shall we? The following picture shows a
So how does this look in code? Well in XAML, it is as follows: <Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WPF_Tour_Beginners_Layout.DockPanelDEMO"
x:Name="Window"
Title="DockPanelDEMO"
WindowStartupLocation="CenterScreen"
Width="640" Height="480">
<DockPanel Width="Auto" Height="Auto" LastChildFill="True">
<Rectangle Fill="CornflowerBlue" Stroke="CornflowerBlue"
Height="20" DockPanel.Dock="Top"/>
<Rectangle Fill="Orange" Stroke="Orange" />
</DockPanel>
</Window>
And in C#, this would be as follows: DockPanel dp = new DockPanel();
dp.LastChildFill = true;
//this is the same as Width="Auto" in XAML, as long as its not applied
to a GridColumn Width/Height /GridRow Width/Height which has special classes
dp.Width = Double.NaN;
dp.Height = Double.NaN;
//add the WrapPanel as sole child of Window
this.Content = dp;
//Add Rectangles
Rectangle rTop = new Rectangle();
rTop.Fill = new SolidColorBrush(Colors.CornflowerBlue);
rTop.Stroke = new SolidColorBrush(Colors.CornflowerBlue);
rTop.Height = 20;
dp.Children.Add(rTop);
rTop.SetValue(DockPanel.DockProperty,Dock.Top);
Rectangle rFill = new Rectangle();
rFill.Fill = new SolidColorBrush(Colors.Orange);
rFill.Stroke = new SolidColorBrush(Colors.Orange);
dp.Children.Add(rFill);
Whilst in VB.NET, this would be: Dim dp As New DockPanel()
dp.LastChildFill = True
dp.Width = [Double].NaN
'this is the same as Width="Auto" in XAML
dp.Height = [Double].NaN
'this is the same as Height="Auto" in XAML
'add the DockPanel as sole child of Window
Me.Content = dp
'Add Rectangles
Dim rTop As New Rectangle()
rTop.Fill = New SolidColorBrush(Colors.CornflowerBlue)
rTop.Stroke = New SolidColorBrush(Colors.CornflowerBlue)
rTop.Height = 20
dp.Children.Add(rTop)
rTop.SetValue(DockPanel.DockProperty, Dock.Top)
Dim rFill As New Rectangle()
rFill.Fill = New SolidColorBrush(Colors.Orange)
rFill.Stroke = New SolidColorBrush(Colors.Orange)
dp.Children.Add(rFill)
And that's about all there is to basic GridThe <Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
Where I have declared three In order for child controls of a
And to specify how many rows or columns a cell should occupy, we simply use the following dependency/attached properties, which starts at
By clever usage of a Let's see an example of the
So how does this look in code? Well in XAML, it is as follows: <Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WPF_Tour_Beginners_Layout.GridDEMO"
x:Name="Window"
Title="GridDEMO"
WindowStartupLocation="CenterScreen"
Width="640" Height="480">
<Grid Width="Auto" Height="Auto" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Rectangle Fill="Aqua" Grid.Column="0" Grid.Row="0"/>
<Rectangle Fill="Plum" Grid.Column="1" Grid.ColumnSpan="2"/>
</Grid>
</Window>
And in C#, this would be as follows: Grid grid = new Grid();
grid.Width = Double.NaN; //this is the same as Width="Auto" in XAML
grid.Height = Double.NaN; //this is the same as Height="Auto" in XAML
//add the Grid as sole child of Window
this.Content = grid;
//col1
ColumnDefinition cd1 = new ColumnDefinition();
cd1.Width = new GridLength(40);
grid.ColumnDefinitions.Add(cd1);
//col2
ColumnDefinition cd2 = new ColumnDefinition();
cd2.Width = new GridLength(1, GridUnitType.Star);
grid.ColumnDefinitions.Add(cd2);
//col3
ColumnDefinition cd3 = new ColumnDefinition();
cd3.Width = new GridLength(2, GridUnitType.Star);
grid.ColumnDefinitions.Add(cd3);
//Now add the cells to the grid
Rectangle r1c1 = new Rectangle();
r1c1.Fill = new SolidColorBrush(Colors.Aqua);
r1c1.SetValue(Grid.ColumnProperty, 0);
r1c1.SetValue(Grid.RowProperty, 0);
grid.Children.Add(r1c1);
Rectangle r1c23 = new Rectangle();
r1c23.Fill = new SolidColorBrush(Colors.Plum);
r1c23.SetValue(Grid.ColumnProperty, 1);
r1c23.SetValue(Grid.ColumnSpanProperty, 2);
grid.Children.Add(r1c23);
Whilst in VB.NET, this would be: Dim grid As New Grid()
grid.Width = [Double].NaN
'this is the same as Width="Auto" in XAML
grid.Height = [Double].NaN
'this is the same as Height="Auto" in XAML
'add the Grid as sole child of Window
Me.Content = grid
'col1
Dim cd1 As New ColumnDefinition()
cd1.Width = New GridLength(40)
grid.ColumnDefinitions.Add(cd1)
'col2
Dim cd2 As New ColumnDefinition()
cd2.Width = New GridLength(1, GridUnitType.Star)
grid.ColumnDefinitions.Add(cd2)
'col3
Dim cd3 As New ColumnDefinition()
cd3.Width = New GridLength(2, GridUnitType.Star)
grid.ColumnDefinitions.Add(cd3)
'Now add the cells to the grid
Dim r1c1 As New Rectangle()
r1c1.Fill = New SolidColorBrush(Colors.Aqua)
r1c1.SetValue(Grid.ColumnProperty, 0)
r1c1.SetValue(Grid.RowProperty, 0)
grid.Children.Add(r1c1)
Dim r1c23 As New Rectangle()
r1c23.Fill = New SolidColorBrush(Colors.Plum)
r1c23.SetValue(Grid.ColumnProperty, 1)
r1c23.SetValue(Grid.ColumnSpanProperty, 2)
grid.Children.Add(r1c23)
As I say, the Putting It All TogetherSo now we can put all this good knowledge together and we could create something as beautiful as this:
No... only joking, I think the best thing to do here is, let's say I have some hypothetical layout that I want to achieve. Let's say one of the common layouts, favoured for years where we have a menu bar at the top, followed by a main content area, and a status bar area at the bottom. Let's see a mock up (designed as a simple WinForm) of what we are aiming for:
I think I have given you all the tools you need to carry out designing this sort of layout in WPF. Do you want a hint, I think you will need to use the In case you are wondering (lost f£*k it), here is how I did it (XAML only this time, sorry folks): <Window x:Class="WPF_Tour_Beginners_Layout.PuttingItAllTogether"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="CenterScreen"
Title="PuttingItAllTogether" Width="640" Height="480" >
<DockPanel Width="Auto" Height="Auto" LastChildFill="True">
<!--Top Menu Area-->
<Menu Width="Auto" Height="20" Background="#FFA9D1F4"
DockPanel.Dock="Top">
<!-- File Menu -->
<MenuItem Header="File">
<MenuItem Header="Save"/>
<Separator/>
<MenuItem Header="Exit"/>
</MenuItem>
<!-- About Menu -->
<MenuItem Header="Help">
<MenuItem Header="About"/>
</MenuItem>
</Menu>
<!--Bottom Status Bar area, declared before middle section,
as I want it to fill entire bottom of Window,
which it wouldn't if there was a Left docked panel before it -->
<StackPanel Width="Auto" Height="31" Background="#FFCAC5C5"
Orientation="Horizontal" DockPanel.Dock="Bottom">
<Label Width="155" Height="23" Content="Status Bar Message...."
FontFamily="Arial" FontSize="10"/>
</StackPanel>
<!--Left Main Content area-->
<StackPanel Width="136" Height="Auto" Background="White">
<Button Margin="5,5,5,5" Width="Auto" Height="26" Content="button1"/>
<Button Width="126" Height="26" Content="button2" Margin="5,5,5,5"/>
<Button Width="126" Height="26" Content="button3" Margin="5,5,5,5"/>
</StackPanel>
<!--Right Main Content area, NOTE HOW this Grid is the last child
so takes all the remaining room -->
<Grid Width="Auto" Height="Auto" Background="#FFCC9393">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Aqua" Margin="10,10,10,10" Grid.Row="0" Grid.Column="0"/>
<Rectangle Fill="Aqua" Margin="10,10,10,10" Grid.Row="0" Grid.Column="1"/>
<Rectangle Fill="Aqua" Margin="10,10,10,10" Grid.Row="1" Grid.Column="0"/>
<Rectangle Fill="Aqua" Margin="10,10,10,10" Grid.Row="1" Grid.Column="1"/>
</Grid>
</DockPanel>
</Window>
Which resulted in the following
There are a couple of tricks here, namely the following:
Performance ConsiderationsAs some panels can be bound to items (this will be discussed further in the However all is not that bad, WPF has one further trick up its sleeve to aid in this situation. We can use the dependency/attached property When a panel is virtualized, it means that only the visible elements are created. The rest aren't displayed. For example, creating a Custom LayoutsIt's also possible to create your own custom panel which performs all types of custom layouts. Now I could try and be clever here, and come up with some new spin on how to do this, but Paul Tallet, has already done a great job here, with a section in his great FishEye panel CodeProject article. So this next little section's words are thanks to Paul Tallet.
There is a whole bunch of custom panel links over on Rob Relyea's blog here. We're DonePhew, that was a lot to go through I suppose, but I hope by now you have the basics of layout in WPF. But... Like I Said If You Want More...As I stated earlier, this article series is a big commitment for me, so if you want more, please vote, and leave a message so I know that it's worth me doing a whole series like this, at this level. History
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||