tags:

views:

706

answers:

2

I have created some settings in a C# application using .NET 3.5 and Visual Studio 2008 Express. I have a number of application-scoped settings which I would like to be able to modify from within the application - I can access them through Properties.Settings.Default but they are read only as expected. I do not want to have to make these become user-scoped settings as they should be application-wide. Is this possible without loading the XML and reading/writing from/to it myself?

I have seen examples using System.Configuration.ConfigurationManager.OpenExeConfiguration, but the config xml looks to be in a different format to that which I am using (is this from an older version?)

Thanks


Edit

I worked out that I can modify the values doing this, but it seems like a ridiculous hack.

Configuration config = ConfigurationManager.OpenExeConfiguration(Application.ExecutablePath);
SettingElementCollection settingElements = ((ClientSettingsSection)config.GetSectionGroup("applicationSettings").Sections[0]).Settings;

SettingElement element = settingElements.Get("SettingName");
element.Value.ValueXml.InnerText = "new value";

config.Save(ConfigurationSaveMode.Modified, true);
+1  A: 

OpenExeConfiguration (or any of the other ConfigurationManager methods) is the preferred entry point for most modifications to configuration files. Once you have a Configuration instance you should get the section you wish to modify and after the modifications call any of the ConfigurationManager.Save methods. However, it is impossible to retrieve the applicationSettings section this way.

There is no API for changing settings from the applicationSettings section in your app.config file. Only user-scoped settings can be changed this way.

So actually changing these settings can only be done by directly manipulating the app.config XML file.

Some confusion may occur because the indexed property from Properties.Settings.Default is actually writable. The following is perfectly legal:

Properties.Settings.Default["MySetting"] = "New setting value";
Properties.Settings.Default.Save();

However, the setting will not be saved.

Ronald Wildenberg
And remember to allow for permissions on the config file. Expect the app.exe.config to only be writeable to administrators.
Richard
Thanks. What section would I need in order to get the settings in Properties.Settings.Default?
Tom Haigh
I see I didn't read your question well enough. The method I propose is for general changes to configuration files. I updated my answer to show another (better) way to change assembly-scoped settings.
Ronald Wildenberg
hmm, it doesn't seem to write the changes back to file. But maybe I'm being thick
Tom Haigh
Ok, seems I made a mistake, sorry for that. After further research I found out you cannot save application-scoped settings without resorting to direct manipulation of the xml.
Ronald Wildenberg
+1  A: 

You could also use the Windows Registry to store app-specific state. There are per-user and per-machine keys in the registry - either or both are available to you . For example, some people use the registry to store the location and size of the app window upon exit. Then when the app is restarted, you can position and size the window according to its last known size. This is a small example of the sort of state you can store in the registry.

To do it you would use different APIs for storage and retrieval. Specifically the SetValue and GetValue calls on the Microsoft.Win32.RegistryKey class. There may be libraries that are helpful in persisting complex state to the registry. If you have simple cases ( a few strings and numbers) then it is easy to just do it yourself.

  private static string _AppRegyPath = "Software\\Vendor Name\\Application Name";

  public Microsoft.Win32.RegistryKey AppCuKey
  {
      get
      {
          if (_appCuKey == null)
          {
              _appCuKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(_AppRegyPath, true);
              if (_appCuKey == null)
                  _appCuKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(_AppRegyPath);
          }
          return _appCuKey;
      }
      set { _appCuKey = null; }
  }


  private void RetrieveAndApplyState()
  { 
      string s = (string)AppCuKey.GetValue("textbox1Value");
      if (s != null) this.textbox1.Text = s;

      s = (string)AppCuKey.GetValue("Geometry");
      if (!String.IsNullOrEmpty(s))
      {
          int[] p = Array.ConvertAll<string, int>(s.Split(','),
                           new Converter<string, int>((t) => { return Int32.Parse(t); }));
          if (p != null && p.Length == 4)
          {
              this.Bounds = ConstrainToScreen(new System.Drawing.Rectangle(p[0], p[1], p[2], p[3]));
          }
      }
  }

  private void SaveStateToRegistry()
  {
      AppCuKey.SetValue("textbox1Value", this.textbox1.Text);

      int w = this.Bounds.Width;
      int h = this.Bounds.Height;
      int left = this.Location.X;
      int top = this.Location.Y;

      AppCuKey.SetValue("Geometry", String.Format("{0},{1},{2},{3}", left, top, w, h);
  }


  private System.Drawing.Rectangle ConstrainToScreen(System.Drawing.Rectangle bounds)
  {
      Screen screen = Screen.FromRectangle(bounds);
      System.Drawing.Rectangle workingArea = screen.WorkingArea;
      int width = Math.Min(bounds.Width, workingArea.Width);
      int height = Math.Min(bounds.Height, workingArea.Height);
      // mmm....minimax            
      int left = Math.Min(workingArea.Right - width, Math.Max(bounds.Left, workingArea.Left));
      int top = Math.Min(workingArea.Bottom - height, Math.Max(bounds.Top, workingArea.Top));
      return new System.Drawing.Rectangle(left, top, width, height);
  }

That code uses Microsoft.Win32.Registry.CurrentUser, and so it sets and retrieves user-specific app settings. If you are setting or retrieving machine-wide state, you want Microsoft.Win32.Registry.LocalMachine.

Cheeso