C# - approach for saving user settings in a WPF application? - c#

What approach do you recommend for persisting user settings in a WPF windows (desktop) application? Note that the idea is that the user can change their settings at run time, and then can close down the application, then when starting up the application later the application will use the current settings. Effectively then it will appear as if the application settings do not change.
Q1 - Database or other approach? I do have a sqlite database that I will be using anyway hence using a table in the database would be as good as any approach?
Q2 - If Database: What database table design? One table with columns for different data types that one might have (e.g. string, long, DateTime etc) OR just a table with a string for the value upon which you have to serialize and de-serialize the values? I'm thinking the first would be easier, and if there aren't many settings the overhead isn't much?
Q3 - Could Application Settings be used for this? If so are there any special tasks required to enable the persistence here? Also what would happen regarding usage of the "default" value in the Application Settings designer in this case? Would the default override any settings that were saved between running the application? (or would you need to NOT use the default value)

You can use Application Settings for this, using database is not the best option considering the time consumed to read and write the settings(specially if you use web services).
Here are few links which explains how to achieve this and use them in WPF -
User Settings in WPF
Quick WPF Tip: How to bind to WPF application resources and settings?
A Configurable Window for WPF

Update: Nowadays I would use JSON.
I also prefer to go with serialization to file. XML files fits mostly all requirements. You can use the ApplicationSettings build in but those have some restrictions and a defined but (for me) very strange behavior where they stored. I used them a lot and they work. But if you want to have full control how and where they stored I use another approach.
Make a class Somewhere with all your settings. I named it MySettings
Implement Save and Read for persistence
Use them in you application-code
Advantages:
Very Simple approach.
One Class for Settings. Load. Save.
All your Settings are type safe.
You can simplify or extend the logic to your needs (Versioning, many Profiles per User, etc.)
It works very well in any case (Database, WinForms, WPF, Service, etc...)
You can define where to store the XML files.
You can find them and manipulate them either by code or manual
It works for any deployment method I can imagine.
Disadvantages:
- You have to think about where to store your settings files. (But you can just use your installation folder)
Here is a simple example (not tested)-
public class MySettings
{
public string Setting1 { get; set; }
public List<string> Setting2 { get; set; }
public void Save(string filename)
{
using (StreamWriter sw = new StreamWriter(filename))
{
XmlSerializer xmls = new XmlSerializer(typeof(MySettings));
xmls.Serialize(sw, this);
}
}
public MySettings Read(string filename)
{
using (StreamReader sw = new StreamReader(filename))
{
XmlSerializer xmls = new XmlSerializer(typeof(MySettings));
return xmls.Deserialize(sw) as MySettings;
}
}
}
And here is how to use it. It's possible to load default values or override them with the user's settings by just checking if user settings exist:
public class MyApplicationLogic
{
public const string UserSettingsFilename = "settings.xml";
public string _DefaultSettingspath =
Assembly.GetEntryAssembly().Location +
"\\Settings\\" + UserSettingsFilename;
public string _UserSettingsPath =
Assembly.GetEntryAssembly().Location +
"\\Settings\\UserSettings\\" +
UserSettingsFilename;
public MyApplicationLogic()
{
// if default settings exist
if (File.Exists(_UserSettingsPath))
this.Settings = Settings.Read(_UserSettingsPath);
else
this.Settings = Settings.Read(_DefaultSettingspath);
}
public MySettings Settings { get; private set; }
public void SaveUserSettings()
{
Settings.Save(_UserSettingsPath);
}
}
maybe someone get's inspired by this approach. This is how I do it now for many years and I'm quite happy with that.

