[WPF] Binding to application settings using a markup extension


Hi, this is my first post on this blog, I hope you will enjoy it ;-). If you want to know a few things about me, please check out this page.

The end-user of any application expects that his preferences (window size, state of this or that option…) are saved to be restored at the next run : that’s why .NET 2.0 introduced application settings as a unified way to persist these settings. However, if there are many settings, it can be a real hassle for the developper to handle them… even with the help of the Settings class generated by Visual Studio, there is still quite a lot of code to write to apply these settings to the user interface, then update them according to user modifications.

In Windows Forms, it was possible to define bindings between control properties and application settings, but it wasn’t very intuitive, and wasn’t very widely used (I’m not so sure about that, but I actually never saw it used by anyone…).

With WPF, we can do something similar in a much more elegant way… although it’s not “officially” documented, it is possible to create bindings to application settings in XAML. For instance, to persist the window size and position in application settings, many blogs suggest this approach :

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:p="clr-namespace:WpfApplication1.Properties"
        Title="Window1"
        Height="{Binding Source={x:Static p:Settings.Default}, Path=Height, Mode=TwoWay}"
        Width="{Binding Source={x:Static p:Settings.Default}, Path=Width, Mode=TwoWay}"
        Left="{Binding Source={x:Static p:Settings.Default}, Path=Left, Mode=TwoWay}"
        Top="{Binding Source={x:Static p:Settings.Default}, Path=Top, Mode=TwoWay}">

(In that example, Height, Width, Top and Left are application settings)

This code does work, but honestly, do you feel like writing this for every setting of the application ? It’s too verbose, not intuitive, and makes the code harder to read…

Of course, I’m not saying this idea is bad… but it’s very easy to improve it, by creating our own « markup extension ». In this post I’m going to explain how to write a class that inherits Binding, and allows to bind easily to application settings.

« Markup extension » are objects that can be used in XAML to retrieve values. They are used all the time in WPF : for instance, Binding, StaticResource and DynamicResource are markup extensions.

It’s quite easy to define your own markup extension, by creating a class that inherits the abstract MarkupExtension class, and implements the ProvideValue method. In our case, most of what we need is already implemented in the Binding class (which indirectly inherits MarkupExtension). So we’re just going to inherit Binding, and initialize the necessary properties in order to bind to application settings :

using System.Windows.Data;

namespace WpfApplication1
{
    public class SettingBindingExtension : Binding
    {
        public SettingBindingExtension()
        {
            Initialize();
        }

        public SettingBindingExtension(string path)
            :base(path)
        {
            Initialize();
        }

        private void Initialize()
        {
            this.Source = WpfApplication1.Properties.Settings.Default;
            this.Mode = BindingMode.TwoWay;
        }
    }
}

Note the « Extension » suffix at the end of the class name : by convention, most markup extensions have this suffix (Binding is an exception…). It can be omitted when using the class in XAML (similarly to attributes, for which the « Attribute » suffix can be omitted).

In that class, we defined two constructors, matching those of the Binding class. We don’t need to redefine the ProvideValue method, because the one implemented in the Binding class suits us perfectly (and anyway it is marked as sealed, so we couldn’t override it even if we wanted to…). The part that actually makes the code work is the Initialize method. It initializes the Source property, so that the Path of the binding maps to the specified setting, and sets Mode to TwoWay so that application settings are automatically updated from the UI. The point of doing this is that we don’t have to set these properties every time we bind to a setting…

To illustrate the usage of this markup extension, let’s go back to the previous example, and replace the Bindings with the SettingBinding extension :

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:WpfApplication1"
        Title="Window1"
        Height="{my:SettingBinding Height}"
        Width="{my:SettingBinding Width}"
        Left="{my:SettingBinding Left}"
        Top="{my:SettingBinding Top}">

Isn’t it much clearer, more readable, and shorter to write ?

And of course, to make it work, let’s not forget to save the settings in the application’s Exit event…

        private void Application_Exit(object sender, ExitEventArgs e)
        {
            WpfApplication1.Properties.Settings.Default.Save();
        }

