Alternative to ApplicationSettings in .NET

After dealing with lost settings, an unclear upgrade path, and my own confusion surrounding the magic of Settings in a .NET client application, I decided to build my own.

You’re probably familiar with this UI in Visual Studio. It hasn’t changed much since it was first created:

image

A list of properties, data type, scope and a default value. Admittedly, it makes things simple. However, with my WPF .NET application that I created a few years ago (SnugUp), I’ve always been troubled by the magic of the settings. It was too easy to get in a situation where a user would loose their settings doing uninstalls, reinstalls, upgrades.

While I’m sure it’s possible to make the built in settings classes to work, it wasn’t worth the effort for me to understand them and learn what the nuances of where they’re placed, how to do a decent upgrade, how not to loose them, etc.

In the SnugUp WPF UI, the code uses a two-way bindings to directly edit the settings of the application (like: "{Binding AppSettings.DebugMode}"). It was simple, and all I needed. It’s handy that ApplicationSettingsBase implements the INotifyPropertyChanged interface which WPF needs for simple two-way data bindings.

My solution, which I admit is heavier than the original as it requires a large additional assembly is to use JSON.NET as the serializer/deserializer for a new settings class I created.

So, the basic pattern:

   1: public class ApplicationSettings : INotifyPropertyChanged

   2: {

   3:     public event PropertyChangedEventHandler PropertyChanged;

   4:  

   5:     private bool _debugMode;

   6:     private string _albumNameFormat;

   7:     private string _extraFileExtensions;

   8:     private bool _automaticRun;

   9:     private string _galleryCreationSubCategory;

  10:     private bool _filenameOnlyCheck;

Properties created the standard way for INotifyPropertyChaged:

   1: private DateTime _nextUpdateCheck;

   2: public DateTime NextUpdateCheck

   3: {

   4:     get { return _nextUpdateCheck; }

   5:     set

   6:     {

   7:         if (_nextUpdateCheck != value)

   8:         {

   9:             _nextUpdateCheck = value;

  10:             RaisePropertyChanged("NextUpdateCheck");

  11:         }

  12:     }

  13: }

I wanted a predictable path for storing settings (so it would be easy to document and backup for users). I used the AssemblyCompany attribute and the AssemblyProduct attribute as the folder names:

   1: internal static string GetSettingsDirectory()

   2: {

   3:     string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);

   4:     var attrs = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);

   5:     if (attrs.Length == 1)

   6:     {

   7:         path = Path.Combine(path, ((AssemblyCompanyAttribute)attrs[0]).Company);

   8:  

   9:     }

  10:     attrs = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);

  11:     if (attrs.Length == 1)

  12:     {

  13:         path = Path.Combine(path, ((AssemblyProductAttribute)attrs[0]).Product);

  14:     }

  15:     return path;              

  16: }

In this WPF application, in the AssemblyInfo.cs file, the attributes are set as follows:

   1: [assembly: AssemblyCompany("WiredPrairie.us")]

   2: [assembly: AssemblyProduct("SnugUp")]

On my machine, that maps to this path:

d:\Users\Aaron\AppData\Roaming\WiredPrairie.us\SnugUp\

Loading settings then is straightforward using JSON.NET:

   1: public static ApplicationSettings Load(string filename)

   2: {

   3:     ApplicationSettings settings = null;

   4:     var directory = GetSettingsDirectory();

   5:     var path = Path.Combine(directory, filename);

   6:  

   7:     if (File.Exists(path))

   8:     {

   9:         string fileData = File.ReadAllText(path);

  10:         try

  11:         {

  12:             settings = JsonConvert.DeserializeObject<ApplicationSettings>(fileData, new JsonSerializerSettings { });

  13:         }

  14:         catch { }

  15:     }

  16:     if (settings == null)

  17:     {

  18:         settings = new ApplicationSettings();

  19:         SetDefaults(settings);

  20:         // initialize settings once

  21:         Save(settings, filename);

  22:     }

  23:     return settings;

  24: }

In my code, if the settings file didn’t exist or fails to serialize into something meaningful, a new settings file is created with a few defaults. (I haven’t decided what to do when there’s an exception when reading the file, hence the empty catch).

Saving the settings is just as easy:

   1: public static void Save(ApplicationSettings settings, string filename)

   2: {

   3:     Debug.Assert(settings != null);

   4:     var directory = GetSettingsDirectory();

   5:     var path = Path.Combine(directory, filename);

   6:  

   7:     JsonConvert.SerializeObject(settings);

   8:  

   9:     if (!Directory.Exists(directory))

  10:     {

  11:         Directory.CreateDirectory(directory);

  12:     }

  13:  

  14:     var fileData = JsonConvert.SerializeObject(settings, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate });

  15:     try

  16:     {

  17:         using (StreamWriter writer = File.CreateText(path))

  18:         {

  19:             writer.Write(fileData);

  20:             writer.Close();

  21:         }

  22:     }

  23:     catch { }

  24: }

The SerializeObject method returns a string which is then written to a file using a StreamWriter.

I added a Save method to the instance of the ApplicationSettings:

   1: public void Save()

   2: {

   3:     ApplicationSettings.Save(this);

   4: }

This preserved the functionality that exists in the built in Settings support in .NET (which was being used in my application).

By keeping all of the property names the same and making a few tweaks to the types of some fields in my application, I had swapped out the entire “settings” infrastructure in about 45 minutes.

I’m planning some other JSON activities within my application, so the overhead of using JSON.NET is acceptable.

The best part of this alternative is that there isn’t any magic. It’s all easy to manage. Further, I can easily modify my installer to properly handle/update, etc., the settings file with just a few clicks.

I’m not going back to the built-in .NET settings support again. I’ve learned my lesson.  Smile

What have you done for “user” settings?

