Client Settings - Part 2

In a post a few months ago, I wrote about a new Whidbey feature called Client Settings. While we briefly described this feature at the PDC, it wasn't available in the build of Whidbey released then. The Community Preview build gives you a chance to try out everything I mentioned and more.

Quite some functionality has been added to this feature since the PDC. I list some of the new capabilities below. If you haven't read my previous post, please read that first for background.

[Caveat: Not all this may work very well in the recently released Community Preview build]

  • First of all, we now provide full fledged design time support in Visual Studio for you to easily create some settings and bind to them. As a simple example, lets say you want to provide the user of your app the ability to customize the BackColor of a Form, and you want to save their setting, so that when they run the app again, you can restore their preference. All you need to do is this: in the Form designer in Visual Studio, you will notice a new property in the property browser named “(Application Settings)”. This allows you to create a setting (in this case, user scoped) and set up a binding to a property in a manner very similar to setting up data bindings. In the background, the Settings Designer will create a class for you that derives from ApplicationSettingsBase that has a property corresponding to the setting you just created. And the Forms Designer will generate the code to data bind that setting to your BackColor property. All you need to do now is to call Save() on your settings object - say, in the Form's Closing event. This line, in VB, will look something like:

My.Settings.Save()

That's it! You now have everything setup to save each user's preference for BackColor of the Form.

  • The Settings Designer itself is another place you can start to create settings. In a VB project, the settings designer is offered as a tab in the Application Designer. In C#, you should be able get to it in a similar manner in a future build. The designer has simple UI to create settings and assign default values to them. It will generate the code for the settings class and its properties, with appropriate attributes specified.
  • ApplicationSettingsBase offers a bunch of new functionality. It has events that are fired when a setting's value changes, or when settings are saved (i.e., passed down to the provider). You can use these to do validation for example. It also provides methods to reset the settings to their default values and upgrade settings from a previous version of the settings class.
  • We also provide the ability for components to store their own settings, in a manner transparent to the host, through an interface called IPersistComponentSettings. The ToolStrip is an example of a control that implements this interface. If you drop a ToolStrip on your Form, you will notice a boolean property called 'SaveSettings'. Just set this to true, and the ToolStrip will automatically begin to store its own settings - like which rafting container it is placed in and what its location is within the container. ToolStrip items too can store out settings of their own. All this happens transparently - a ToolStrip user doesn't need to setup anything - just set SaveSettings to true! You can provide this same functionality for your component/control just by implementing IPersistComponentSettings.

So you may be wondering - where are these settings stored? That really depends on the settings provider you are using. The default settings provider that ships in Whidbey is called the LocalFileSettingsProvider. This stores all the settings in configuration files. One of the other things the Settings Designer described above does for you is put the required entries in the application config file, where our provider stores application scoped settings, and default values for user scoped settings.  In .Net Framework v1.1, there were two levels of configuration files - machine and application. In Whidbey, we have introduced the concept of user specific config files, that are stored under the user's profile path. These config files are created when a user saves their preferences, and contain the values that differ from the defaults.

Anyway, that's just an overview of some of the new functionality we have added since the PDC. If you are using the feature, do let us know what you think!

