In this part 2 of learn WPF we are going to talk about Controls and see how these controls are different in WPF and what capabilities it provides you build a good WPF application. Also we would see how we can handle the user input using the events and commands.
Let’s get started. You have already seen a Button in WPF. WPF also have a CheckBox and RadioButton. But as you could see in the image below
ButtonBase is the base class for the any button in WPF and ToggleButton is the base class for the CheckBox and RadioButton. Now its really interesting to know that the controls in WPF are divided into two namespaces : System.Windows.Controls (Contains Button, CheckBox, RadioButton) and System.Windows.Controls.Primitive (ButtonBase, ToggleButton, RepeatButton). Primitive controls are not designed in a way to be used individually but as a part of other controls. But you would see that the RepeatButton (A button that repeatedly raises its click event when it is pressed continiously) is part of the primitive namespace but still can be used individually. The reason because it is in the Primitive namespace is because in WPF it used as a part of other controls such as scrollbar.
RadioButtons are designed to be used in groups, so that only one of the RadioButton in the group is selected at a time. So when you assign the same group name for multiple radio buttons then only one of the radio buttons can be selected at a time. The grouping also works if you place all the radio buttons in one parent but if you assign a group name after that as well then then the groupname will override the parent grouping.
As you might have seen in the Part 2 of Learn WPF that we could place just anything inside a button, like text, arbitrary content, data, etc. if type the below code into a WPF application you would see that you have placed multiple objects inside button.
This Content Model is available for any control in WPF. It not only supports the built in controls but also supports the custom controls that are made.
When you are adding just the text as content to the button you add an underscore before the letter which you want as the accelerated control for the button click.
Controls don’t have to be limited to a single piece of content, some controls have 2 content property. The base class for these controls is HeadedContentControl which has the both Header and Content property.
Expander and GroupBox derive from this Base Class. This allows them to have anything in the body and they can have anything in the header as well. To set anything as header we need to use the xaml property syntax which looks something like below:
This means I can place anything in header and anything in content. You can type the following code in a WPF application and see that header and content and have the same model but at the two different places.
WPF has 3 controls for Text Input.
- TextBox – This takes only input text as string and does not support any formatting.
- RichTextBox – Allows formatting of text. Although you might be surprised to know that RichTextBox does not use the Rich Text Format internally. It uses WPF’s FlowDocument internally for formatting. This is actually good because you will find easier to work with WPF text model then to work with native RTF. Now you might think then why is it called as RichTextBox, the reason is that Windows call anything that supports rich text as RichTextBox. Also it supports RTF because whenever you copy any rich text from the clipboard to the RichTextBox it will treat it as rich text and vice versa as well i.e. when you copy text from RIchTextBox to Clipboard the formatting is not lost.
Both TextBox and RichTextBox derive from the same BaseClass which is TextBoxBase and hence support some common clipboard operations like copy, cut, paste.
- PasswordBox – This textbox does not share the same base class as the other two. This is designed for editing password and the reason why it does not use the same base calss is that it does not want to offer some clipboard operations like copy, cut as these cause security risks.
The TextBox and RichTextBox both support spell check functionality. All you need to do is to set the SpellCheck.IsEnabled equals True and you would get spell check in the textbox. When you type a wrongly spelled work into the textbox you would see a red underlined in the misspelled word and also when you would right click on the misspelled word it will give you options to correct the spelling.
When xaml comes across the dot in SpellCheck.IsEnabled it treats the IsEnabled property as an attached property (An attached property is one which defined by a different class than the target object). So here the property is being used by the Textbox but the Property is defined in the class SpellCheck. This might seem a little odd to you at first but if you think about it, it’s an excellent idea to add properties to existing classes without modifying them.
The way a xaml compiler interprets this attached property is as below.
So the SpellCheck class uses these access methods like get and set to access the attached property and that’s the exact same way the mobile .NET property really do.
Now most the times we have a label in front of our textboxes and we might want to focus to the textbox using a keypress. Now if use the following code then we could achieve what we intend to.
I would say the main job of the label is focus targeting. If you want to just display some text then I would recommend a TextBlock. That’s what a Label uses internally. If you just want to display some text not use focus handling then using a label will be overkill.
Slider, ProgressBar and ScrollBar share a common base class named RangeBase. These share common properties that they define values within a range. Slider and Scrollbar provide you an option to drag and change the selected value where as in PrgressBar we cannot do so.
There are many items in WPF that can display multiple items like: ListBox, ListView, TreeView, ComboBox and they derive from a common base class which is ItemsControl. This class provides the Items property to hold the child items and also common sets of events along with the databinding support. As you see in the image below that except the TreeView all the other Items Controls derive from class selector which provide the provide the property to handle a selected item but you can still select items in the treeview as well. Actually the selector should have been named as an IndexedSeletor as it provides the indexed selection property. And as the Indexed selection property only makes sense for linear lists hence so those are the classes which are derived from the selector class.
You will also notice that the ListView is derived from a ListBox and the only difference between a ListBox and ListView is that ListBox does not have support or Column Headers. All the Items Controls have a Content Model which is similar to Button that we have already seen before and supports any number of child items.
If you put the code shown in the image in a WPF app then you would be able to realize how flexible the Items Control are.
As any items Control is very flexible this negates the need of a DataGrid which is not present in WPF.
Some other Items Controls are Menu, Tab Controls (Derive from the Selector class) and Toolbar. So we can generate the content of these controls as well from data binding.
Items Controls and Content Models
So now we have seen many Content Controls and Items Controls. As could see in the table below there are four content model classes from which the different controls derive
- The Button and Label derive from Content Control class that support only a single content property. This is also the base class for Window as Window supports only one content.
- Headerd Content Control which derives from the Content Control and is inherited by Exapander, GroupBox and TabItem. So you have a have a header along with content.
- Items Control which provides the Multi Content Model. Each item in the list gets its own instance of it content model.
- Headered Items Control which derives from the ItemsControl but adds an header just like the Headered Content Control. So this is used by Items Contols that have children and also use a header like TreViewItems, Menu Items.
It’s also possible add new Content Model.
The way the Items Control provide Content Model to its children is that each child gets wrapped in its own nested control. So for every item we add to a a ListBox the ListBox adds it to the ListBox ItemsControl. Similarly ListView Wraps its children into lisview items control and so on. So these wrappers are called as Items Container and each ItemsControl Define its own Items Container. We can also define custom ItemsContainers. So we add the following code to a WPF UserControl we could see header, container,etc
There are 3 types of Events in WPF
- Tunneling Events – These events start from the Root Element and move down the child elements towards the Target Element. These represent the Preview Events.
- Bubbling Events – These events start from the Target Element and route up towards the Root element. These represent the main events
- Direct Events – These events do not route at all but just directs towards the target element and that’s all. These used at place where tunneling or bubbling does not make sense like mouseEnter and is only raised for target element.
Lets see an example. Add the code shown below in a WPF application
As you would see we have used MouseEnter and MouseDown Events handler. When we write the code like above and add handler using += then the code and xaml and very tightly coupled. The code makes assumptions about the xaml but the xaml does not have any assumptions about the code. But we add method names into the xaml we have xaml also making assumptions and there methods and handler are treated as attributes. Also when you used Visual Studio or Blend they add function names in the xaml and hence we wish to rely on these design tool s then we should utilize the xaml attributes approach.
If have a look at the output widow while running the application you would see that the mouse enter events are direct events and the mouse down events are bubbling up.
If we remove the background for border and the stackpanel then the white area which you see is the window and the same will be reflected by the Mouse Enter and Mouse Down events. Also if you change the background to transparent for stackpanel and border we would have the same look as no background but will behave the way there is a background.
Now if want the bubbling to stop then we can add e.Handled = “true” to the event handler where we want it to stop bubbling further.
Let’s add some more code to the cs file.
So when you click on a button the background will turn green. But we have not used event bubbling. So lets change the code a little bit to use event bubbling, so lets add the handler to the stackpanel to handle the button click.
When we run this we will get an invalid cast exception, so we need to change the code again a little bit. The event is not originating from Button, its originating from the element to which we have attached the event handler to.
Commands are the actions that the user would like the application to perform. WPF defines a lot of builtin commands. We could see some of them below:
But need to note here that these commands do not actually do anything. These commands are nothing more than a name unless you define as what these do would. But the controls in WPF have defined these commands in their implementations. Like the textbox has defined the implementation of the copy, cut and paste command. The pattern for the command is that commands on their own do not mean anything but they need to execute in the context of a particular control. So lets implement a command handler. Put the following code in a WPF application.
When you will run this app you will see that both the buttons have greyed out. This is because these do not know what to do with these commands. So let’s provide a handler for these commands to work. So add the following code in the code behind and you would see the commands working.
You could also use CanExecute property to decide at the runtime as to whether that property is available to run or not.
We could also bind the commands with the Menus. So we add built in commands to menus then the menu will be able to figure the shortcut key for each command and show it. It is able to do that becase each builtin command knows what its shortcut key is.
Also we don’t have to say ApplicationCommands.Copy. Also we have not specified the command objects text and so the menu looks up the display text for the commands and shows that. So the commands display the local language display text. But the access key is not available for the Menu items so if we want the menu items to have access key we need to specify the header. The reason that it is not done automatically is that commands are at application scope whereas the access keys are menu item scope for example in File Menu P would mean Print but in Edit menu P would mean Paste.
Now when you run the app you would see that the commands are working without writing any code that is because TextBox has registered the command binding for these commands.
If we do not wish to use commands we could input gestures to provide the keyboards shortcuts for these items.
The code for this post can be downloaded here
Any questions, comments and feedback are most welcome.