Introduction

This article is targeted for Beginner to Intermediate level WPF programmers. This article assumes that you have some basic knowledge of WPF in regards to layout. If you have no experience with WPF, I strongly suggest reading Sacha Barber’s “WPF: A Beginner’s Guide” series.

The Windows Presentation Foundation (WPF) provides developers with a vast amount of data binding functionality that has been praised by many, including yours truly. However, one area that I’ve found where the data binding falls short is with the XmlDataProvider. Unlike most data binding options in WPF, the XmlDataProvider does not natively support two-way binding. Meaning, if you make modifications to the data via a bound control, the changes are not persisted to the source XML file. This article will explain how to overcome that shortcoming and mimic two-way binding with the XmlDataProvider.

Background

The application in this article was developed with Visual Studio 2008, and targets the 3.5 version of the .NET framework.

Overview

The attached demo code consists of a WPF application with a single window and an XML file. Since I enjoy fantasy football, my XML file consists of NFL teams and their associated conferences. An example of the data within the Teams.xml file looks like this:

The single window in the application consists of several controls: a ListBox, three Labels, and three TextBoxes. The ListBox displays the NFL team name. The three TextBox controls display the team data associated with the currently selected team within the ListBox.

The WPF window looks like this:

One Way Binding

I won’t get into the details of layout here. However, I want to point out the items within the XAML that make the binding from the source to the control possible. The first, and the most important, item is the XmlDataProvider. The XmlDataProvider specifies the underlying source XML file through its Source attribute. The XPath attribute is used to specify the level at which the data binding occurs. The x:Name attribute exposes the XmlDataProvider to the code-behind.

You’ll notice in the example above that the XmlDataProvider is contained within Grid.DataContext tags. The layout of the application’s single window is a Grid. Since the XPath statement starts the binding at the team level and the XmlDataProvider is defined within the Grid’s DataContext, the Grid is bound to the data at the team level.

Note: The binding does not have to be done at the Grid level. We could bind the ListItem and other controls to the data within the XmlDataProvider individually. However, there is a synchronization issue when multiple controls bind directly to the XmlDataProvider and use the XPath statement. Explanation of the issue is out of the scope for this article, but can be found at Ian Griffiths’ blog. The approach recommended by Ian was incorporated into this article and the demo code.

The next item in the binding architecture is the ListBox. Since the ListBox is contained within the Grid, and the Grid is bound to the data at the team level, the bindings can be inherited. We can accomplish this simply by setting the ListBox's ItemsSource property to the current bindings.

Each ListBox item is now bound to a Team element in the XmlDataProvider. To display the team name in the place of the ListBox item, we utilize the teamItemTemplateDataTemplate. The DataTemplate displays a Label control and utilizes the XPath attribute to traverse down to the Name element.

The final items in the binding architecture are the TextBox controls to display the selected team’s data. For layout purposes, all of the Label and TextBox controls for the team’s individual items are displayed within a StackPanel. The StackPanel, like the previously mentioned ListBox, is contained within the Grid. We again use the inherited bindings, and by way of the XPath attribute, traverse down to the corresponding element.

This is all that is needed to provide one way binding of WPF controls to an XML file.

Two-Way Binding

Now that we can consume an XML file and bind it to our GUI, it would be really nice if we could persist any changes that we make to the data. Here is where we realize the shortcomings of the XmlDataProvider. If you modify the data within the Team Name textbox, you will see that the changes are persisted within the ListBox. Unfortunately, these changes are only in memory and are not persisted to the underlying XML file. If you stop and restart the application, the original data will be reloaded from the unchanged XML file. Luckily, we can very easily write some code to mimic the binding from the GUI back to the XML source file.

Before I discuss the code, there is one configuration option we must modify. Since the Teams.xml file will need to be deployed with the application’s executable, it is easier to copy it to the output directory upon a successful build. We can right click the Teams.xml file, choose Properties, and select the “Copy if newer” option for the Copy to Output Directory configuration. This will copy the XML file to the same location as the application’s executable. Now, anytime we need to reference the file, we can simply append “Teams.xml” to the path of the currently executing assembly.

There are two code blocks that must be added to the window’s code-behind file to finalize the two-way binding architecture. First, we add code to set the Source property of the XmlDataProvider to the copy of the XML file located in the output directory. This ensures that we are reading from and writing to the same instance of the XML file. Second, we utilize an event handler to execute the code to persist the in-memory data to the XML file. In the demo, I used the Click event handler for the Save button. This could easily be modified to execute within a different event handler such as a TextBox.LostFocus or a Window.Closing event. To persist the changes, we simply call the Save method on the XmlDataProvider’s Document property and specify the XmlDataProvider’s Source as the destination file.

Alain - I apologize for the late response. I have been off the grid for a little while. Because of my late response, instead of attempting to answer you question with plain text, I have created a follow article that addresses the inserting and deleting of records. Please refer to WPF: XmlDataProvider Two-Way Data Binding - Enhanced for details on how to perform insertions and deletions. Thanks for your feedback.