Binding a C# property to a WPF validated Control

By Jerome at August 29, 2007 05:48
Filed Under: .NET

The DataBinding in WPF allows the binding of control properties to many things, like other control properties, arrays, data providers, ... But it can also bind a control property to a property defined by code on the C# side.

It is interesting to bind to C# property to be able to have the integrated WPF validation and still use a simple property from the code. In that case, I wanted to have the validation of a TextBox displaying a DateTime object.

Here's how to do this.

On the C# side :


public partial class Window1 : Window

  public Window1() 
  {   
    MyDate = DateTime.Now;
    InitializeComponent();
  }
  public DateTime MyDate { get; set; }
}
 

Note that I'm using the latest C# 3.0 syntax to declare variable-less properties. This is compiler trick, the variable is still declared at compile time, but since I don't need to have a specific code in the get or set accessor, it can stay in this short form.

Now on the XAML side :


<Window ... >

  <TextBox Width="100" Height="20">

    <Binding Path="MyDate" RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType={x:Type Window}}" UpdateSourceTrigger="PropertyChanged">

      <Binding.ValidationRules>

        <ExceptionValidationRule />

      </Binding.ValidationRules>

    </Binding>

  </TextBox>

</Window>

The interesting part here is the use of RelativeSource and the FindAncestor mode. Here, we're looking for the property MyDate from the nearest ancestor instance of the Window type, from the current instance.

This way, you'll have a validated date time in your property value. Just make sure you're checking that the value is really valid using the System.Windows.Controls.Validation class.

WPF DataBinding and Application Settings

By Jerome at February 15, 2007 21:47
Filed Under: .NET

Well, yet an other post on WPF and some DataBinding. But this time, this is about DataBinding to application settings that are automatically generated by visual studio. These settings come in handy when you want to save your application settings per user, or have some application wide settings. In my case, I wanted to have my application to remember its size and position, as well as the window state.

WinForms were providing an UI to do this, and I wanted to have all that functionality back. It is not all that "visual" as WinForms can do it, but it works rather well. I guess that Orcas will provide a way to do this visually.

All I had to do to use these settings from XAML was to create a resource to be usable for DataBinding, from a separate file :


File: SettingsRes.xaml



<ResourceDictionary xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation

                    xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml 

                    xmlns:settings = "clr-namespace:WindowsApplication2.Properties">

  <ResourceDictionary.MergedDictionaries>

    <ResourceDictionary>

      <settings:Settings x:Key="settings" />

    </ResourceDictionary>

  </ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

WindowsApplication2 is the default namespace for the application, and since the Settings class is automatically generated, the default namespace is used. This resource will be used application wide and is referenced like this :


<Application.Resources>

  <ResourceDictionary>

    <ResourceDictionary.MergedDictionaries>

      <ResourceDictionary Source = "SettingsRes.xaml"/>

    </ResourceDictionary.MergedDictionaries>

  </ResourceDictionary>

</Application.Resources>

This is handy because resources are separated in multiple files. I also add here references to value converters, when I have to use them everywhere in the application. Value converters, yet an other interesting subject. Maybe in an other post :)

Now, about binding the data. We want to bind the Width, Height and WindowState of the default Window to some settings in the ApplicationSettings.

Here's what to do :


<Window x:Class="WindowsApplication2.Window1"

        xmlns = http://schemas.microsoft.com/winfx/2006/xaml/presentation

        xmlns:x = http://schemas.microsoft.com/winfx/2006/xaml

        Height = "{Binding Source={StaticResource settings}, Path=Default.MainWidth, Mode=TwoWay}"

        Width = "{Binding Source={StaticResource settings}, Path=Default.MainHeight, Mode=TwoWay}"

        WindowState = "{Binding Source={StaticResource settings}, Path=Default.MainState, Mode=TwoWay}">

We create three bindings, each for an attribute, and set the source to the "settings" resource. The interesting part here is that the even thought the property we use is static, Settings.Default here, the Binding engine does seem to support it.

Setting the binding path to Default.MainWidth binds to the appropriate property. The last parameter instructs the binding to set the value of the property when the binding destination value changes, which can be the actual window height, for instance. The type of the property does not need to match the destination exact type, and since the type of the WindowState property is not known by VS2005 settings designer by default, setting the type to string seems to be enough.

Simple and easy. Now my application remembers where it was !

The sample here is a bit more complex, since it includes code to actually save the settings. But this is nothing really complex.

Man ! I like WPF... !

WPF DataContext and CurrentItem

By Jerome at February 13, 2007 15:23
Filed Under: .NET

DataBinding is one of the technologies I like the most. 

Winforms did a fine job introducing the concept, but it was pretty easy to be stuck when trying to perform complex databinding. .NET 2.0 brought the concept of BindingSource, which eased the management of multiple "Current items" on a single form. You might have encountered this when creating multiple master/detail views on the same form. That was the case when you wanted to walk through a set of tables through their relations, say down to three levels.

WPF has a far more advanced DataBinding engine and introduces the concept of DataContext. There's the notion of a hierarchy of DataContexts and a databound control uses the nearest DataContext available.

An example is better than a thousand words. Here's a data source :