Comments

  • Anonymous
    April 03, 2004
    That's a cool feature. But then I might not be the most objective person to make that kind of comments. :]
  • Anonymous
    June 17, 2004
    The comment has been removed
  • Anonymous
    June 18, 2004
    I can't find an example of the file format used for LocalFileSettingsProvider - is this available anywhere for me to see?

    In addition, how do collections (eg HashTables) get serialized?
  • Anonymous
    June 20, 2004
    ...and...
    SettingsPropertyWrongType
    should end with the string "Exception". Doesn't FxCop pick this up for you guys?
  • Anonymous
    June 20, 2004
    RichB: That's a good observation. I will pass your comment on to the team that owns that exception class.
  • Anonymous
    June 21, 2004
    Please could you make the properties (eg MyFormColor or FooLocation in your example) virtual. That way we can derive from the auto-generated class to include extra behaviour.

    For example, I have a requirement that the default value for a date is DateTime.Now -3 months. The nicest way I can think of doing this is setting this property in the constructor of a subclass.

    There also doesn't appear to be a way to "hint" to the provider what name to use for the configuration item. It appears to simply use the name of the property. I would like it to default to the name of the property, but be able to read an alternate name from one of the custom attributes.
  • Anonymous
    June 22, 2004
    Nice suggestions again - do keep the feedback coming! We will consider them for Beta 2.

    About the property name: what's the scenario in which you would want a property to read from a different name in the store? Our intention was to hide the naming concerns from the user.
  • Anonymous
    June 22, 2004
    A bit of background: The application I'm working on has an existing Configuration architecture - but we realized it wasn't very nice so I set about replacing it. I looked at all the configuration systems around from Config4j to the samples on CodeProject to Whidbey.

    The Client Settings feature of Whidbey most closely mapped to my preferred architecture - so I spent a day re-implementing it purely from the docs on longhorn.msdn.microsoft.com

    So, now I have 19 classes with the same names and properties as Whidbey's Client Settings and most of the functionality.

    eg

    [ApplicationScopedSetting]
    [DefaultSettingValue("true")]
    public bool ShowLoaded
    {
    get { return (bool) this["ShowLoaded"]; }
    set { this["ShowLoaded"]=value; }
    }

    However, I also need to do something like this:

    [ApplicationScopedSetting("Facades.CustServiceWebFacade.CustServiceFacade")]
    [DefaultSettingValue("")]
    [SettingsProvider("Runtime.AppConfigProvider")]
    public string CustServiceWebFacadeURI
    {
    get { return (string) this["Facades.CustServiceWebFacade.CustServiceFacade"]; }
    }

    The things to notice about this are:
    1) It uses a non-default settings provider.
    2) The ApplicationScopedSettingAttribute accepts a parameter - which is a hint to the provider to use an alternative key - rather than simply use the Property Name.

    Why did I implement the ability to give it an alternative hint? Well, we already had an existing configuration system which read some values from the app.config file - and I didn't want the maintenance headache of upgrading the app.config on every user's machine when we release the new code. Therefore, I wanted to keep the same keys we already use in the app.config. However, some of these keys had periods in their names - and of course the CLR doesn't allow method names containing periods. So I implemented a system to tell the provider (actually the SettingsBase) to use an alternative name.

    In summary, my reason was for backwards compatibility.

    I hope this helps.
  • Anonymous
    June 23, 2004
    Interesting. You can accomplish this in our model by simply writing a custom provider that talks to your pre-existing config section. You can define a custom attribute to pass in the keys used to identify the settings.
  • Anonymous
    June 23, 2004
    That sounds a sensible solution. Of course, since I've never seen your persistence format (or even how you serialize types) then my format is probably incompatible with yours!

    (BTW - I use TypeConverters and a <Settings/> root element for the LocalFileSettingsProvider)
  • Anonymous
    June 23, 2004
    Our default provider stores settings in separate sections per settings class. We have also extended the config system to user specific config (as against machine, app config that exist today).

    Serialization is done through TypeConverters and falling back to XmlSerializer when no TypeConverter is available.

    I am not sure when all the detailed documentation will be available, but you should be able to get a clearer picture if you get a chance to install the VS 2005 Beta.

  • Anonymous
    July 12, 2005
    Hi,

    for a current 2.0 project i need to update the app config file for application wide settings. Before writing my own custom SettingsProvider i want to ask if anybody has written one that can update ApplicationScope settings as well.
  • Anonymous
    July 26, 2005
    AMAZING! I've been using the Client Settings API in Whidbey Beta 2 and I have to say this is by far one of the greatest improvements in the IDE/Framework! You guys have a lot to be proud of.

    I have one question and one suggestion.

    Question:
    Will this sort of functionality be available to WebApplications by the Nov. release?

    Suggestion:
    I love the built-in UI for the .settings file. Have you guys considered making the two types of settings (application & user) two different tabs in the project settings? That is, instead of having just a "Settings" tab, you can have one tab called "Application Settings" and one for "User Settings." That way, there's a greater logical seperation of the settings (and also reflects how they are stored in the .config file).

    Thanks, and keep up the amazing work!
  • Anonymous
    July 26, 2005
    <P>Mike-E: Thanks for the feedback and good to know you liked the feature! Regarding settings for web apps: ASP.NET 2.0 exposes the settings API through the Profiles feature, which you can find more details about here:</P>
    <P>http://msdn2.microsoft.com/library/at64shx3(en-us,vs.80).aspx</P>
    <P>The underlying infrastructure and provider model is the same, but the programming model is more suited to the web world. Your suggestion about having two separate tabs for app &amp; user scoped settings is a nice one. However, the application designer is a 'prime location' and exposes a lot of general app development features, like resources, debugging, versioning etc. We may not be able to justify having 2 tabs devoted to just settings. However, you can always add new settings files to your app and group the settings that way. </P>
  • Anonymous
    July 27, 2005
    Ah! Yes, it's true that the Profile is there, but that's User-scoped variables. Unless there's something totally obvious I'm missing, there isn't a way to have strong-typed application-wide settings for web applications.

    As far as not justifying 2 tabs for settings. Perhaps you underestimate just how important these bad boys are. :)

    I really do think that configuration has been sorely misrepresented in the Framework. It's really good to see the effort to bring it back up to speed.

    Now, the next step is to be able to display GUI for all the different sections found in a particular .config file. For instance, I'd like to be able to go through each section in my web.config and have a tabbed interface that's dynamically built from the properties found from each corresponding SectionGroup. Hand-editing XML is sooooo 2002. :)

    And yes, I see that ASP.NET Configuration does a little bit towards this, but it doesn't provide an interface that addresses all the SectionGroups within the file.

    Baby steps... :D
  • Anonymous
    July 28, 2005
    Hi Prabhu,

    How to write a custom SettingsProvider? I am asking this question as we could not able to get any links explaining it in better way. If you can provide any samples that will be helpful and also please provide us a solution how to set this custom SettingsProvider to the application, so that application reads the settings from the custom SettingsProvider rather than default LocalFileSettingsProvider

    Thanks
    MDeevi.
  • Anonymous
    July 29, 2005
    I have replied to your question here: http://blogs.msdn.com/rprabhu/archive/2005/06/29/434007.aspx
  • Anonymous
    August 26, 2005
    can i allow user to create his own settings and then later allow him to use them ?
  • Anonymous
    August 29, 2005
    Sandeep: Not sure what you mean...?
  • Anonymous
    September 14, 2006
    We have a generic configuration framework which reads all configuration information from database. I have data for config section of type "System.Configuration.ClientSettingsSection" stored in database. When I try to read the config section data from database and deserialize into System.Configuration.ClientSettingsSection, I get the error
    "There was an error reflecting type 'System.Configuration.ClientSettingsSection'.
     ----> System.InvalidOperationException : You must implement a default accessor on System.Configuration.ConfigurationLockCollection because it inherits from ICollection. "

    Should I implement custom SettingProvider to read from database and deserialize ?

    Any help is appreciated.
  • Anonymous
    September 15, 2006
    psrik: I am not sure why you are seeing that error. I recommend posting on one of the msdn forums or submitting a report through msdn feedback to follow up on this, since I no longer work on this area.

    That said, I would recommend writing a custom SettingsProvider to do what you are doing, rather than route the config section. That would be a much simpler and better tested scenario.