WPF and DataBinding

If you are a regular reader, you will have noticed that I don't often blog about UI type stuff.  Honestly, I suck at UI work.  I am the typical example of the separation between developers and designers.

I have been playing with WPF a little more lately.  Yeah, I know the rest of the world seems to be playing with Silverlight 2, but I am really getting into WPF and it's DataBinding capabilities.  I am more and more impressed at what WPF provides and how much easier it is for me to write applications than using Windows Forms.

In a previous post, I showed how to use the ObjectDataProvider with an IValueConverter, and in this post, I am going to elaborate on how to use the XmlDataProvider and ObjectDataProvider.  This is a really rudimentary example of using WPF, but it shows how you can very quickly build applications that access remote data. 

The key is to create the XmlDataProvider as a resource, specifying the x:Key and Source properties.  The key is used to identify the data source from your binding. 

For instance, here is a really quick app I wrote that presents the content of my blog.

 <Window x:Class="WpfApplication3.Window1"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="480" Width="640">
    <Window.Resources>
        <XmlDataProvider x:Key="kirksblog" Source=https://blogs.msdn.com/kaevans/rss.xml
        XPath="rss/channel"/>                    
    </Window.Resources>
    <StackPanel DataContext="{Binding Source={StaticResource kirksblog}}" Height="Auto">
        <Border></Border>
        <Label Content="{Binding XPath=title}"/>        
        <Label Content="{Binding XPath=description}"/>
        <Label Content="{Binding XPath=link}"/>               
            <ListBox x:Name="lbColor" 
                 ClipToBounds="True"
                 ScrollViewer.VerticalScrollBarVisibility="Visible"
                 Height="300" 
                 ItemsSource="{Binding XPath=item}" ScrollViewer.CanContentScroll="False">                        
            <ListBox.ItemTemplate>
                <DataTemplate>                    
                    <StackPanel >
                        <TextBlock Text="{Binding XPath=title}" Background="Blue" 
                        Foreground="White" />
                        <TextBlock Text="{Binding XPath=link}"></TextBlock>
                        <TextBlock Text="{Binding XPath=description}"  
                        Background="BlanchedAlmond"/>                        
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>        
</Window>

Yeah, cheesy, I know... the HTML content is presented as raw HTML instead of written to disk and presented in a WPF Frame element or using the WindowsFormsHost to host a browser control. But bear with me, I'm just getting into WPF. 

The coolest thing to me about this example is that I can leverage various contexts within the databinding.  For instance, I specify part of the XPath in the XmlDataProvider itself (rss/channel).  In the Label controls, I don't have to specify the full XPath (examples would be rss/channel/title or rss/channel/description), I can just specify the child elements of the channel element.  In the ListBox, I specify its ItemsSource to be the item element (rss/channel/item), and in the content elements within the ListBox I simply specify the child element instead of the full XPath (rss/channel/item/title). 

This notion of context works well for hierarchical data structures, including object graphs.  Instead of specifying the XPath property above, I can use the Path property to access members as well as to specify the context path. 

Let's look at a simple example.  I use the ObjectDataProvider to create an instance of a System.DateTime type.  This example is kind of cool because it shows use of the ConstructorParameters as well as the MethodParameters.  The AddYears method is called, which returns another DateTime, which is the actual instance that our binding provides.

 <Window x:Class="WpfApplication3.Window1"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sysxml="clr-namespace:WpfApplication3"    
    xmlns:sys="clr-namespace:System;assembly=mscorlib"    
    Title="Window1" Height="250" Width="640">
    <Window.Resources>
        <ObjectDataProvider x:Key="demo" ObjectType="{x:Type sys:DateTime}" 
        MethodName="AddYears">
            <ObjectDataProvider.ConstructorParameters>
                <sys:Int32>2008</sys:Int32> 
                <sys:Int32>3</sys:Int32>
                <sys:Int32>21</sys:Int32>
                <sys:Int32>18</sys:Int32>
                <sys:Int32>5</sys:Int32>
                <sys:Int32>21</sys:Int32>
            </ObjectDataProvider.ConstructorParameters>
            <ObjectDataProvider.MethodParameters>
                <sys:Int32>5</sys:Int32>               
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>        
    </Window.Resources>
    <StackPanel DataContext="{Binding Source={StaticResource demo}}" >
        <StackPanel Background="BlanchedAlmond">
            <TextBlock FontWeight="Bold" TextWrapping="Wrap">Original Date</TextBlock>                        
            <TextBlock Text="{Binding Path=Year}"/>
            <TextBlock Text="{Binding Path=Month}"/>
            <TextBlock Text="{Binding Path=Hour}"/>
            <TextBlock Text="{Binding Path=Minute}"/>
            <TextBlock Text="{Binding Path=Second}"/>               
        </StackPanel>
        <StackPanel Background="BurlyWood" DataContext="{Binding Path=Date}">
            <TextBlock FontWeight="Bold" TextWrapping="Wrap">Date Component Only</TextBlock>
            <TextBlock Text="{Binding Path=Year}"/>
            <TextBlock Text="{Binding Path=Month}"/>
            <TextBlock Text="{Binding Path=Hour}" 
            Background="Yellow" HorizontalAlignment="Left" Width="100" />
            <TextBlock Text="{Binding Path=Minute}" 
            Background="Yellow" HorizontalAlignment="Left" Width="100" />
            <TextBlock Text="{Binding Path=Second}" 
            Background="Yellow" HorizontalAlignment="Left" Width="100" />               
        </StackPanel>
    </StackPanel>
</Window>

In the outer StackPanel, we provide a DataContext to the binding source itself.  In the second child StackPanel, we provide a DataContext to the Date property.  The Date property of the System.DateTime type will return a DateTime that contains the Date component only, no time component is included.  I highlighted those to be Yellow just for effect.

I could have used a less contrived example (Customer, Orders, OrderDetail, etc), but wanted to show something you could quickly run in XamlPad while showing off how to use the various features of the ObjectDataProvider.

This is really fun to work with.  I am able to build things very quickly in a very intuitive manner.  I'm not yet proficient with Expression Blend, so I am doing everything in Visual Studio 2008 with Intellisense completion helping me out.  Makes things incredibly easy to work with.