That’s it ! the window size and position are now saved, and restored when the application is started again, without having to write anything more…

Download source code

Update : If you understand French and want to know more about markup extensions, I suggest you read my tutorial on this topic : Les markup extensions en WPF

About these ads

13 Responses to “[WPF] Binding to application settings using a markup extension”

  1. paul Says:

    Looks nice and it works. The problem for me is that the designer doesn’t like it and thinks there is an error.

    Error 2 No constructor for type ‘SettingBindingExtension’ has 1 parameters.

    • Thomas Levesque Says:

      Really ? for me it works fine in the designer… What’s very strange is that the statement in the error message is clearly wrong, since there IS a constructor with one argument…
      Try to rebuild the project and re-open the designer, and if it still doesn’t work, you can always specify the Path property explicitly :

      Height=”{my:SettingBinding Path=Height}”

    • Mek Says:

      I am having the exactly same problem with a markup extension I created. I found this post while searching for the solution… do you have one, please? Looks like a bug of VS to me…

      • Thomas Levesque Says:

        Hi Mek,

        Actually the WPF designer has trouble with markup extensions that are defined in the same assembly as the current project… So you have 2 options :
        – Put the markup extension in a separate assembly
        – Use the default constructor and specify the Path property explicitly :

        Height="{my:SettingBinding Path=Height}"

  2. Paul Says:

    Your solution works great, but I have one question: is there any way to access Properties.Setting.Default object outside from its owner project? E.g. I have simple Class Library, which contains yours SettingBinding Markup Extension and I can’t write there this line:

    this.Source = WpfApplication1.Properties.Settings.Default;

    because from the one hand my Settings object has internal modifier and from the other hand I want to have universal SettingBindingExtension, no only connected with one default WpfApplication1 project. I also tried to resign from that universal conception, but when I want to use:

    [assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "MyNamespace")]

    Visual Studio report an error, that SettingBinding is not registered in default namcespace. Interesting thing is that if I have the same XmlnsDefinition in my Class Library (instead of in my main project) this problem doesn’t exist and everything works fine (besides that I can’t use then Properties.Settings.Default statement in that Class Library). Any suggestion will be appreciated.

    • Thomas Levesque Says:

      Hi Paul,

      Indeed, this markup extension can’t work from another assembly, at least in its current state. However, it’s pretty easy to modify it to make it “universal”, or close to it…

      Here’s the solution I came up with: instead of explicitly using WpfApplication1.Properties.Settings.Default as the source, I created a new SettingsManager class with a static Settings property. The class initializes the Settings property using reflection in its static constructor, but it is possible to explicitly assign it when the program starts.

      I posted the new code here: http://pastebin.com/T7waZ2mL

      Regards,
      Thomas

      • Paul Says:

        Thank you very much, this is exactly what I was looking for. Work like a charm :)

        Best regards,
        Paul.

  3. Mike Kulls Says:

    Doesn’t this have the issue that 2 windows will overwrite each other’s settings? So you need to define the properties as Window1_Height, Window2_Height etc

  4. Binding To Applications Settings | Dot Net Lore Says:

    [...] this approach to Love this approach to binding to application settings using a markup extension in WPF. About Jewel [...]

  5. ohadsc Says:

    Works great!

    Were you able to get Resharper to recognize it though? According to their blog it should work but it doesn’t for me (I tried the “Binding” suffix and the MarkupExtensionReturnType attribute to no avail). It tries to resolve the settings property from the DataContext in its intellisense which of course fails.

    BTW settings bindings in Winforms worked great! It was actually better than WPF because you could do it straight from the designer. I used it all the time :)

  6. Алексей Баркалов Says:

    Unfortunately it doesn’t work with converter:
    Error 1 Object of type ‘System.Windows.StaticResourceExtension’ cannot be converted to type ‘System.Windows.Data.IValueConverter’.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: