Monthly Archives: April 2014

Persisting User Config Settings – Including Deployments With ClickOnce

Wow! You let me customize that?

Very often I have had to allow users to make changes to the UI like column widths, font sizes, colors etc. When I discovered that I could set up defaults by editing the solutions app.config I was pretty psyched. Unfortunately the mood was soured when complaints came in from my users that all their precious custom settings would get overridden every time I deployed a new release. 🙁

No Way! … Way!

Sure enough, every time the project was re-compiled and deployed the click once package would override all config values to the pre-set values in the app.config. This could end up being really annoying for the users, especially if they had spent time setting up connection parameters and path variables.

My first instinct was to devise a cool custom set of methods that would handle persisting and retrieving some XML that would hold my apps settings. I knew it would need to be smart enough to choose between “defaults” I could include in the deployment and values the user set at their particular installation.

Once I started to realize how tedious it would be to code the “smart” logic of juggling defaults vs. user settings. I quickly lost interest and searched for a better way.

An added level of complexity of when you install an app via “Click Once” is that Visual Studio will create and deploy your app to a directory somewhere on the users hard drive and give it a hashed name that you can’t possibly predict. So, if you are planning on implementing some sort of custom config files for your app, you will have to manage setting a path to some custom location for your app or figure out how to ask .NET where the heck it installed the .exe. I figured .NET must know how to find where it deployed my app so there has to be a way for me to get that info too.

Configure This

I found quite a bit of information on the ConfigurationManager object but it left me really confused. Amazingly, I didn’t find one (simple) example of how to manage the user settings. Instead I found tons of articles on how to create my own Custom Settings Sections etc.  I just couldn’t believe that Microsoft didn’t have some built in scheme for managing user configs.

Well of course they did! And here is how I ended up implementing it.

Use Case: App executes and retrieves config values from app.config. User sets values as desired. App is executed subsequently and uses the new settings as set by the user.

 Solution

  • Set a Reference to System.Configuration
  • In VS go to Project/App Name Properties. Select the Settings Tab
      • Set up your user config settings as desired. Make sure to set the Scope to USER!
      • What’s really awesome about setting these config variables up is that they will appear in the intellisense!  Nice one Microsoft!
  • Observe the XML of the app.config and you will see that a new section has been added.
  <userSettings>
    <UserConfigDeploymentSample.Properties.Settings>
      <setting name="ID" serializeAs="String">
        <value>OriginalID</value>
      </setting>
      <setting name="Info" serializeAs="String">
        <value>Original Information</value>
      </setting>
    </UserConfigDeploymentSample.Properties.Settings>
  </userSettings>
  •  You access these settings using the Properties object. As I said, this is awesome because config keys will be provided by the intellisense.
IdTextBox.Text = Properties.Settings.Default.ID; // .NET automatically makes named properties of the user settings
  •  When you invoke the Save() method of the Properties object, .NET will create a “special” folder with your user.config file!
// Note you will have needed to set at least one key to cause the user.config to be persisted
Properties.Settings.Default.ID = "Custom ID Value";
// this creates the special directory and user.config file (if it doesn't exist yet)
Properties.Settings.Default.Save();
  • Going forward, all Properties.Settings.Default.xxx will return the values in the user.config not the original app.config user settings values!
  • Finally, you can use the ConfigurationManager to get the path to where .NET is storing the config files. Even if it was click once!
// Where the exe is running -- very useful in the case of ClickOnce
System.IO.Path.GetDirectoryName(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath);
// Where the custom user.config will be stored 
UserLevelPerUserRoamingAndLocalConfigPath.Text = System.IO.Path.GetDirectoryName(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath);

Sample App:

[download id=”73″]

Fun!

 

Help! My app is minimized and my dialog won’t pop up!

Recently, one of our clients complained that they weren’t “adequately” notified when some of their trading orders stopped. The big problem with this is that they only noticed the Idle orders near the end of the trading day.  Apparently, they didn’t see the awesome “Errors and Exceptions” dialog that is supposed to pop up anytime the server throws an exception on an order.

Don’t Minimize Me Bro!

After I let the trader vent his frustration, I tried to do some detective work and recreate the situation and figure out what happened. Sure enough the user found a use case I hadn’t considered before. To save screen real estate he had minimized the app and left it on auto-pilot.

The code that responded to the error messages and displayed the Errors Dialog worked correctly but will not show a dialog if the parent window is minimized.

Who’s Your Daddy?

Some searching of the interwebs suggested that I set the dialog windows Topmost = “True” but that wasn’t enough to get the job done.  I figured that it probably had to do with the fact that I was assigning the ErrorDialog.Owner to the MainWindow and since the MainWindow was minimized it must be forcing all its “children” to be minimized as well.

The Code 🙂

I severed the relationship between the two windows and then I added an “else” so that if the user had minimized the Error Dialog instead of closing it, it would still pop up without creating additional Dialogs.

        public void ViewErrorListManager(object param)
        {            
            // Can only call from Main Dispatcher so check before proceeding and switch if necessary
            // Note the best way to check if the UI thread is active is to use .CheckAccess()
            // Dispatcher.CurrentDispatcher.Thread == Thread.CurrentThread is NOT 
            // a good way to check for UI thread. This is becasue Dispatcher.CurrentDispatcher will
            // create a new Dispatcher associated with the current Thread. This is not what you want!
            if (Application.Current.Dispatcher.CheckAccess())
            {
                // check if window is already open
                if (!Application.Current.Windows.OfType<SystemAlertDialog>().Any())
                {
                    SystemAlertDialog dlg = new SystemAlertDialog();
                    dlg.DataContext = _alertVM;
                    dlg.Show();
                }
                else
                {
                    SystemAlertDialog dlg = Application.Current.Windows.OfType<SystemAlertDialog>().First();
                    if (dlg != null)
                    {
                        dlg.WindowState = WindowState.Normal;
                        dlg.Focus();
                    }
                }
            }
            else
            {
                // Invoke the call from the "Main Dispatcher"
                // Note this.Dispatcher is not valid on a non Window class
                // Note Dispatcher.CurrentDispatcher.BeginInvoke() will also not work 
                // because only the main UI thread has a message queue and pump
                Application.Current.Dispatcher.BeginInvoke(new Action(() => ViewErrorListManager(new object())));
            }
        }

Yes!