In this conclusion to a four-part series that illustrates WPF through the example of building a to-do list application, we will complete the program we started. You will learn how to make the software handle deleting tasks, and how to give it the look and feel you want.
Now that a mechanism for the addition of new tasks has been added, the user needs a way to delete an existing task. In the code, deleting tasks is much easier than adding tasks. The element representing the selected task simply needs to be removed from the XML when the user clicks the delete button.
In Visual Studio, double click the delete button in the designer to automatically create a click event handler, or manually add it:
The code for deletion is really simple. First, the code needs to check that an item is indeed selected. If nothing is selected in TaskListBox, then its SelectedIndex property will be -1. So, the code simply needs to check that SelectedIndex isn't -1. If it isn't, then the value of the SelectedItem property of TaskListBox needs to be cast into an XmlElement object, and this element needs to be removed from its parent element (Tasks):
You've probably noticed that even though tasks can be added to and deleted from the list, the changes are erased when the program is closed. This is because with an XML data source, the binding really only goes one way. Any changes are actually only saved to an in-memory XML document, not the original source file.
However, it's not very difficult to work around this. We simply need to add code that writes the in-memory document to the physical XML file. Perhaps the easiest way to do this is to write the changes when the application is closed, although a more complex system would be better in a real application.
In the XAML for Window1, add an attribute pointing to a handler for the Closing event:
The to-do list works fine, but it does look a bit dull. We need to fix this by stylizing the application a bit. After all, WPF was built with the user's visual experience in mind, and why not take advantage of this?
One way—the most basic way—to change the look of the to-do list is to manipulate properties of the controls at the attribute level in XAML. The Properties Window in Visual Studio provides a convenient way to explore some of these properties and assign values.
Using attributes in XAML, we can easily modify the look of the “Add Task” button to give it an orange look:
If you run the application, you'll see that this works just fine: the button now sports a new look. However, if the look of one button is changed, then the look of other buttons must be changed, along with the looks of the other controls. Otherwise, the look is inconsistent and probably unappealing. However, applying a style to each control individually through attributes quickly gets tiring, and, moreover, the resulting XAML will look like a complete mess and be very difficult to read, much less manage or update. Rather than dealing with every control individually, we need to deal with the controls in groups.
Fortunately, WPF provides a way to do just that. Using styles, we can apply a consistent look for a target set of controls. This way, we can style the buttons the exact same way, the labels, and so on, without having to apply the style to each control individually. All we need to do is set the attributes one time. This saves a lot of work, and the resulting XAML is neat. Moreover, the look of the application can be changed very quickly, since it doesn't involve changing each control individually.
Styles belong in the same group as templates: they're resources, and so they need to go in a Resources section of the XAML. However, the to-do list application uses two windows, and, as a result, we can't put styles in Windows.Resources. Instead, the styles must be put in Application.Resources inside the App.xaml file. This way, the styles will be global rather than specific to a certain window. If you look inside of App.xaml, you'll notice that the Application.Resources tag is already there.
Defining styles is very simple. We only need to specify the target type (Button, CheckBox, etc.) and then list the properties we want to set and the values with which we'll be setting them. Let's start by applying the style from the “Add Task” button to all buttons. Go ahead and remove the style attributes from the button, if you've added them. Next, put the following XAML inside of the Application.Resources element:
When the application is run, all buttons will share the same look, even the buttons in the add task dialog. This creates a little problem, however. The dialog buttons are much smaller than the main window buttons, but the font size is the same in both. This means the text on the dialog buttons is cut off at the bottom. In this situation, a completely consistent look works against us.
This problem is easily fixed, thankfully. Just as, in C#, one class can inherit from another, so too, in XAML, can one style inherit from another. The default button style was defined at the application level. However, any other definitions at a lower level (at the window level or the layout level—it's also possible to give, for example, a Grid its own resources) will take precedence. When we do define a new style, we can re-use elements from another style so that the properties don't all have to be redefined. First, though, the button style that we just created will need to have an identifier:
The font size now fits the small size of the button.
Notice that this creates another problem, however. When the first style is given an identifier, it ceases to be the default style automatically applied to everything. It is possible, though, to explicitly apply a style to a control:
Now that you've learned the basics of styles, it's time to finish up the application. The principles are the same as before. We can create additional styles in the exact same way:
Of course, feel free to stylize the application as you see fit. One of the things you might want to do is create a base style for all controls, and then derive other styles from that, creating styles for specific controls while saving a lot of space. Of course, there's more to styles, but everything can't possibly be covered here in such a small amount of space.
Now the to-do application is complete, and you've seen a little bit of WPF in action. We've worked with XAML, controls, events, databinding, styles, templates, and a little bit of code. However, there is a lot more to WPF, and this only serves as a short introduction. But now you know the basics, and continuing on won't be very difficult at all. See if you can expand upon the to-do list and add new features to it, such as a menu or a calendar feature.