Gigi Labs

Please follow Gigi Labs for the latest articles.
Showing posts with label settings. Show all posts
Showing posts with label settings. Show all posts

Tuesday, April 15, 2014

C# AppSettings: Upgrading Settings between Assembly Versions

Hullo! :)

In "C# AppSettings: Saving User Preferences in a WPF Browser application", we learned how to define and use user settings in a .NET application. In this article we'll deal with a slight complication when it comes to maintaining settings between different application versions. But first, let's build a very simple application to get the point across.

After creating a new Console Application in your favourite IDE, go Add -> New Item... and add a Settings file called... well... Settings.settings! In the designer, add a setting called Name which will be a String in User scope:



In our Main() method, let us now write some code that does something with this setting.

        public static void Main(string[] args)
        {
            string name = Settings.Default.Name;
           
            if (string.IsNullOrEmpty(name))
            {
                Console.WriteLine("Hello! Who might you be?");
                Console.Write("You say: ");
                name = Console.ReadLine();
                Console.WriteLine("Pleased to meet you, {0}!", name);
               
                Settings.Default.Name = name;
                Settings.Default.Save();
            }
            else
            {
                Console.WriteLine("Hi {0}! Nice to see you again!", name);
            }
           
            Console.ReadKey(true);
        }

We're not doing anything fancy here. If the Name setting contains something, then we just output a one-liner; otherwise we ask the user for his name and then save it in the Name setting. Here's what it looks like when you run it for the first time:


And when we run it again:



Now you see, these settings are tied to the version of the program. Each project you compile has something called an assembly version. This is an internal version number assigned to each .exe and .dll file. In SharpDevelop, by default this is set to 1.0.*, which generates something like 1.0.5218.35490 (the actual value is different each time you build, because of the '*' bit). You can find the AssemblyVersion within the AssemblyInfo.cs file that is automatically created when you create a new project:


Now, since a different assembly version is generated with each build, you'll be quick to notice a problem. If you change the code (even just add a space) and build and run again, the setting you had earlier is somehow lost, and you're back to the name prompt:


The problem here is that the settings are tied not only to program and user, but also to the assembly version. Each time the assembly version changes, a new set of settings are created for the application.

We can use a fixed assembly version:

[assembly: AssemblyVersion("1.0.0.0")]

...and now even after changing code, the settings are remembered:


However, the problem remains. At some point we'll want to change that assembly version to, say, 1.0.0.1, and we don't want to lose our settings.

There is actually a solution to this problem, described in this StackOverflow answer. In a nutshell, you need to import the settings from your old application version by upgrading them.

To do this, first add a boolean setting called UpgradeRequired in your settings file, and make sure to set it to True by default:


Before we actually use this, let's change the assembly version to see that the settings are not loaded:

[assembly: AssemblyVersion("1.0.0.1")]

And sure enough, you are asked for your name once again when running the program:



Then, at the beginning of Main(), add the following code to upgrade the existing settings:

            if (Settings.Default.UpgradeRequired)
            {
                Settings.Default.Upgrade();
                Settings.Default.UpgradeRequired = false;
                Settings.Default.Save();
            }

If you now run the application, the old settings are retrieved and saved into the new settings. Note that the UpgradeRequired setting is set to false as part of the upgrade operation, so that it is done only the first time.


It works pretty nicely.

In this article, we learned about the assembly version, an internal version associated with each .dll and .exe file produced when compiling a .NET project. We also learned about the role it plays in .NET user settings, and how to upgrade them to make them survive changes in the assembly version.

Sunday, March 23, 2014

C# AppSettings: Saving User Preferences in a WPF Browser application

Hey Ya! :)

In yesterday's article, "C# AppSettings: Launching Programs and Storing AppSettings", we learned how you can store application settings in an App.config file so that they can be easily changed without having to recompile the program. This is useful for settings that don't change very often, but is not the best way to store user preferences.

In today's article, we will see how we can use a .NET Settings file in order to save user preferences. To do this, we will build a simple WPF browser application* and allow the user to save his homepage.

* For those who already know a little bit about WPF, we're just going to make a simple application that includes a WebBrowser control - not an XBAP.

So, for starters, create a new WPF application using your favourite IDE. Aside from an App.xaml, you should also have a window - this would be Window1.xaml in SharpDevelop, or MainWindow.xaml in Visual Studio.

