Figure 1
In the sample we use the patterns and assemblies introduced in the Oct 09 verion of Prism. Also, this is similiar to the XmlDataProvider with TreeView sample we have posted. However, it uses Telerik's TreeView so we don't have to implement the extra source code as we did in the first sample.
If you haven't checked out Telerik and their wide variety of controls for the .Net framework, I suggest you give them a try. I don't taught too many companies but they do a great job of listening to customers and going the extra mile with their support.
A brief overview of the application layout. (If you aren't familiar with the patterns of Prism or MVVM, please check out our Prism NW series of lessons.)
First, in our main application, we have our bootstrapper which loads our Shell. Our shell contains the TreeRegion which we use to load our TreeView view from the TreeView module. This View will simulate our editor which allows us to make changes to our XML file and control the the visibility of our UI Elements which are bound indirectly to the XML settings.
It also contains the UIRegion which we use to display our MainUIView from our MainUIModule. This view will mimick a normal view you would have in an application used to display data to users.
We use StackPanels and bind their Visible property to the Visible attribute in our XML file to control whether or not they are visible when rendered. The idea is to be able to disable UI Elements by group or individual.
Since we are reading string values from an XML file, in some cases we'll need to convert these strings to the appropriate values. WPF/Silverlight allows you to create value converters in these sitations. During binding, if you you specify the converter which gets passed the values to be converted. If you have converters you want to share throughout the application, it's standard to create them in the main WPF application. Then create a ResourceDictionary to define them. Finally we make reference to them in the app.xaml Application.Resources section. Then they are available throughout the application by using a Static resource.
The RadTreeViewCheckBoxXMLMVVM project contains our value converters as well as our Resource Dictionaries. One of the converters is the NullToBoolConverter class. Under the Resources directory you'll see where we have defined a ResourceDictionary called GlobalConverters.
The xaml is as follows:
Code:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:RadTreeViewCheckBoxXMLMVVM" > <local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" /> <local:NullToBoolConverter x:Key="NullToBoolConverter" /> </ResourceDictionary>
Next, in the app.xaml file you can see where we create a ResourceDictionary in our MergedDictionaries.
Code:
<ResourceDictionary Source="Resources/GlobalConverters.xaml"/>
Here is a code we use for the Binding to utilize a converter:
Code:
<Setter Property="IsExpanded" Value="{Binding XPath=@IsExpanded, Converter={StaticResource NullToBoolConverter}}" />
So far we've covered the plumbing of our application and made our way to a binding statment in our TreeView.Xaml View. Now we're going to jump to how we are able to bind to the data in our XML file. (Again, I'm going to assume you understand the basics of MVVM and understand the concepts of a View binding to a ViewModel. If not, please check our our Prism NW lessons or some of the other links we have in the community for good resources on MVVM.)
WPF provides a XmlDataProvider out of the box. This object gives us the ability to load an XML file into an XML Document to give us read and write access. In our scenario, we want our config file to be central to the application. Also, it's a good idea to create an object to which everyone goes through to access the file. This will prevent a lot of different handles from opening and loading the contents of the files and should help with contention problems.
I chose to create a singleton and place it in our Infrasctructure object as it will need to be accessible from any assembly in our solution. The name of the Singleton is UIConfigProvider. The constructor takes care of loading contents of our UIConfig.xml file and loading it to the Document property of our XmlDataProvider. We also set the XPath property to the root of our xml file, UIConfig.
We create a public property called Provider which any callers can use to obtain a reference to our XmlDataProvider. Any element that binds to this property will be able to update and automatically reflect changes made to the xml data real time.
Also, in the UIConfigProvider we have a public Save function. Callers use Save to save any changes back to the XML file.
Now that we have a central class to handle our XML data, next we go back to our TreeView Module. We need to setup our TreeViewModel class and provide all the objects that will be needed by our TreeView View to display our data to our users.
First in our ViewModel constructor, we use Depency Injection to get the ITreeView passed in. We then set the ViewModel to the view to this. Note in setting the Views ViewModel, we are setting the DataContext of the View to the TreeViewModel so we can bind to anything in it.
First we create the CurrentUIConfig property which is of Type XmlDataProvider. This returns the XmlDataProvider from our UIConfigProvider in the Infrastruture assembly.
Code:
public XmlDataProvider CurrentUIConfig
{
get { return UIConfigProvider.Instance.Provider; }
}
Now we make our way back to our TreeView. Now that we have all the plumbing setup, it's time to start creating our page.
After setting up the page layout, we'll create a button and set it's command to our SaveUIConfigCommand we defined above in the TreeViewModel. We clicked, this will save the contents of our XmlDataProvider out to our XML file.
Next we'll add a Telerik Rad TreeView. The Rad TreeView is feature rich and easily gives us the ability to display tree lines by with the IsLineEnabled & IsRootLinesEnabled properties.
Then we set the DataContext of our RadTreeView to the CurrentUIConfig property of our ViewModel. Remember what allows us to just set this directly to the CurrentUIConfig is that our TreeView view had it's DataContext set to our TreeViewModel in the TreeViewModel's constructor.
Make sure to set your ItemsSource="{Binding}" This tells the control to use the controls DataContext to get the items to display.
So far we have some basic propertys of our Tree defined. However, lets define the requirements of our TreeView.
- We need the ability to set whether a tree node is expanded or not.
- We want each element of the Tree to have a checkbox that is bound to the Visible property of each element of our UIConfig.XML file.
- We also want to display Text attribute each element.
- Finally, we need to ability to determine whether a check box is enabled or not.
We create a style and give it a key name of RadTreeViewItemStyle. We set it to a TargetType="telerik:RadTreeViewItem" . The target type will apply this style any RadTreeView's RadTreeViewItem's when the RadTreeView's ItemContainerStyle is set to RadTreeViewItemStyle.
Setting the TargetType gives us the ability to start setting properties that are part of the object defined as the TargetType. We need to set the IsExpanded Property of each of our RadTreeViewItem's. So with is line of Xaml:
Code:
<Setter Property="IsExpanded" Value="{Binding XPath=@IsExpanded, Converter={StaticResource NullToBoolConverter}}" />
Also, note, for our Binding we use XPath=@IsExpanded . This is possible because the DataContext of our RadTreeView is set to an XmlDataProvider.
In order for our RadTreeView to implement this style we've created, we bind our ItemContainerStyle to is as follows. (Full code is in the TreeView.Xaml)
Code:
ItemContainerStyle="{StaticResource RadTreeViewItemStyle}"
WPF gives us the functionality through DataTemplates. In our sample, we'll be using a HiearchicalDataTemplate called CheckBoxItemTemplate.
Code:
<HierarchicalDataTemplate x:Key="CheckBoxItemTemplate" ItemsSource="{Binding XPath=*}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<CheckBox Focusable="False" IsChecked="{Binding XPath=@Visible, Converter={StaticResource NullToBoolConverter}, Mode=TwoWay}"
VerticalAlignment="Center"
IsEnabled="{Binding XPath=@IsCheckBoxEnabled, Converter={StaticResource NullToBoolConverter}, Mode=OneWay}"
/>
<ContentPresenter Grid.Column="1" Content="{Binding XPath=@Text, Mode=OneWay}" Margin="2,0"/>
</Grid>
</HierarchicalDataTemplate>
</ResourceDictionary>
Also, please note our use of Mode. For the IsChecked property, we use a TwoWay mode. This will allow and changes to the checkbox to automatically update the data stored in our XmlDataProvider. Which in returns any control bound to the same attribute will automatically get updated. Thus, when a user clicks one of the checkboxes in the RadTreeView, you the MainUIView UI Elements will automatically change their visibility based on whether the checkbox is checked or unchecked. Very cool!!!
Since the IsEnabled property only needs to be read from the file in this scenario, we set the Mode to OneWay. Only use TwoWay if you need it.
Finally we use the ContentPresenter and bind it's content to our XML Elements Text attribute. Again we only need a OneWay binding with the text property for our sample.
Now we have a working sample. When you run the app, our TreeView displays on the left and the MainUIView to the right. In a real world scenario, the TreeView would be in a pop or load by itself. But for demonstration purposes I put them side by side to demonstrate how our changes could be made real time without a lot of refresh code having to be written.
So there you have it, using the Telerik RadTreeView, we have a very simple way of creating a TreeView Editor bound to attributes of an XML file using the modularity of the MVVM concepts.







Section Widget
Category Widget (bottom-up)

Rate this article