The most simple way to implement settings in a C# Windows application

I've written a number of small Windows applications (usually using WinForms, but occasionally WPF). In developing an application for users you eventually get to the point where you want to save a user's settings. Most of the time, this means writing to a file somewhere.

By Googling "winforms settings" (or some other similar query) you'll quickly find out that there's a built-in way to do this that is pretty simple. Once you set up a setting, you can simply call it throughout your project by using Properties.Settings.Default.[SettingName]

Now here are the reasons why I'm proposing something different:
  • Sometimes you need to be able to change the settings by modifying the settings file itself (rather than through the program's UI). This could be because a certain setting is causing the program to crash before it opens, it will lead to unwanted behavior upon launch, etc
  • By creating your own, custom settings file you can choose where it will be saved. This leads to a great deal of flexibility, but also allows you to jump into creating a folder for your program in the user's AppData folder (where you can start putting other things like log files, credentials, temporary files, etc)
  • A custom settings file leads to easier sharing of settings between two different computers (if you want to make sure two computers have the same settings)
  • Creating a new setting is slightly faster with my implementation

To implement mine, you can simply add a new .cs file to your project like this (Remember: change "namespace TestProject" to your appropriate project's namespace):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System;
using System.IO;
using System.Xml.Serialization;

namespace TestProject
{
    public class Settings
    {
 private static string applicationName = "Your Application";
 private static string userSettings = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), applicationName);
        private static string serializationPath = Path.Combine(UserSettings, "Settings.xml");

        #region Intialize settings with default values
        public bool Test = false;
        public string AnotherSetting = "this setting";
        public double ADouble = 5.3;
        public static Settings Instance = new Settings();
        #endregion Intialize settings with default values
  
        public static void SaveSettings()
        {
            TextWriter writer = new StreamWriter(serializationPath);
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(Settings));
            xmlSerializer.Serialize(writer, Instance);
            writer.Close();
        }

        public static void ReadSettings()
        {
            if (File.Exists(serializationPath))
            {
                FileStream fileStream = new FileStream(serializationPath, FileMode.Open);
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(Settings));
                try { Instance = (Settings)xmlSerializer.Deserialize(fileStream); }
                catch { }
            }
        }
    }
}

So there's a few things to cover here. We'll go through this chunk by chunk.

First you'll notice lines 14-16 that define where our settings file is going to be saved. You can condense these into one line if you like or even change it up altogether. But this is the route I like to take (obviously change "Your Application" to fit the name that you are going to use for your application folder).

Next we have lines 19-22 that define our settings. This is where you'll make the majority of your changes. The great part about this is in order to add a setting, you just have to create another variable for it here (and initialize it with a default value). As examples, I have a bool, string, and double setting. Yes, it is necessary to keep the "Instance" variable - we'll get to that in a second.

Finally, we have the rest of the file: the SaveSettings() and ReadSettings() methods (you can rename these if you like). The purpose of these is to save your settings to a file or to read the settings from a file. Ideally, one of the first things your program does would be to call the ReadSettings method and get the user's settings. And every time a setting gets changed (and you also want that change reflected in the file) you'll call SaveSettings. Pretty simple to use!

The great thing about this method is that it uses the XmlSerializer class. XmlSerializer is great because it is type-independent. Meaning that the resulting Settings.xml for our file here will look like this:


1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Test>false</Test>
  <AnotherSetting>this setting</AnotherSetting>
  <ADouble>5.3</ADouble>
</Settings>

That's it! All of it! You may notice that nowhere does the xml state that "Test" is a bool or "AnotherSetting" is a string. The values are simply stored plainly and the type cast happens when ReadSettings is called. Speaking of which, here are examples of how you would use this:


1
2
3
4
5
Settings.ReadSettings(); //Read the settings from xml file
Settings.Instance.Test = true; //Set "Test" to true
Settings.Instance.AnotherSetting = "hi"; //Set "AnotherSetting to "hi"
Settings.Instance.ADouble *= 5; //Set "ADouble" to 5x its current value
Settings.SaveSettings(); //Save the settings to xml file

Notice that you have to use "Instance" when reading/writing a particular setting's value. This "Instance" is required for the XmlSerializer to work. (You could just call Settings.Test for example, but you would only get the default value - if you try to write to Settings.Test it won't get saved to the xml).

As a final note, notice the try{} catch{} in the ReadSettings method. This is in case one of the settings in the xml gets set to the wrong type (e.g. <ADouble>hi</ADouble>). This isn't a problem in the xml, but throws an exception when the XmlSerializer tries to type cast it back to a double. You can put some sort of log call, Console.WriteLine, etc in the catch{} and even add a finally{} with some different settings set. But as the code stands right now, it will simply fail to overwrite the Instance and instead the Instance will be set to its default value (which is a newly-initialized version of this class, containing the default values for all settings).

Hope this helps you out! Please let me know if you have any questions

Using the Google Calendar API to edit recurring events alongside regular events

I've been using the Google Calendar API a lot for my program Bulk Edit Calendar Events. Here's something I learned today when trying to edit recurring events:

Let's say you have recurring events like this:

  • Event 1 (June 1st)
  • Event 2 (June 2nd)
  • Event 3 (June 3rd)
(It's a single event that repeats every day until there are 3 of itself)

There are two ways to edit recurring events: as individual events or as a single event.

As a single event

Google has one recommendation for how to do this: basically, when you are doing your Events List request you should set "singleEvents" to false (which it is by default). Note that here, "singleEvents" refers to "instances" (whereas I'm using it to refer to the original recurring event). Important note: if "singleEvents" is false, "orderBy" cannot be set to "startTime". My guess for why is that if a recurring event is shown as one event, it is "fuzzy" how it should be ordered with other events if all of its instances have different start times.

I have a different recommendation. Since I am editing both regular events and recurring events at the same time, I give the user the option how they want to edit the recurring events (it's a setting in my program): again, as individual events or as a single event.

So I simply get a list of events (with "singleEvents" set to "true" and "orderBy" set to "startTime". I think this makes the most sense to the user as this is what is shown in their calendar) and loop through that list of events. When I come across a recurring event (checked by !string.IsNullOrEmpty(event.recurringEventId)) and I want to edit recurring events as one single event, I call a Events Get request on that recurringEventId (this gives me the original recurring event).  Then I use that resulting event to replace the current event in the loop. I also store that recurringEventId for later in a list so that when I loop through the other instances of that event that the user selected, I can simply skip them.

As individual events

Obviously this method breaks up the events so that they are separated from the original series (but sometimes that is what is desired).

Here we're going to skip the "Events Get" request and simply change some properties of the event. We're going to set the event's recurringEventId to "" (empty string) and set the event's originalStartTime to null. These changes are what break the recurrence, but they are necessary in order to not hit exceptions in some of the other API requests (especially when we're calling Events Update on the event later).


With these two methods we can simply loop through a list of events (including all the instances of a recurring event) and still editing recurring events as the user pleases!

Hope this helps someone out!

double.NaN == double.NaN will never be true

Something I came across recently was this oddity:

double.NaN == double.NaN will never be true.

Basically, according to wikipedia, "Not a Number" will never equal anything - not even itself. This is an interesting take on the idea of "nothing" or "unassigned" since null == null returns true. So, as a solution, we have been given double.IsNaN(double)

#FunFact

How I downloaded all my photos from Photobucket

I used to be a photographer. Did I take good pictures? No. Did I like taking pictures? Yes. So I took lots of pictures, but none very good. ...