You can store your settings info as Strings of XML in the Settings.Default. Create some classes to store your configuration data and make sure they are [Serializable]. Then, with the following helpers, you can serialize instances of these objects--or List<T> (or arrays T[], etc.) of them--to String. Store each of these various strings in its own respective Settings.Default slot in your WPF application's Settings.
To recover the objects the next time the app starts, read the Settings string of interest and Deserialize to the expected type T (which this time must be explcitly specified as a type argument to Deserialize<T>).
public static String Serialize<T>(T t)
{
using (StringWriter sw = new StringWriter())
using (XmlWriter xw = XmlWriter.Create(sw))
{
new XmlSerializer(typeof(T)).Serialize(xw, t);
return sw.GetStringBuilder().ToString();
}
}
public static T Deserialize<T>(String s_xml)
{
using (XmlReader xw = XmlReader.Create(new StringReader(s_xml)))
return (T)new XmlSerializer(typeof(T)).Deserialize(xw);
}

The long running most typical approach to this question is: Isolated Storage.
Serialize your control state to XML or some other format (especially easily if you're saving Dependency Properties with WPF), then save the file to the user's isolated storage.
If you do want to go the app setting route, I tried something similar at one point myself...though the below approach could easily be adapted to use Isolated Storage:
class SettingsManager
{
public static void LoadSettings(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
{
EnsureProperties(sender, savedElements);
foreach (FrameworkElement element in savedElements.Keys)
{
try
{
element.SetValue(savedElements[element], Properties.Settings.Default[sender.Name + "." + element.Name]);
}
catch (Exception ex) { }
}
}
public static void SaveSettings(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
{
EnsureProperties(sender, savedElements);
foreach (FrameworkElement element in savedElements.Keys)
{
Properties.Settings.Default[sender.Name + "." + element.Name] = element.GetValue(savedElements[element]);
}
Properties.Settings.Default.Save();
}
public static void EnsureProperties(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
{
foreach (FrameworkElement element in savedElements.Keys)
{
bool hasProperty =
Properties.Settings.Default.Properties[sender.Name + "." + element.Name] != null;
if (!hasProperty)
{
SettingsAttributeDictionary attributes = new SettingsAttributeDictionary();
UserScopedSettingAttribute attribute = new UserScopedSettingAttribute();
attributes.Add(attribute.GetType(), attribute);
SettingsProperty property = new SettingsProperty(sender.Name + "." + element.Name,
savedElements[element].DefaultMetadata.DefaultValue.GetType(), Properties.Settings.Default.Providers["LocalFileSettingsProvider"], false, null, SettingsSerializeAs.String, attributes, true, true);
Properties.Settings.Default.Properties.Add(property);
}
}
Properties.Settings.Default.Reload();
}
}
.....and....
Dictionary<FrameworkElement, DependencyProperty> savedElements = new Dictionary<FrameworkElement, DependencyProperty>();
public Window_Load(object sender, EventArgs e) {
savedElements.Add(firstNameText, TextBox.TextProperty);
savedElements.Add(lastNameText, TextBox.TextProperty);
SettingsManager.LoadSettings(this, savedElements);
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
SettingsManager.SaveSettings(this, savedElements);
}

Apart from a database, you can also have following options to save user related settings
registry under HKEY_CURRENT_USER
in a file in AppData folder
using Settings file in WPF and by setting its scope as User

In my experience storing all the settings in a database table is the best solution. Don't even worry about performance. Today's databases are fast and can easily store thousands columns in a table. I learned this the hard way - before I was serilizing/deserializing - nightmare. Storing it in local file or registry has one big problem - if you have to support your app and computer is off - user is not in front of it - there is nothing you can do.... if setings are in DB - you can changed them and viola not to mention that you can compare the settings....

You can use SQLite, a small, fast, self-contained, full-featured, SQL database engine. I personally recommend it after trying settings file and XML file approach.
Install NuGet package System.Data.SQLite
which is an ADO.NET provider for SQLite.
The package includes support for LINQ and Entity Framework
Overall you can do many things with such supporting features to your settings window.
1.Install SQLite
2.Create your database file
3.Create tables to save your settings
4.Access database file in your application to read and edit settings.
I felt this approach very much helpful for application settings, since i can do adjustments to database and also take advantage of ADO.Net and LINQ features

I typically do this sort of thing by defining a custom [Serializable] settings class and simply serializing it to disk. In your case you could just as easily store it as a string blob in your SQLite database.

In all the places I've worked, database has been mandatory because of application support. As Adam said, the user might not be at his desk or the machine might be off, or you might want to quickly change someone's configuration or assign a new-joiner a default (or team member's) config.
If the settings are likely to grow as new versions of the application are released, you might want to store the data as blobs which can then be deserialized by the application. This is especially useful if you use something like Prism which discovers modules, as you can't know what settings a module will return.
The blobs could be keyed by username/machine composite key. That way you can have different settings for every machine.
I've not used the in-built Settings class much so I'll abstain from commenting. :)

I wanted to use an xml control file based on a class for my VB.net desktop WPF application. The above code to do this all in one is excellent and set me in the right direction. In case anyone is searching for a VB.net solution here is the class I built:
Imports System.IO
Imports System.Xml.Serialization
Public Class XControl
Private _person_ID As Integer
Private _person_UID As Guid
'load from file
Public Function XCRead(filename As String) As XControl
Using sr As StreamReader = New StreamReader(filename)
Dim xmls As New XmlSerializer(GetType(XControl))
Return CType(xmls.Deserialize(sr), XControl)
End Using
End Function
'save to file
Public Sub XCSave(filename As String)
Using sw As StreamWriter = New StreamWriter(filename)
Dim xmls As New XmlSerializer(GetType(XControl))
xmls.Serialize(sw, Me)
End Using
End Sub
'all the get/set is below here
Public Property Person_ID() As Integer
Get
Return _person_ID
End Get
Set(value As Integer)
_person_ID = value
End Set
End Property
Public Property Person_UID As Guid
Get
Return _person_UID
End Get
Set(value As Guid)
_person_UID = value
End Set
End Property
End Class

Related

How do I save multiple user settings in runtime in C#

I am trying to make my app stay the way I left it after closing the app. Therefore I want to save set of items from ListView to the settings and I can't figure out how to do that. I've found some solutions but I believe they are outdated as they don't seem to work.
Image shows set of items in ListView which I want to save so they appear there after restarting the app:
Items
This is where I want them to appear:
Settings
And this is part of code that I've tried out so far
private void btn_SaveConfig_Click(object sender, EventArgs e)
{
int i = 0;
Settings.Default["IP"] = tBox_discoverServerFrom.Text;
Settings.Default["DiscoveredServers"] = cBox_discoveredServers.Text;
foreach (var item in lV_groups.Items)
{
var property = new System.Configuration.SettingsProperty("Group"+i);
property.PropertyType = typeof(string);
property.Name = "Group " + i;
Settings.Default.Properties.Add(property);
Settings.Default.Save();
i++;
}
}
I do not think using the Settings API is a great idea if you want to save any significant amount of data.
I would recommend the following
Create a class describing all the data you want to save. To make serialization easier it should have a parameter less constructor and public setters for all properties. This is sometimes called a Data-Transfer-Object (DTO)
Use json to serialize the object to a file. You would normally place the file in a subfolder in the local app data folder: Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData).
Do the reverse when you start the application. If there is a file, use Json to deserialize it and use it however you want.
You may optionally add logic to save the file periodically, this would allow recovery in case of application crashes. You might also want some system to keep more than one file, in case the application or computer crashes in the middle of a save operation, and the file becomes corrupt.

How do I use the 'Project Properties>Settings'?

I want to set a cache limit for my C# program so I decided to use the Project Properties>Settings function of Visual Studio [2015] to do so.
I had some help and was told to enter this.
My settings I want are as follows:
Folder Path- C:\SysApp
Size Limit- 150MB
Amount to Delete- 149MB
For the sizeLimit and toDelete sections I need to know what unit (ie. bytes, megabytes, kilobytes...) they're in so I can convert them to what I listed above.
I was also told that
If you change the settings value in the program you need to save the new values before exiting the application. This is done with Properties.Settings.Default.Save();. This command creates a .config file with your values.
I need to know where in my coding to insert the Properties.Settings.Default.Save(); command.
Screenshots would be very helpful. Thanks.
The is no possibility to store metadata like units into default settings. You have to define the unit (kB, MB,...) the user should enter or store it as a string (e.g. 150MB) and parse it yourself.
The Save method must be called after setting the values (example):
Properties.Settings.Default.sizeLimit = 150000
Properties.Settings.Default.Save();
If you only want to read the settings (see comments below) change the scope of the settings from "User" to "Application" and read the settings in your program like this:
class Program {
void main(string args[]) {
String folderPath = Properties.Setings.Default.folder;
int folderSizeLimit = Properties.Setings.Default.sizeLimit;
int amountToDelete = Properties.Setings.Default.toDelete;
DeleteOldFilesIfOverFolderLimit(folderPath, folderSizeLimit, amountToDelete);
}
private private void DeleteOldFilesIfOverFolderLimit(string folderPath,
long folderSizeLimit,
long amountToDelete)
...... from other post .....
}
}

MVC Caching of frequently accessed data that would very infrequently change

I'm new to the concept of caching data and I wanted to ask if there are any issues I"m overlooking with the strategy I"m building for caching data in my MVC web application from my database. I want to stress that basically the data I'm looking at caching is practically read only, and would only be updated very very infrequently (coinciding with code upgrades where the app would be being refreshed).
From what I have been able to research I was planning on using a static class to serve as a helper for building and maintaining the cache. The below code shows how I would build the cache from a database table which would contain the different baseball card condition grades (Mint, Near Mint, etc)
Static CacheHelper class:
public static class CacheHelpers
{
private static HashSet<baseballcardcondition> _CardConditionsCache = null;
public static HashSet<baseballcardcondition> CardConditionsCache
{
get
{
if (_CardConditionsCache == null)
{
_CardConditionsCache = (HttpContext.Current.Cache["CardConditionsCache"] as HashSet<baseballcardconditions>);
if (_CardConditionsCache == null)
{
mydbEntities db = new mydbEntities();
_CardConditionsCache = new HashSet<baseballcardconditions>(db.baseballcardconditions);
HttpContext.Current.Cache.Insert("CardConditionsCache", _CardConditionsCache);
}
}
return _CardConditionsCache;
}
set
{
HttpContext.Current.Cache.Insert("CardConditionsCache", _CardConditionsCache);
}
}//public static HashSet<baseballcardconditions> CardConditionsCache
}//public static class CacheHelpers
Later I would be using this data to build a formatted string for a jqGrid grid (a jQuery javascript plugin for displaying tabular data) and would access it via:
//cardConditionSelectListHelperString
string tempString = "";
foreach (var keyPair in CacheHelpers.CardConditionsCache)
{
tempString += keyPair.IdBaseballCardCondition + ":" + keyPair.Condition + ";";
}
Does this look like a solid, robust, and thread safe way to manage my cache for my MVC web application? I would probably expand my CacheHelpers class to have other cached Hashsets so I don't have to hit the database for this read only, basically static data.
Thank you in advance.
"Does this look like a solid, robust, and thread safe way to manage my cache for my MVC web application? "
No, it doesn't. For example how do you keep the size of your data low? You should flush the unused data from the cache (check this: http://msdn.microsoft.com/en-us/library/dd632018.aspx). In addition it is not thread safe.
I think create an own cache solution is the typical "re-inviting the wheel" scenario, there are a lot of cache (even distributed cache) on the market - in addition some of them are free. A good cache solution is much more complex than just start using some static field.

C# AppSettingsReader: "reread" values into the AppSettingsReader (runtime)?

In my application, I need to change some value ("Environment") in appSetting of app.config at runtime.
I use AppSettingsReader
private static AppSettingsReader _settingReader;
public static AppSettingsReader SettingReader
{
get
{
if (_settingReader == null)
{
_settingReader = new AppSettingsReader();
}
return _settingReader;
}
}
Then at some stage I do this
config.AppSettings.Settings[AppSettingString.Environment.ToString()].Value = newEnvironment.ToString();
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
However, next time I try to read "Environment" like this
string environment = (string)SettingReader.GetValue(AppSettingString.Environment.ToString(), typeof(System.String));
I end up with the old value of Environment.
I noticed that I can fix this by doing
_settingReader = new AppSettingsReader();
before I read "Environment".
But I think creating a new instance is not the proper approach.
Maybe there is a way to let my SettingReader know, that the values have changed to use the same instance of it, but with refreshed values?
(Not a project-breaking question obviously, more of an educational one)
AppSettingsReader doesn't seem to have any method to reload from disk. It just derives from Object. Creating a new instance seems to be the only thing that would work... I may be wrong, but AppSettings are supposed to be read-only values for your app. More like configuration parameters for your application that can be tweaked before startup.
For read-write application settings, I think the Settings mechanism with IDE support (System.Configuration.ApplicationSettingsBase) would be the preferred approach. This has Save and Reload methods. The designer-gen class makes the code far more readable too..
Double click on the Properties Node under your Project in Solution Explorer. Find the Settings tab.
Instead of
sEnvironment = (string)SettingReader.GetValue(AppSettingString.Environment.ToString(), typeof(System.String));
you could have typed properties like
sEnvironment = Properties.Settings.Default.Environment;
The designer generated class exposes a synchronized singleton instance via the Default property.. which should mean you don't need to reload.. you'd always get the latest value within the application.
Upgrade is the function you are looking for
System.Configuration.ApplicationSettingsBase.Upgrade
System.Configuration.ApplicationSettingsBase.Reload

c#: Create new settings at run time

c# windows forms: How do you create new settings at run time so that they are permanently saved as Settings.Default.-- values?
Just in case that still matters to anyone:
You can dynamically add settings through Settings.Default.Properties.Add(...) and have these also persisted in the local storage after saving (I had those entries reflected in the roaming file).
Nevertheless it seems that the dynamically added settings keep missing in the Settings.Default.Properties collecion after loading again.
I could work around this problem by adding the dynamic property before first accessing it.
Example (notice that I "create" my dynamic setting from a base setting):
// create new setting from a base setting:
var property = new SettingsProperty(Settings.Default.Properties["<baseSetting>"]);
property.Name = "<dynamicSettingName>";
Settings.Default.Properties.Add(property);
// will have the stored value:
var dynamicSetting = Settings.Default["<dynamicSettingName>"];
I don't know if this is supported by Microsoft as the documentation is very rare on this topic.
Problem is also described here http://www.vbdotnetforums.com/vb-net-general-discussion/29805-my-settings-run-time-added-properties-dont-save.html#post88152 with some solution offered here http://msdn.microsoft.com/en-us/library/saa62613(v=VS.100).aspx (see Community Content - headline "How to Create / Save / Load Dynamic (at Runtime) Settings"). But this is VB.NET.
In addition to John's solution for saving, the proper method for loading is add the property, and then do a Reload() on your settings.
Your dynamic setting will be there!
For a full example, valid for using in library code, as you can pass the settings in ..
ApplicationSettingsBase settings = passed_in;
SettingsProvider sp = settings.Providers["LocalFileSettingsProvider"];
SettingsProperty p = new SettingsProperty("your_prop_name");
your_class conf = null;
p.PropertyType = typeof( your_class );
p.Attributes.Add(typeof(UserScopedSettingAttribute),new UserScopedSettingAttribute());
p.Provider = sp;
p.SerializeAs = SettingsSerializeAs.Xml;
SettingsPropertyValue v = new SettingsPropertyValue( p );
settings.Properties.Add( p );
settings.Reload();
conf = (your_class)settings["your_prop_name"];
if( conf == null )
{
settings["your_prop_name"] = conf = new your_class();
settings.Save();
}
Since the Settings class is generated at build time (or, actually, whenever you update the settings file from within the designer), you can't use this mechanism for dynamic scenarios. You can, however, add some collection or dictionary to the application settings and modify that dynamically.
You can't add settings directly (at least not without editing the config XML at runtime), but you can fake it.
In my case, I had a group of identical custom controls on the form, and I wanted to store the runtime state of each control. I needed to store the state of each control, since each one had different data it.
I created a new StringCollection setting named ControlData and placed my own data in there. I then load the data from that list and use it to initialize my controls.
The list looks like this:
Box1Text=A
Box1List=abc;def;foo;bar;
Box2Text=hello
Box2List=server1;server2;
In my startup code, I read through the key/value pairs like this:
foreach (string item in Properties.Settings.Default.ControlData) {
string[] parts=item.split('=');
parts[0] will have the key and parts[1] will have the value. You can now do stuff based on this data.
During the shutdown phase, I do the inverse to write the data back to the list. (Iterate through all the controls in the form and add their settings to ControlData.
How would you access the new settings that you have created? The point of the Visual Studio settings designer is that you can write code that uses these settings with compile-time checking of your code. If you want to dynamically create new settings for your app to use, you will also need to dynamically load them. For dynamic settings, you may want to look at the System.Configuration assembly, notably ConfigurationSection. You can create a custom configuration section with that, which you could use for dynamic setting addition/removal. You might use a ConfigurationCollection for that dynamic addition/removal.
INI files eh? Google turned up this INI library for .NET.
What you could do is create a new registry key.
Name the new key "Your program settings".
RegistryKey ProgSettings = Registry.CurrentUser.OpenSubKey("Software", true);
ProgSettings.CreateSubKey("Your Program settings");
ProgSettings.Close();
Now you can add String Identifiers and values.
RegistryKey ProgSettings = Registry.CurrentUser.OpenSubKey("Software\\Your Program settings", true);
ProgSettings.SetValue("Setting Name", value); // store settings
string settings = ProgSettings.GetValue("Setting Name", false); // retreave settings
ProgSettings.DeleteValue("Setting Name", false);
Besure to close the registry key when you are done to avoid conflicts with other parts of your program that may write to the registry.
Many comercial software applications use these methods.
stackoverflow has many examples about writing and reading to the registry.
This is much easyer then modifying the appconfig.xml file that is used when you create settings.
It took me a long time using the top two answers here plus this link (Create new settings on runtime and read after restart) to get it to finally work.
First of all, set your expectations. The answer here will create a new user setting and you can get its value the next time you launch your app. However, the setting you created this way will not appear in the Settings designer. In fact, when you relaunch the app and try to access the setting in your code, it will not find it. However, the setting you have created through code is saved in the user.config file (say jDoe.config) somewhere in your file system. For you to access this value, you have to add the setting again.
Here is a working example I have:
private void FormPersistence_Load(object sender, EventArgs e)
{
StartPosition = FormStartPosition.Manual;
// Set window location
var exists = Settings.Default.Properties.OfType<SettingsProperty>().Any(p => p.Name == Name + "Location");
if (exists)
{
this.Location = (Point)Settings.Default[Name + "Location"];
}
else
{
var property = new SettingsProperty(Settings.Default.Properties["baseLocation"]);
property.Name = Name + "Location";
Settings.Default.Properties.Add(property);
Settings.Default.Reload();
this.Location = (Point)Settings.Default[Name + "Location"];
}
}
Note:
My new setting's name will be resolved at run time. Name is really this.Name, which is the form's name. This is a base form that other forms can inherit from, so all the child forms will be able to remember their locations.
baseLocation is a setting I have manually created in Settings designer. The new setting I have is the same type. This way I don't have to worry about things like provider, type, etc. in code.
I see how what I wanted was the wrong idea. I'm porting a c++ app over to c# and it has a lot of ini file settings and I was looking for a shortcut to add them in. I'm lazy.

Categories