Announcing SnugUp version 2

imageMore than a few years ago, I created SnugUp version 1, which is a handy way of synchronizing folders of images with SmugMug for Windows users. I’ve made a number of changes in the last month based on some requests and the result is a significant update (yet the core features are all there – just improved!).

New features:

  • (Changed to version 2)
  • Completely revamped look and feel (which did remove a few ‘flashy’ features)
  • Uses Click once for application updating (which should make it easier to push updates and bug fixes out to users)
  • Added settings for automatic upload (when application starts)
  • Added setting for Subcategory selection (only selection, no creation of subcategories through SnugUp)
  • Added setting for custom gallery naming
  • Completely changed settings user interface to more logically group and explain settings
  • Fixed a number of bugs (that shouldn’t have appeared to user anyway!)
  • Added support for new file extensions (to upload videos in particular)

It does require .NET 4.0 Framework Client profile (which likely you’ve already got on your machine, but if you don’t the installer SHOULD make it easy to download).

Go here (http://www.wiredprairie.us/SnugUp) for more information and to download. I’d recommend uninstalling the old version first.

SNAGHTML1ffa6494

Nest Thermostat Review, Update #9

Summary/Index

When I woke up this morning, I decided that I’d use the remote features of my Nest Thermostat to increase the temperature of the first floor as the normal schedule hadn’t started yet.

Here’s what I saw on my iPad:

image

Basement: ?

First Floor: ?

When I tapped the Basement image, this alert was displayed:

image

“Thermostat Disconnected: The thermostat Basement last connected to nest.com more than 7 hours ago.”

What?

I next checked the First Floor. Thankfully, it said that it had only been 17 minutes since it last connected. I’ve seen that issue before and it usually resolves. But, I’ve never seen one go more than about 50 53 minutes without reporting in.

The fact that I was using the iPad and the Nest application meant that WiFi Internet was available in our house.

I went to the Basement thermostat and noticed this glaring issue after clicking through to settings:

image

What?  Seriously?

As I wasn’t sure what the best option was at 6:45AM for support, I decided to re-add the thermostat to our account. The thermostat had no trouble accessing the Nest cloud and obtaining one of the one time connection keys. (So, I maintain, it’s not general Internet connectivity issues).

After deleting and adding it back, the web site still reported it as MIA. So, I tried a reset:

image

After resetting, and waiting several minutes after it had completely restarted, it appeared again on the Nest web site.

However, 3 hours later:

image

It’s again, gone missing.

So, I called support this fine Sunday morning and talked to one of the same support engineers I’ve spoken with in the past (“DK”). I explained the general problem (and emphasized that my biggest concern was that it had lost my account information), my “solution”, and said that it was again not reporting in. In a typical (somewhat ironic) support fashion, as I was explaining the issue and walking to the basement, the thermostat reported in successfully to the Nest cloud.

He had me drop the account, add it to my account, and restart it again. He said that resolves the problem in about 70% of the cases. In the other 30%, he mentioned that they often manually update the firmware and that will help. [ugh]

I’m going to keep an eye on it and will add more details as needed/available.

 

If you want to talk and discuss more about digital thermostats with others, I’d suggest here: digtstat.com (it’s a web site I created to help provide a better place to have discussions about the Nest thermostat).

Macbook Pro battery fails to charge

The non replaceable battery on my Macbook Pro (2010) had discharged recently completely as I’d left the laptop unplugged for more than a month without turning it on.

When I went to use it this morning, I plugged it in, and then turned it on. After a few minutes of use, the battery status still showed as “not charging.”

image

The light on the mag-safe connection was green, as if the battery was completely charged.

When I depressed the battery indicator button on the side of the MacBook Pro, the response was 5 quick green flashes on the first LED. According to Apple support, that indicates the battery hasn’t been charged to what’s required for a single indicator light yet. However, while my MacBook Pro had discharged like this before, it would normally start charging right away.

So, apparently, before taking it in for a repair (which is what a lot of people in forums were recommending), you might try resetting the System Management Controller. One of the issues that a reset could fix is that the battery does not appear to be charging properly.

To reset the controller, perform these steps (as documented on the Apple Support web site):

  1. Shut down your laptop completely.
  2. Plug in the MagSafe power adapter to a power source, connecting it to the MacBook if its not already connected.
  3. On the built-in keyboard, press the (left side) Shift-Control-Option keys and the power button at the same time.  [Wow, it’s awkward to do that! I pressed the S-C-O keys first and then the power button and it worked]
  4. Release all the keys and the power button at the same time.
  5. Press the power button to turn on the computer. 
    Note
    : The LED on the MagSafe power adapter may change states or temporarily turn off when you reset the SMC.

A few moments after I performed the steps above, the mag safe LED light switched to an orange color (charging) and the battery indicator no longer blinked 5 times quickly.

Problem resolved, and much simpler than taking it to a local Apple “Genius” bar.

As this wasn’t an obvious fix, I’ve decided to put this on my blog in the hope that someone else might find it useful, and so when I have this happen again, I don’t need to go hunting for the solution! Smile

Announcing digtstat.com

Last week Johnk suggested in a comment that someone in the user community should set up a forum for discussion of the Nest thermostat.

I have done just that this afternoon. Smile

It’s brand new, and hasn’t had the tires kicked much yet (just the absolute basics), but I thought I’d put something out there as the comments and discussion have outgrown my installation of WordPress (and it’s ability to nicely manage a discussion effectively about multiple topics). I really enjoy the conversation and thought it might be best provided in a different forum (pun intended).

It’s here: digtstat.com. (It’s short for Digital Thermostat)

If there’s enough traffic to warrant it, I’ll definitely promote some others as moderators, as it will be kept a civil location for discussions about digital thermostats.

Enjoy.

image