Change the window's Title, Width and Height properties to something decent (e.g. width of 800 and height of 600). Then, replace the default <Grid> in the window's XAML with the following:

    <DockPanel>
        <DockPanel LastChildFill="True" DockPanel.Dock="Top">
            <Button Name="GoButton" DockPanel.Dock="Right" Margin="5 5 5 5" Click="Button_Click">Go</Button>
            <Button Name="SaveButton" DockPanel.Dock="Right" Margin="5 5 5 5">Save homepage</Button>
            <TextBox Name="Homepage" DockPanel.Dock="Left" Margin="5 5 5 5" />
        </DockPanel>
        <WebBrowser Name="Browser" VerticalAlignment="Stretch" />
    </DockPanel>

This isn't a WPF tutorial so don't worry about the above XAML; you'll see what it does in a second. Find the window's codebehind file by expanding your Window1.xaml (or MainWindow.xaml) node in Solution Explorer, and open it:


In the window class's constructor, add the following code after InitializeComponent():

            string homePageUrl = "http://en.wikipedia.org/wiki/Main_Page";
            this.Homepage.Text = homePageUrl;
            this.Browser.Navigate(homePageUrl);

You can now run the application to see what it looks like:


That was pretty easy! We set up the browser embedded in our window to load Wikipedia by default, and that's what it did. Now, let's get our program to actually do something.

In the XAML for the "Go" button, start typing the word "Click", then press TAB twice. This should generate the following code in your window's codebehind file (remember, it's the .xaml.cs file):

        void GoButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            throw new NotImplementedException();
        }

Replace the body of this method with the following:

            this.Browser.Navigate(this.Homepage.Text);

We can now type in a URL and press the "Go" button to navigate there:


Now, let's work on that "Save homepage" button. First, add a button handler as you did before - by starting to type the word "Click" in the XAML for the "Save homepage" button, pressing TAB, selecting "<new event handler>", and then pressing TAB again. You should have an empty handler similar to the one you had for the "Go" button earlier:

        void SaveButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            throw new NotImplementedException();
        }

Next, right click on your project in Solution Explorer, and select Add -> New Item... and add a new Settings file. If you're using SharpDevelop, you'll find this under the Misc category:


This file opens up a table where you can add settings as you like. Let's add one for the homepage:


You just need to set the name, data type, and default value of the property. The Scope can be either User or Application. This article at MSDN explains the difference:
Application-scope settings are read only, and can only be changed at design time or by altering the <AssemblyName>.exe.config file in between application sessions. User-scope settings, however, can be written at run time, just as you would change any property value. The new value persists for the duration of the application session. You can persist changes to user settings between application sessions by calling the Settings.Save method. These settings are saved in the User.config file.
In short, Application-scope settings are pretty similar to what we did yesterday in "C# AppSettings: Launching Programs and Storing AppSettings", while User-scope settings are ideal for saving user preferences - which is what we want to do now.

In your window's constructor, you can now load the homepage from your settings. You first need to build your project to allow the IDE to do some magic underneath (otherwise you won't be able to find the property. Then, you can load the homepage setting like this:

            string homePageUrl = Settings1.Default.HomePage;

The good thing about these kinds of settings are that they are strongly-typed. This means that if you're loading, for example, an integer, you can just assign it directly to an integer variable without further ado. Using regular AppSettings (see "C# AppSettings: Launching Programs and Storing AppSettings"), on the other hand, you first have to read values into strings and then convert them to the appropriate type.

Saving settings is also pretty straightforward. You assign them using the same notation as above, and then Save() them so that the settings can be remembered next time you run the application:

        void SaveButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            Settings1.Default.HomePage = this.Homepage.Text;
            Settings1.Default.Save();
           
            MessageBox.Show("Settings have been saved", "Homepage update");
        }

So now, we can run our application, enter a new URL in the homepage, and click the "Save homepage" button:


...and the next time we run our application, it loads our new homepage by default:


As you can see, the WPF WebBrowser control is actually some version of Internet Explorer - you can see it compared to a REAL browser in a previous screenshot. Still, Internet Explorer can sometimes be useful. To download another browser, for instance. :D

Cool. :) In this article, we saw how to use a .NET Settings file to load and save user preferences quickly and easily. We saw this in action by loading and saving the homepage in a simple WPF browser application. Thanks for reading, and see you in time for the next article! :)