Somethings are simple... Somethings are complex... Whenever I feels something may help somebody I post it here...
Wednesday, December 2, 2009
Tuesday, July 14, 2009
Exercise for Part -1
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
StackPanel DockPanel Grid
controls in order to get the job done.
If you're not sure how to do it. I shall give the XAML here below..
<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">
<Menu Width="Auto" Height="20" Background="#FFA9D1F4"
DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="Save"/>
<Separator/>
<MenuItem Header="Exit"/>
MenuItem>
<MenuItem Header="Help">
<MenuItem Header="About"/>
MenuItem>
Menu>
<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>
<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>
<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>
There are a couple of tricks here, namely the following:
- The
Grid
for the right hand content area, must be the last child declared in order for it to take up the remaining space that the parentDockPanel
wants to fill, due to theLastChildFill="True"
- The
StackPanel
used for the status bar, must be before any other child that is declared as beingDockPanel.Dock="Left"
orDockPanel.Dock="Right"
. As if there was another element before the status barStackPanel
, the status barStackPanel
would not be able to span the entire width available, as this space would have been stolen by any child that wasDockPanel.Dock="Left"
orDockPanel.Dock="Right"
. Try it, you'll see what I mean. Simply move the status bar XAML further down in the XAML file, say to the end.
Performance Considerations
As some panels can be bound to items (this will be discussed further in the DataBinding
article) there may be occasions where the number of children elements displayed in a panel is quite large. For example, if a StackPanel
contains a ListBox
that is bound to a large database query. This would be lots of items, OK in this case it's the ListBox
that will have lots of children. However, internally, the ListBox
control uses a Vertical StackPanel
to render its items by default. Mmmm, that's not so great.
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 VirtualizingStackPanel.IsVirtualizing
on a ListBox
, which means that the ListBox
controls internal StackPanel
to render its items, will now be virtualized. But what the heck is a vitualizingStackPanel
?
When a panel is virtualized, it means that only the visible elements are created. The rest aren't displayed. For example, creating a listbox
displaying images bound to a database holding 100,000 rows. This would take a long time for the listbox
to load. If you use a virtualize panel, then only the visible images will get created in the UI. When you scroll down, the currently visible items will get destroyed, and the new visible items will get loaded onto the UI. There is only one panel that supports virtualization and it is the VirtualizingStackPanel
. If you need to create any new virtualized panels, you will have to write your own.
I suppose, I hope by now you have the basics of layout in WPF.
Friday, July 10, 2009
WPF - Beginners tutorial PART - 1
* Layout (this article)
* XAML vs Code / Markup Extensions And Resources (next article)
* Commands and Events
* Dependency Properties
* DataBinding
* Styles/Templates
Layout
Layout 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 Layout
Layout is a fundamental building block used when writing any WPF. 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.
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:
* Canvas
* StackPanel
* WrapPanel
* DockPanel
* Grid
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 Margin
One thing that you simply must know, is how important the Margin property is. By using the Margin, we are able to specify how much space the current control (the one we are specifying the Margin property for), wants to have around it. WPF provides a ValueConverter that accepts a string of the format 5,5,5,5, but what does this mean. Well it's basically saying that we want a Margin of 5 pixels all around the control that is declaring this property. The Margin string, is stating Left, Top, Right, Bottom, and is one of 3 overloaded constructors used by the strangely named Thickness class that is used in the case where we are trying to set the Margin in code behind.
Canvas
The Canvas control, is one of the easier layout controls to use. It is a simple X/Y position container. Where each of the contained (children) controls must specify the following four properties in order to be positioned within the parent Canvas control:
* Canvas.Left
* Canvas.Right
* Canvas.Top
* Canvas.Bottom
With these four properties in place, the control will be positioned using these values within the parent Canvas control. These properties probably look a little bit odd, where we have a Canvas.Left for example, well they are a bit odd actually.
What else do we need to know about a Canvas control and its children. Well actually that's almost it, the only other thing to consider is that if the Canvas control is a simple X/Y position container, what's to stop two child controls, overlapping, and which child control should be on top. Well that's all taken care of by another dependency/attached property of the Canvas control. This is called the Canvas.ZIndex property, and this indicates which control should be on top. Basically the higher the Canvas.ZIndex value is, the more on top the control that declares this dependency/attached property will be. If no Canvas.ZIndex is declared for any of the children controls, the Canvas.ZIndex will be set to the order in which the children are added to the Canvas control.
Let's see an example of this, shall we? The following picture shows a Canvas control with two children, one on top of the other. This is taken from the file called CanvasDEMO.xaml in the attached demo project.
In XAML it will be like..
<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);
And that's about all there is to basic Canvas layout.
I'm not good with VB. If you want to have it in VB, you can try some convertors.
StackPanel
The StackPanel
control is also very easy to use. It simply stacks its contents, vertically or horizontally, using a single property, called Orientation
.
The following picture shows a WrapPanel
control with 10 children. This is taken from the file called WrapPanelDEMO.xaml.
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);
}
And that's about all there is to basic WrapPanel
layout.
DockPanel
The DockPanel
control is one of the most useful (IMHO) layout controls. It is the one that we would probably use as the base layout control that any new Window
uses. Basically with a DockPanel
control (or 2), we can achieve the sort of layout that has been the main layout for most applications we have ever seen. We can basically get a menu docked to the top, then a left/right main content area, and a status strip at the bottom. This is all thanks to a couple of properties on the DockPanel
control. Basically we can control the docking of any of our child controls that is within a parent DockPanel
control by the use of the following dependency/attached property.
DockPanel.Dock
This property may be set to Left/Right/Top or Bottom. There is one further property exposed as a normal CLR property on the DockPanel
control which is called LastChildFill
which when set to true
will make the last child control that was added to the DockPanel
control, fill the remaining available space. This will override any DockPanel.Dock
property that the child control may have already set.
Let's see an example of this, shall we? The following picture shows a DockPanel
control with two children, one docked to the top, and the other docked to fill the remaining available area. This is taken from the file called DockPanelDEMO.xaml in the attached demo project.
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);
And that's about all there is to basic DockPanel
layout.Grid
The Grid
control, is by far, the most sophisticated WPF layout control there is (at present). It is sort of like an HTML table control, where you can specify rows and columns, and have cells that span multiple rows, or cells that span multiple columns. There is also a strange syntax which may be used for the Width/Height of Columns and Rows, which is known as the Star "*" notation, which is exposed through the use of the GridLength
class. Think of this as being like a percentage of what's left divider. For example I could have some markup such as:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
Where I have declared three Grid ColumnDefinition
controls, where the first ColumnDefinition
gets a fixed width of 40 pixels, and the remaining space is divided between the last two ColumnDefinition
controls, where the last one gets twice as much as the second last one. This is the same principle for RowDefinition
.
In order for child controls of a Grid
control to tell the WPF layout system which cell they belong to, we simply use the following dependency/attached properties, which use a 0 based index.
Grid.Column
Grid.Row
And to specify how many rows or columns a cell should occupy, we simply use the following dependency/attached properties, which starts at 1
.
Grid.ColumnSpan
Grid.RowSpan
By clever usage of a Grid
control, you should almost be able to mimic any of the other layout controls. I'll leave that as an exercise for the reader.
Grid
control, shall we? The following picture shows a Grid
control with 3 Columns and 1 Row, where there are two children. The first child occupies Column 1, and the second child occupies Columns 2-3 as its Grid.ColumnSpan
is set to 2.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);
As I say, the Grid
control is quite sophisticated so I urge you to explore
this one further. You can do all sorts of things with the Grid
such as have
GridSplitter
controls for resizing Columns/Rows, and you can set up shared
sizes across multiple grids, this is known as SizeGroup
. So please explore the
Grid
control further.
Tuesday, April 21, 2009
Free Cool ListBox - Customized for alternate item colors and text wrap properties.
So I designed one cool listbox that with the above requirements satisfied. I have given the code below.
My List box looks like the below image..
To acheive this, First you have to add one ListBox control and one Textbox with multiline property enabled underneath it. I have named my textbox as txtMessage and ListBox as ListBox1.
The below code shows how to add the contents from the text box on carriage return press ('Enter' button press)...
1: private void txtMessage_KeyPress(object sender, KeyPressEventArgs e)
2: {
3: if (e.KeyChar == 13)
4: {
5: ListBox1.Items.Add(txtMessage.Text.Trim().ToString());
6: txtMessage.Text = "";
7: }
8: }
Now, folllow the instructions below...
1.Select the ListBox1 from your design view and make the "DrawMode" property as "OwnerDrawVariable"
2. Generate the event for DrawItem and MeasureItem of the ListBox.
1: /// <summary>
2: /// DrawItem event triggers when the list box is visible in the form
3: /// </summary>
4: /// <param name="sender"></param>
5: /// <param name="e"></param>
6: private void ListBox1_DrawItem(object sender, DrawItemEventArgs e)
7: {
8: if (ListBox1.Items.Count > 0)
9: {
10: // Draw the background of the ListBox control for each item.
11: // Create a new Brush and initialize to a Black colored brush
12: // by default. And inintialise other properties to set the font
13: //bg color font size etc.. as below.
14:
15: FontFamily family = FontFamily.GenericSerif;
16: float size = 8F;
17: Font myFont = new Font(family, size, FontStyle.Regular);
18: Color highlightercolor = Color.White;
19: Brush fontcolor = Brushes.Black;
20:
21: //Method call to draw the back ground if the list box [Metadata]
22: e.DrawBackground();
23:
24: //Inintialize a rectangle to contain each item
25: Rectangle rectangle = new Rectangle(2, e.Bounds.Top + 2,
26: e.Bounds.Height, e.Bounds.Height - 4);
27:
28: //Select alternate items
29: int type = 0;
30: type = e.Index % 2;
31:
32: // Determine the color of the font, bg color, and brush to
33: //draw each item based on the index of the item to draw.
34:
35: switch (type)
36: {
37:
38: case 0:
39: family = FontFamily.GenericSansSerif;
40: size = 8;
41: myFont = new Font(family, size, FontStyle.Regular);
42: highlightercolor = Color.Gainsboro;
43: fontcolor = Brushes.Black;
44: break;
45:
46: case 1:
47: family = FontFamily.GenericSansSerif;
48: size = 8;
49: myFont = new Font(family, size, FontStyle.Regular);
50: highlightercolor = Color.WhiteSmoke;
51: fontcolor = Brushes.Black;
52: break;
53:
54: default:
55: break;
56: }
57:
58: // Draw the current item text based on the current
59: // Font and the custom brush settings.
60: e.Graphics.FillRectangle(new SolidBrush(highlightercolor), e.Bounds);
61:
62: // Draw each string in the Listbox,
63: e.Graphics.DrawString(((ListBox)sender).Items[e.Index].ToString(),
64: myFont, fontcolor, new RectangleF(e.Bounds.X + rectangle.Width,
65: e.Bounds.Y, e.Bounds.Width, e.Bounds.Height));
66:
67: // If the ListBox has focus, draw a focus rectangle
68: // around the selected item.
69: // [Metadata]
70: e.DrawFocusRectangle();
71: }
72:
73: }
The above method takes care on the alternate back ground color, font properties etc..
1: /// <summary>
2: /// Event triggers when the ListBox1 is visible in the form
3: /// </summary>
4: /// <param name="sender"></param>
5: /// <param name="e"></param>
6: private void ListBox1_MeasureItem(object sender, MeasureItemEventArgs e)
7: {
8: Graphics gr = e.Graphics;
9:
10: //We will get the size of the string which we are about to draw,
11: //so that we could set the ItemHeight and ItemWidth property
12: SizeF size = gr.MeasureString(((ListBox)sender).Items[e.Index].ToString(),
13: ((ListBox)sender).Font,
14: ((ListBox)sender).Width - 3 - SystemInformation.VerticalScrollBarWidth);
15:
16: //Set the height and width to the item
17: e.ItemHeight = Convert.ToInt16(size.Height) + 5;
18: e.ItemWidth = Convert.ToInt16(size.Width) + 3;
19: }
This above method declares the wrap of the content of the ListBox1.
You can alter the features as you like, hopefully all the code were explained neatly.
Monday, April 20, 2009
Marquee in Windows forms
I have tried some marquee controls available ready in google search and code project, but they are not thead safe in my experience i faced problems in using them in my project as it is.
Here I have given a simple code to create your own marquee that is thread safe and reasonably serves your requirement with out causing any problem.
1: System.Text.StringBuilder sb;
2: /// <summary>
3: /// Method called during form load. initiates MArquee items
4: /// </summary>
5: void StartMarquee()
6: {
7: //This method sets all the input for your marquee
8: sb = new System.Text.StringBuilder(Marquee.marqueeProperties.Text + " -- ");
9: //Enable the timer control
10: timerMarquee.Enabled = true;
11: //You can simply alter the speed of the marquee
12: timerMarquee.Interval = Marquee.marqueeProperties.RotationSpeed;
13: //Change the marquee text color
14: txtMarqueeDisplay.ForeColor = Marquee.marqueeProperties.TextColor;
15: //Change the bach ground color here
16: txtMarqueeDisplay.BackColor = Marquee.marqueeProperties.TextBgColor;
17: }
The above has to be called from your windows form load where you have to have your text box control where the marquee is going to be displayed. Here my text box is named as "txtMarqueeDisplay".
1: /// <summary>
2: /// That Runs Marquee
3: /// </summary>
4: /// <param name="sender"></param>
5: /// <param name="e"></param>
6: private void timerMarquee_Tick(object sender, EventArgs e)
7: {
8: //This is the timer's tick event
9: char ch = sb[0];
10: sb.Remove(0, 1);
11: sb.Insert(sb.Length, ch);
12: //Change the 100 below to fit your requirement
13: if (txtMarqueeDisplay.Text.Length > 100)
14: txtMarqueeDisplay.Clear();
15: txtMarqueeDisplay.AppendText(sb.ToString());
16: }
The above method the event from the timer control which is availbale from .NET 2.0
the timer is used to make sure the marque is thread safe!
Hope you got the marquee in a very simple way!!