<xmldataprovider x:key="ops" xpath="/data/level1">

  <x:XData>

    <data xmlns="">

      <level1 name="1">

        <level2 name="1-1">

          <level3 name="test">Some Value</level3>

          <level3>Yet an other value from level 3</level3>

        </level2>

        <level2 name="1-2">

          <level3>Some other Value</level3>

          <level3>Yet an other Value</level3>

        </level2>

      </level1>

      <level1 name="2">

        <level2 name="2-1">

          <level3>Some Value</level3>

          <level3>Yet an other value from level 3</level3>

        </level2>

        <level2 name="2-2">

          <level3>Some other Value</level3>

          <level3>Yet an other Value</level3>

        </level2>

      </level1>

    </data>

  </x:XData>

</xmldataprovider>


It's a three level hierarchy, and I want to display this data, by recursively selecting each level in a ListBox to see its content.

Now, let's bind this data to a list box, contained in a GroupBox to be a bit more readable :


<GroupBox Header="Level 1" DataContext="{Binding Source={StaticResource ops}}">

  <ListBox ItemsSource="{Binding}"

           DisplayMemberPath="@name"

           IsSynchronizedWithCurrentItem="True" />

</GroupBox>

This performs a binding to the attribute "name" of the level1 node list. The IsSynchronizedWithCurrentItem tells the listbox to set the CurrentItem of the current DataContext, which can be used to fill the next ListBox for the level.

Now, to add a new DataContext level, let's add a new GroupBox, and a stack panel to have a nice layout :


<GroupBox Header="Level 1"

          DataContext="{Binding Source={StaticResource ops}}">

  <StackPanel Orientation="Horizontal">

    <ListBox ItemsSource="{Binding}"

             DisplayMemberPath="@name"

             IsSynchronizedWithCurrentItem="True" />

    <GroupBox Header="Level2"

              DataContext="{Binding Path=CurrentItem}">

      <ListBox ItemsSource="{Binding}"

               DisplayMemberPath="@name"

               IsSynchronizedWithCurrentItem="True" />

    </GroupBox>

  </StackPanel>

</GroupBox>

Now, there is a new DataContext for any children of the second group box, and is having the CurrentItem of the upper DataContext as a root. This is fairly easy to do, so let's do this for the final level.


<GroupBox Header="Level 1"

          DataContext="{Binding Source={StaticResource ops}}">

  <StackPanel Orientation="Horizontal">

    <ListBox ItemsSource="{Binding}"

             DisplayMemberPath="@name"

             IsSynchronizedWithCurrentItem="True" />

    <GroupBox Header="Level2"

              DataContext="{Binding Path=CurrentItem}">

      <StackPanel Orientation="Horizontal">

        <ListBox ItemsSource="{Binding}"

                 DisplayMemberPath="@name"

                 IsSynchronizedWithCurrentItem="True" />

        <GroupBox Header="Level3"

                  DataContext="{Binding Path=CurrentItem}">

          <StackPanel Orientation="Horizontal">

            <ListBox ItemsSource="{Binding}"

                     IsSynchronizedWithCurrentItem="True" />

            <Label Content="{Binding Path=CurrentItem}" />

          </StackPanel>

        </GroupBox>

      </StackPanel>

    </GroupBox>

  </StackPanel>

</GroupBox>

Each time a new DataContext is set, a new CurrentItem is created. That kind of behavior was hard to reproduce using DataBinding with WinForms; WPF allows it only by using a simple declarative syntax. Easy and powerful.

Also there is a fine feature that came up when using this DataContext and CurrentItem : The CurrentItem "chain" for a specific path is -- when the data source does not change -- kept if you change the selection, and come back to that particular path. Pretty interesting.

Here is a working xaml sample of this post.

Did I say that a really like WPF already ? :)

WPF, Xml namespace and XmlDataProvider

By Jerome at January 30, 2007 16:15
Filed Under: .NET

I was playing with the XmlDataProvider in WPF and I wanted to bind the content of inline XML from that provider to a ComboBox.

So, with I wrote this little piece of code :

<XmlDataProvider x:Key="ops" XPath="/operations/op">  <x:XData>    <operations>      <op>A</op>      <op>B</op>      <op>C</op>    </operations>  </x:XData></XmlDataProvider>[...]<ComboBox   ItemsSource="{Binding Source={StaticResource ops}}" />

Turns out that the combobox does not display anything, even though the XPath for the XmlDataProvider is correct and the ItemSource binding as well.

In reality, the problem is not in the binding but rather in the XPath, where the XML "operations" node in the inline xml inherits from the System.Windows XML namespace, which renders the XPath "/operations/op" ineffective. In that case, the XPath expression selects nodes from the "" (empty) namespace.

I just needed to write this instead :

<XmlDataProvider x:Key="ops" XPath="/operations/op">  <x:XData>    <operations xmlns="">

to reset the namespace used for that node, and my ComboBox was filled ! 

As a side note on WPF, I am just wondering on how this technology will be adopted. Many concepts are fairly innovative and differ from the usual concepts found in Winforms or even MFC.

I'm convinced that it is definitely going in the right direction with the data and design separation, but I'm not sure on how beginners are going to apprehend all this. Maybe a new release of Cider is going to ease development with this technology...Wait and see. 

Meanwhile, I'm continuing to enjoy the fact that I can databind a TreeView very easily :)


About me

My name is Jerome Laban, I am a Software developer and .NET enthustiast from Montréal, QC. You will find my blog on this site, where I'm adding my thoughts on current events, or the things I'm working on, such as the Bluetooth Remote Control Software for Windows Mobile.