867 lines
31 KiB
C#
867 lines
31 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Reflection;
|
|
using System.Runtime.Serialization.Formatters.Binary;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Xml;
|
|
|
|
namespace Inspectron.Settings
|
|
{
|
|
|
|
public class InspectronSettings
|
|
{
|
|
|
|
public InspectronSettings(string path = null, string profile = null)
|
|
{
|
|
Assembly assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
|
|
|
|
AssemblyName assemblyName = assembly.GetName();
|
|
_applicationName = assemblyName.Name;
|
|
Version version = assemblyName.Version;
|
|
_versionString = version.Major + "." + version.Minor;
|
|
|
|
string startupPath = Path.GetDirectoryName(new Uri(assemblyName.CodeBase).LocalPath);
|
|
_defaultSettingsPath = Path.Combine(startupPath, "DefaultSettings.xml");
|
|
|
|
string settingsFileName = string.IsNullOrEmpty(profile)
|
|
? "AppSettings.xml"
|
|
: "AppSettings_" + profile + ".xml";
|
|
|
|
if (path == null)
|
|
{
|
|
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
|
_settingsPath = string.Format("{0}\\{1}\\{2}\\{3}", appDataPath, _applicationName, _versionString, settingsFileName);
|
|
}
|
|
else
|
|
{
|
|
_settingsPath = Path.Combine(path, settingsFileName);
|
|
}
|
|
}
|
|
internal Path<object> GetSettingsPath(string pathName)
|
|
{
|
|
string[] pathSegments = pathName.Split('/', 16);
|
|
object[] path = new object[pathSegments.Length + 1];
|
|
|
|
// first node is the settings tree root
|
|
Tree<object> node = (Tree<object>)_userSettings;
|
|
path[0] = _userSettings;
|
|
|
|
// middle nodes are folders
|
|
for (int i = 1; i < path.Length - 1; i++)
|
|
{
|
|
node = GetOrCreateFolder(pathSegments[i - 1], node);
|
|
path[i] = node;
|
|
}
|
|
|
|
// leaf node is user settings object
|
|
foreach (Tree<object> leaf in node.Children)
|
|
{
|
|
UserSettingsInfo info = leaf.Value as UserSettingsInfo;
|
|
if (info != null && info.Name == pathSegments[pathSegments.Length - 1])
|
|
{
|
|
path[path.Length - 1] = leaf;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return new Path<object>(path);
|
|
}
|
|
/// <summary>
|
|
/// Gets or sets the current state of all properties (Memento pattern)</summary>
|
|
public object State
|
|
{
|
|
get
|
|
{
|
|
MemoryStream stream = new MemoryStream();
|
|
Serialize(stream);
|
|
return stream;
|
|
}
|
|
set
|
|
{
|
|
MemoryStream stream = value as MemoryStream;
|
|
if (stream == null)
|
|
throw new ArgumentException("Not a valid memento");
|
|
stream.Position = 0;
|
|
Deserialize(stream);
|
|
}
|
|
}
|
|
|
|
private IEnumerable<PropertyDescriptor> UserPropertyDescriptors
|
|
{
|
|
get
|
|
{
|
|
var userSettings = UserSettings as Tree<object>;
|
|
if (userSettings == null)
|
|
throw new InvalidOperationException("userSettings");
|
|
var all = userSettings.LevelOrder.Where(x => x.Value is UserSettingsInfo);
|
|
foreach (Tree<object> node in all)
|
|
{
|
|
UserSettingsInfo info = node.Value as UserSettingsInfo;
|
|
if (info != null)
|
|
{
|
|
foreach (PropertyDescriptor property in info.Settings)
|
|
yield return property;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private static readonly object _unusedComponent = new object();
|
|
public void SetDefaults()
|
|
{
|
|
foreach (SettingsInfo info in _settings.Values)
|
|
{
|
|
foreach (SettingsInfo.Setting setting in info.Settings.Values)
|
|
{
|
|
if (setting.PropertyDescriptor != null &&
|
|
setting.PropertyDescriptor.CanResetValue(_unusedComponent))
|
|
{
|
|
setting.PropertyDescriptor.ResetValue(_unusedComponent);
|
|
}
|
|
}
|
|
}
|
|
// Load default settings if exist.
|
|
if (File.Exists(_defaultSettingsPath))
|
|
{
|
|
using (Stream stream = File.OpenRead(_defaultSettingsPath))
|
|
Deserialize(stream);
|
|
}
|
|
}
|
|
internal List<PropertyDescriptor> GetProperties(Tree<object> tree)
|
|
{
|
|
var info = tree.Value as UserSettingsInfo;
|
|
if (info != null)
|
|
return info.Settings;
|
|
|
|
return null;
|
|
}
|
|
protected class SettingsInfo
|
|
{
|
|
/// <summary>
|
|
/// Constructor with name</summary>
|
|
/// <param name="name">Name associated with this group of settings</param>
|
|
public SettingsInfo(string name)
|
|
{
|
|
Name = name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a setting to group of settings as a name and value, replacing the previous value if present</summary>
|
|
/// <param name="name">Setting name</param>
|
|
/// <param name="valueString">Setting value</param>
|
|
public void Add(string name, string valueString)
|
|
{
|
|
Setting setting;
|
|
if (Settings.TryGetValue(name, out setting))
|
|
{
|
|
setting.Set(name, valueString);
|
|
}
|
|
else
|
|
{
|
|
Settings.Add(name, new Setting(name, valueString));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a setting to group of settings as a PropertyDescriptor, replacing the previous value if present</summary>
|
|
/// <param name="descriptor">PropertyDescriptor describing setting</param>
|
|
public void Add(PropertyDescriptor descriptor)
|
|
{
|
|
Setting setting;
|
|
if (Settings.TryGetValue(descriptor.Name, out setting))
|
|
{
|
|
setting.Set(descriptor);
|
|
}
|
|
else
|
|
{
|
|
Settings.Add(descriptor.Name, new Setting(descriptor));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Name associated with this group of settings</summary>
|
|
public readonly string Name;
|
|
/// <summary>
|
|
/// Dictionary for names and values of settings in group</summary>
|
|
public readonly SortedDictionary<string, Setting> Settings = new SortedDictionary<string, Setting>();
|
|
|
|
/// <summary>
|
|
/// Class for handling individual setting information</summary>
|
|
public class Setting
|
|
{
|
|
/// <summary>
|
|
/// Constructor with PropertyDescriptor</summary>
|
|
/// <param name="descriptor">PropertyDescriptor describing setting</param>
|
|
public Setting(PropertyDescriptor descriptor)
|
|
{
|
|
PropertyDescriptor = descriptor;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor with name and value of setting</summary>
|
|
/// <param name="name">Setting name</param>
|
|
/// <param name="valueString">Setting value</param>
|
|
public Setting(string name, string valueString)
|
|
{
|
|
Name = name;
|
|
ValueString = valueString;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a setting's value with name and value of setting</summary>
|
|
/// <param name="name">Setting name</param>
|
|
/// <param name="valueString">Setting value</param>
|
|
public void Set(string name, string valueString)
|
|
{
|
|
Name = name;
|
|
ValueString = valueString;
|
|
if (PropertyDescriptor != null)
|
|
{
|
|
SetValue();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a setting's value with PropertyDescriptor</summary>
|
|
/// <param name="descriptor">PropertyDescriptor describing setting</param>
|
|
public void Set(PropertyDescriptor descriptor)
|
|
{
|
|
PropertyDescriptor = descriptor;
|
|
if (Name != null && ValueString != null)
|
|
{
|
|
SetValue();
|
|
}
|
|
}
|
|
|
|
private void SetValue()
|
|
{
|
|
if (!CanMakeChanges)
|
|
return;
|
|
|
|
object value = GetValue(PropertyDescriptor.PropertyType, ValueString);
|
|
PropertyDescriptor.SetValue(null, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Setting name</summary>
|
|
public string Name;
|
|
/// <summary>
|
|
/// Setting value</summary>
|
|
public string ValueString;
|
|
/// <summary>
|
|
/// Setting PropertyDescriptor</summary>
|
|
public PropertyDescriptor PropertyDescriptor;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set whether property descriptors are allowed to make changes</summary>
|
|
/// <remarks>Used when persisted settings are being loaded from disk
|
|
/// to prevent property descriptors from being set multiple times as
|
|
/// in the case where DefaultSettings.xml and AppSettings.xml both exist</remarks>
|
|
public static bool CanMakeChanges { private get; set; }
|
|
}
|
|
private static object GetValue(Type type, string valueString)
|
|
{
|
|
object value = null;
|
|
try
|
|
{
|
|
TypeConverter converter = TypeDescriptor.GetConverter(type);
|
|
if (CanConvertToAndFromString(converter))
|
|
{
|
|
value = converter.ConvertFromInvariantString(valueString);
|
|
}
|
|
else if (TypeConverterRegistry.CanConvert(type))
|
|
{
|
|
value = TypeConverterRegistry.GetConverter(type).ConvertFrom(valueString);
|
|
}
|
|
else
|
|
{
|
|
// deserialize
|
|
byte[] data = Convert.FromBase64String(valueString);
|
|
using (MemoryStream stream = new MemoryStream(data))
|
|
{
|
|
BinaryFormatter formatter = new BinaryFormatter();
|
|
value = formatter.Deserialize(stream);
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
value = null;
|
|
}
|
|
return value;
|
|
}
|
|
private static bool CanConvertToAndFromString(TypeConverter converter)
|
|
{
|
|
return (converter.CanConvertFrom(typeof(string)) &&
|
|
converter.CanConvertTo(typeof(string)));
|
|
}
|
|
/// <summary>
|
|
/// Class for group of user settings</summary>
|
|
public class UserSettingsInfo
|
|
{
|
|
/// <summary>
|
|
/// Constructor with name and PropertyDescriptors</summary>
|
|
/// <param name="name">Name of group of user settings</param>
|
|
/// <param name="settings">PropertyDescriptors with settings</param>
|
|
public UserSettingsInfo(string name, PropertyDescriptor[] settings)
|
|
{
|
|
Name = name;
|
|
Settings = new List<PropertyDescriptor>(settings);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Name of group of user settings</summary>
|
|
public readonly string Name;
|
|
/// <summary>
|
|
/// PropertyDescriptors with settings</summary>
|
|
public readonly List<PropertyDescriptor> Settings;
|
|
}
|
|
private readonly SortedDictionary<string, SettingsInfo> _settings = new SortedDictionary<string, SettingsInfo>();
|
|
private ITreeView _userSettings = new TreeView(string.Empty);
|
|
|
|
public void RegisterSettings(string uid, params PropertyDescriptor[] settings)
|
|
{
|
|
SettingsInfo settingsInfo;
|
|
if (!_settings.TryGetValue(uid, out settingsInfo))
|
|
{
|
|
settingsInfo = new SettingsInfo(uid);
|
|
_settings.Add(uid, settingsInfo);
|
|
}
|
|
|
|
foreach (PropertyDescriptor descriptor in settings)
|
|
settingsInfo.Add(descriptor);
|
|
}
|
|
public ITreeView UserSettings
|
|
{
|
|
get { return _userSettings; }
|
|
}
|
|
|
|
public void RegisterSimple(object owner,Expression<Func<object>> expression,string pathName,string name,string category="General",string description="")
|
|
{
|
|
var descriptor = new BoundPropertyDescriptor(owner,expression, name, category,
|
|
description);
|
|
|
|
RegisterSettings(pathName,descriptor);
|
|
|
|
RegisterUserSettings(pathName,descriptor);
|
|
}
|
|
|
|
public void RegisterUserSettings(string pathName, params PropertyDescriptor[] settings)
|
|
{
|
|
if (string.IsNullOrEmpty(pathName))
|
|
throw new ArgumentException("pathName");
|
|
|
|
string[] path = pathName.Split('/', 16);
|
|
|
|
// get root folder
|
|
Tree<object> folder = UserSettings as Tree<object>;
|
|
if (folder == null)
|
|
throw new InvalidOperationException("userSettings");
|
|
|
|
// for each subsequent segment of the path, get folder
|
|
for (int i = 0; i < path.Length - 1; i++)
|
|
folder = GetOrCreateFolder(path[i], folder);
|
|
|
|
// get the node that should hold the settings, if it already exists
|
|
string name = path[path.Length - 1];
|
|
UserSettingsInfo existing = null;
|
|
int index = 0;
|
|
foreach (Tree<object> node in folder.Children)
|
|
{
|
|
UserSettingsInfo info = node.Value as UserSettingsInfo;
|
|
if (info != null)
|
|
{
|
|
if (info.Name == name)
|
|
{
|
|
existing = info;
|
|
break;
|
|
}
|
|
if (info.Name.CompareTo(name) < 0)
|
|
index++;
|
|
}
|
|
}
|
|
|
|
// add the settings, either by merging with the existing node or creating a new one
|
|
if (existing != null)
|
|
{
|
|
foreach (PropertyDescriptor pd in settings)
|
|
existing.Settings.Add(pd);
|
|
}
|
|
else
|
|
{
|
|
Tree<object> node = new Tree<object>(new UserSettingsInfo(name, settings));
|
|
folder.Children.Insert(index, node);
|
|
}
|
|
}
|
|
private static string GetMutexName(string pathName)
|
|
{
|
|
string safeName = pathName;
|
|
|
|
//255 characters will break IpcChannel constructor. 250 works.
|
|
if (safeName.Length > 250)
|
|
safeName = safeName.Substring(safeName.Length - 250);
|
|
|
|
// The Mutex constructor will crash if given '\', unless it's part of a valid path.
|
|
// NextInstanceMonitor.ActivateApplication() will crash if there are '/' characters.
|
|
safeName = safeName.Replace('/', '-');
|
|
safeName = safeName.Replace('\\', '-');
|
|
|
|
return safeName;
|
|
}
|
|
protected void Serialize(Stream stream)
|
|
{
|
|
Saving.Raise(this, EventArgs.Empty);
|
|
|
|
XmlDocument xmlDoc = new XmlDocument();
|
|
xmlDoc.AppendChild(xmlDoc.CreateXmlDeclaration("1.0", "utf-8", "yes"));
|
|
XmlElement root = xmlDoc.CreateElement("settings");
|
|
xmlDoc.AppendChild(root);
|
|
|
|
// add application name and version to the root element.
|
|
root.SetAttribute("appName", _applicationName);
|
|
root.SetAttribute("appVersion", _versionString);
|
|
|
|
foreach (SettingsInfo info in _settings.Values)
|
|
{
|
|
XmlElement block = xmlDoc.CreateElement("block");
|
|
block.SetAttribute("id", info.Name);
|
|
|
|
foreach (SettingsInfo.Setting setting in info.Settings.Values)
|
|
{
|
|
PropertyDescriptor descriptor = setting.PropertyDescriptor;
|
|
if (descriptor != null)
|
|
{
|
|
object value = descriptor.GetValue(null);
|
|
if (CanWriteValue(value))
|
|
WriteValue(descriptor.Name, value, block);
|
|
}
|
|
else
|
|
{
|
|
WriteValue(setting.Name, setting.ValueString, block);
|
|
}
|
|
}
|
|
// skip empty block
|
|
if (block.ChildNodes.Count > 0)
|
|
root.AppendChild(block);
|
|
}
|
|
|
|
XmlWriterSettings settings = new XmlWriterSettings();
|
|
settings.CloseOutput = false;
|
|
settings.Indent = true;
|
|
|
|
using (XmlWriter writer = XmlWriter.Create(stream, settings))
|
|
{
|
|
xmlDoc.WriteTo(writer);
|
|
}
|
|
}
|
|
private bool CanWriteValue(object value)
|
|
{
|
|
if (value == null)
|
|
return false;
|
|
|
|
TypeConverter converter = TypeDescriptor.GetConverter(value.GetType());
|
|
return CanConvertToAndFromString(converter) || value.GetType().IsSerializable;
|
|
}
|
|
private void WriteValue(string name, object value, XmlElement block)
|
|
{
|
|
if (value == null)
|
|
return;
|
|
|
|
// skip persisting if any exception occurs
|
|
string valueString = null;
|
|
Type type = value.GetType();
|
|
TypeConverter converter = TypeDescriptor.GetConverter(type);
|
|
if (CanConvertToAndFromString(converter))
|
|
{
|
|
valueString = converter.ConvertToInvariantString(value);
|
|
}
|
|
else if (TypeConverterRegistry.TryGetConverter(type, out var typeConverter))
|
|
{
|
|
valueString = typeConverter.ConvertTo(value, typeof(string)) as string;
|
|
}
|
|
else if (type.IsSerializable)
|
|
{
|
|
// serialize
|
|
BinaryFormatter formatter = new BinaryFormatter();
|
|
using (MemoryStream stream = new MemoryStream())
|
|
{
|
|
formatter.Serialize(stream, value);
|
|
valueString = Convert.ToBase64String(stream.GetBuffer());
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(valueString))
|
|
return;
|
|
|
|
XmlDocument xmlDoc = block.OwnerDocument;
|
|
XmlElement elmValue = xmlDoc.CreateElement("value");
|
|
elmValue.SetAttribute("name", name);
|
|
elmValue.SetAttribute("type", type.Name);
|
|
|
|
// if the valueString is xmlDoc then
|
|
XmlDocument temp = StringToXmlDoc(valueString);
|
|
if (temp != null)
|
|
{
|
|
// remove xml declaration if exists
|
|
XmlDeclaration decl = temp.FirstChild as XmlDeclaration;
|
|
if (decl != null)
|
|
temp.RemoveChild(decl);
|
|
elmValue.InnerXml = temp.DocumentElement.OuterXml;
|
|
}
|
|
else
|
|
{
|
|
elmValue.InnerText = valueString;
|
|
}
|
|
|
|
block.AppendChild(elmValue);
|
|
}
|
|
private XmlDocument StringToXmlDoc(string strXml)
|
|
{
|
|
XmlDocument xmlDoc = null;
|
|
try
|
|
{
|
|
int len = (strXml.Length > 20) ? 20 : strXml.Length;
|
|
string test = RemoveAllWhiteSpace(strXml.Substring(0, len)).ToLower();
|
|
if (test.Contains("<?xmlversion="))
|
|
{
|
|
xmlDoc = new XmlDocument();
|
|
xmlDoc.LoadXml(strXml);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
xmlDoc = null;
|
|
}
|
|
return xmlDoc;
|
|
|
|
}
|
|
public static string RemoveAllWhiteSpace(string s)
|
|
{
|
|
if (string.IsNullOrEmpty(s))
|
|
return s;
|
|
StringBuilder result = new StringBuilder(s.Length);
|
|
for (int i = 0; i < s.Length; i++)
|
|
{
|
|
char c = s[i];
|
|
if (!char.IsWhiteSpace(c))
|
|
result.Append(c);
|
|
}
|
|
return result.ToString();
|
|
}
|
|
public void SaveSettings()
|
|
{
|
|
string tempNew = string.Empty;
|
|
|
|
string mutexName = GetMutexName(_settingsPath);
|
|
using (Mutex saveMutex = new Mutex(false, mutexName))
|
|
{
|
|
try
|
|
{
|
|
saveMutex.WaitOne();
|
|
|
|
// Create zero-size file.
|
|
tempNew = Path.GetTempFileName();
|
|
|
|
using (Stream stream = File.Create(tempNew))
|
|
Serialize(stream);
|
|
|
|
// Make sure the settings directory exists. Do nothing if it already exists.
|
|
string settingsDir = Path.GetDirectoryName(_settingsPath);
|
|
Directory.CreateDirectory(settingsDir);
|
|
|
|
// Erase old backup (if any) and move current settings file (if any).
|
|
string tempBackup = Path.Combine(settingsDir, "~Settings.xml");
|
|
if (File.Exists(tempBackup)) // seems unnecessary, but Dan put this check in the WPF version --Ron
|
|
File.Delete(tempBackup);
|
|
if (File.Exists(_settingsPath))
|
|
File.Move(_settingsPath, tempBackup);
|
|
|
|
// Move temporary file to be the new settings file, then delete backup.
|
|
File.Move(tempNew, _settingsPath);
|
|
File.Delete(tempBackup);
|
|
}
|
|
catch (TargetInvocationException)
|
|
{
|
|
// Catch and ignore TargetInvocationException happening if Windows
|
|
// is shut down with the application still running.
|
|
// TO DO: Find a way to successfully save settings and exit on shutdown.
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
}
|
|
finally
|
|
{
|
|
// Attempt clean-up. No exception is thrown if file doesn't exist.
|
|
File.Delete(tempNew);
|
|
saveMutex.ReleaseMutex();
|
|
}
|
|
}
|
|
}
|
|
private Tree<object> GetOrCreateFolder(string name, Tree<object> tree)
|
|
{
|
|
// search for folder
|
|
Tree<object> result = null;
|
|
int index = 0;
|
|
foreach (Tree<object> child in tree.Children)
|
|
{
|
|
string folderName = child.Value as string;
|
|
if (folderName != null)
|
|
{
|
|
if (folderName == name)
|
|
{
|
|
result = child;
|
|
break;
|
|
}
|
|
|
|
if (folderName.CompareTo(name) < 0)
|
|
index++;
|
|
}
|
|
else // child is UserSettingsInfo
|
|
{
|
|
index++; // folders should follow settings nodes
|
|
}
|
|
|
|
}
|
|
|
|
// if not found, create it
|
|
if (result == null)
|
|
{
|
|
result = new Tree<object>(name);
|
|
tree.Children.Insert(index, result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
public event EventHandler Saving;
|
|
public event EventHandler Loading;
|
|
public event EventHandler Reloaded;
|
|
|
|
|
|
private class TreeView : Tree<object>, ITreeView
|
|
{
|
|
public TreeView(object root)
|
|
: base(root)
|
|
{
|
|
}
|
|
|
|
#region ITreeView Members
|
|
|
|
public object Root
|
|
{
|
|
get { return this; }
|
|
}
|
|
|
|
public IEnumerable<object> GetChildren(object parent)
|
|
{
|
|
foreach (object child in ((Tree<object>)parent).Children)
|
|
yield return child;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IItemView Members
|
|
|
|
/// <summary>
|
|
/// Gets item's display information</summary>
|
|
/// <param name="item">Item being displayed</param>
|
|
/// <param name="info">Item info, to fill out</param>
|
|
public void GetInfo(object item, ItemInfo info)
|
|
{
|
|
object value = ((Tree<object>)item).Value;
|
|
if (value is string)
|
|
{
|
|
info.Label = (string)value;
|
|
//info.ImageIndex = info.GetImageList().Images.IndexOfKey(Resources.FolderImage);
|
|
info.AllowSelect = false;
|
|
}
|
|
else
|
|
{
|
|
UserSettingsInfo settingsInfo = value as UserSettingsInfo;
|
|
info.Label = settingsInfo.Name;
|
|
info.AllowLabelEdit = false;
|
|
//info.ImageIndex = info.GetImageList().Images.IndexOfKey(Resources.PreferencesImage);
|
|
info.IsLeaf = true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
|
|
|
|
private string _settingsPath;
|
|
private string _defaultSettingsPath;
|
|
private string _applicationName;
|
|
private string _versionString;
|
|
private string _propertyViewState;
|
|
|
|
|
|
public void LoadSettings()
|
|
{
|
|
Loading.Raise(this, EventArgs.Empty);
|
|
|
|
try
|
|
{
|
|
bool defaultSettingsExists = File.Exists(_defaultSettingsPath);
|
|
bool appSettingsExists = File.Exists(_settingsPath);
|
|
|
|
// only update property descriptors during the DefaultSettings.xml pass
|
|
// if DefaultSettings.xml exists and AppSettings.xml does not exist
|
|
SettingsInfo.CanMakeChanges = defaultSettingsExists && !appSettingsExists;
|
|
|
|
// first, load default settings, if they exist:
|
|
if (defaultSettingsExists)
|
|
{
|
|
using (Stream stream = File.OpenRead(_defaultSettingsPath))
|
|
Deserialize(stream);
|
|
}
|
|
|
|
// restore for AppSettings.xml pass
|
|
SettingsInfo.CanMakeChanges = true;
|
|
|
|
// now load user settings, overriding defaults:
|
|
if (appSettingsExists)
|
|
{
|
|
using (Stream stream = File.OpenRead(_settingsPath))
|
|
Deserialize(stream);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
}
|
|
finally
|
|
{
|
|
// restore value
|
|
SettingsInfo.CanMakeChanges = true;
|
|
}
|
|
}
|
|
|
|
protected void ApplyStoredSettings()
|
|
{
|
|
foreach (SettingsInfo info in _settings.Values)
|
|
{
|
|
foreach (SettingsInfo.Setting setting in info.Settings.Values)
|
|
{
|
|
if (setting.PropertyDescriptor is BoundPropertyDescriptor pd)
|
|
{
|
|
if (setting.ValueString == null)
|
|
{
|
|
continue;
|
|
}
|
|
object v;
|
|
if (setting.PropertyDescriptor.PropertyType.IsEnum)
|
|
{
|
|
v = Enum.Parse(setting.PropertyDescriptor.PropertyType, setting.ValueString);
|
|
}
|
|
// if is list
|
|
else if (setting.PropertyDescriptor.PropertyType.IsGenericType &&
|
|
setting.PropertyDescriptor.PropertyType.GetGenericTypeDefinition() == typeof(List<>))
|
|
{
|
|
Type itemType = setting.PropertyDescriptor.PropertyType.GetGenericArguments()[0];
|
|
TypeConverter converter = TypeDescriptor.GetConverter(itemType);
|
|
string[] items = setting.ValueString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
IList list = (IList)Activator.CreateInstance(setting.PropertyDescriptor.PropertyType);
|
|
foreach (string item in items)
|
|
{
|
|
list.Add(converter.ConvertFromInvariantString(item.Trim()));
|
|
}
|
|
v = list;
|
|
}
|
|
else
|
|
{
|
|
v = Convert.ChangeType(setting.ValueString, setting.PropertyDescriptor.PropertyType);
|
|
}
|
|
pd.SetValue(pd.Owner, v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
protected bool Deserialize(Stream stream)
|
|
{
|
|
// create XML DOM from stream
|
|
// if failed, display message box and return
|
|
try
|
|
{
|
|
XmlDocument xmlDoc = new XmlDocument();
|
|
xmlDoc.Load(stream);
|
|
|
|
XmlElement root = xmlDoc.DocumentElement;
|
|
|
|
// get all the blocks
|
|
XmlNodeList blocks = root.SelectNodes("block");
|
|
if (blocks == null || blocks.Count == 0)
|
|
throw new Exception("The setting file is empty");
|
|
|
|
foreach (XmlElement block in blocks)
|
|
{
|
|
try
|
|
{
|
|
string id = block.GetAttribute("id");
|
|
|
|
SettingsInfo info;
|
|
if (!_settings.TryGetValue(id, out info))
|
|
{
|
|
info = new SettingsInfo(id);
|
|
_settings.Add(id, info);
|
|
}
|
|
|
|
// get a list of value element in each block
|
|
XmlNodeList valueNodes = block.SelectNodes("value");
|
|
|
|
// skip over empty block
|
|
if (valueNodes == null || valueNodes.Count == 0)
|
|
continue;
|
|
|
|
foreach (XmlElement xmlElement in valueNodes)
|
|
{
|
|
string name = xmlElement.GetAttribute("name");
|
|
string valueString = GetElementValueString(xmlElement);
|
|
|
|
info.Add(name, valueString);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
}
|
|
}
|
|
//ApplyStoredSettings();
|
|
Reloaded.Raise(this, EventArgs.Empty);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
private static string GetElementValueString(XmlElement element)
|
|
{
|
|
string valueString = element.InnerText;
|
|
if (string.IsNullOrEmpty(valueString))
|
|
valueString = element.InnerXml;
|
|
|
|
return valueString;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
} |