UserSettingsProvider (Persistieren von UserSettings)

    • VB.NET

    Es gibt 28 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

      @VB1963 Ich hab das ganze mal nach C# übertragen.
      Ergebnis: Die Settings in C# funktionieren etwas anders als in VB.NET. Das ist vielleicht für den @ErfinderDesRades interessant.
      Der linear übersetzte Code generiert auch mit SetDataFile() den DLL-Namen als Settings-File.
      Deswegen bin ich da wieder auf die Lösung mit GetModuleFileName() zurückgegangen. Vielleicht findetr ja jemand da auch noch eine elegante Lösung ohne explizite separate Initialisierung.
      UserSettingsProvider
      UserSettingsProvider.cs

      C#-Quellcode

      1. using System;
      2. using System.Configuration;
      3. using System.Collections.Generic;
      4. using System.Collections.Specialized;
      5. using System.IO;
      6. using System.Linq;
      7. using System.Runtime.InteropServices;
      8. using System.Runtime.Serialization.Formatters.Binary;
      9. using System.Text;
      10. namespace MyProvider
      11. {
      12. /// <summary>
      13. /// Dieser Provider speichert die betroffenen Settings-Einstellungen in eine eigene .exe.config.xml-Datei.
      14. /// Diese Datei liegt im Arbeitsverzeichnis der Anwendung.
      15. /// HINWEISE:
      16. /// Die ApplicationName-Property erstellt die ProduktName-Eigenschaft von der My.Application.Info.ProduktName
      17. /// Hier sollte in den Projekteigenschaften/Anwendung/Assemblyinformationen die Eigenschaft ProduktName = AssemblyName gestellt sein!
      18. /// </summary>
      19. public class UserSettingsProvider : SettingsProvider, IApplicationSettingsProvider
      20. {
      21. [DllImport("kernel32.dll", SetLastError = true)]
      22. [PreserveSig]
      23. public static extern uint GetModuleFileName
      24. ([In]IntPtr hModule, [Out]StringBuilder lpFilename, [In][MarshalAs(UnmanagedType.U4)] int nSize);
      25. public override string ApplicationName { get; set; }
      26. // hier wird der Produktname von der Assembly angegeben
      27. public override string Name { get { return "UserSettingsProvider"; } }
      28. private Dictionary<string, object> Datas;
      29. private FileInfo DataFile { get; set; }
      30. private string AppPath { get; set; }
      31. public UserSettingsProvider()
      32. {
      33. // Will write the current executable's complete path into exePath
      34. StringBuilder sb = new StringBuilder(1024);
      35. uint pathLen = UserSettingsProvider.GetModuleFileName(IntPtr.Zero, sb, sb.Capacity);
      36. if (pathLen >= 1024)
      37. {
      38. throw new ArgumentOutOfRangeException("StringBuilder() too small");
      39. }
      40. string exePath = sb.ToString();
      41. FileInfo fi = new FileInfo(exePath);
      42. this.ApplicationName = System.IO.Path.GetFileNameWithoutExtension(exePath);
      43. // in der Debug-Version auf dieselben Settings zugreifen
      44. if (ApplicationName.ToLower().EndsWith(".vshost"))
      45. {
      46. this.ApplicationName = System.IO.Path.GetFileNameWithoutExtension(this.ApplicationName);
      47. }
      48. this.AppPath = fi.DirectoryName;
      49. this.DataFile = new FileInfo(Path.Combine(this.AppPath, string.Concat(ApplicationName, ".exe.config.dat")));
      50. this.Datas = new Dictionary<string, object>();
      51. }
      52. public override void Initialize(string name, NameValueCollection config)
      53. {
      54. base.Initialize(this.ApplicationName, config);
      55. }
      56. /// <summary>
      57. /// SetPropertyValues wird erst abgearbeitet, wenn ApplicationSettingsBase.Save durchgeführt wird.
      58. /// ApplicationSettingsBase stellt sicher, dass für jeden einzelnen Provider nur seine markierten Werte herangenommen werden.
      59. /// Egal, ob der Provider auf einer Einzel-Einstellung angesetzt ist oder ob klassenweit die Einstellung angegeben wurde.
      60. /// Wenn Einstellungen nicht verändert wurden, müssen sie nicht gespeichert werden!
      61. /// Anwendungsspezifische Einstellungen können nicht geändert werden und werden daher auch nicht gespeichert!
      62. /// </summary>
      63. /// <param name="context"></param>
      64. /// <param name="collection"></param>
      65. public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
      66. {
      67. foreach (SettingsPropertyValue prpValue in collection)
      68. {
      69. if (!prpValue.IsDirty || (prpValue.SerializedValue == null))
      70. {
      71. continue;
      72. }
      73. if (this.IsApplicationScoped(prpValue.Property))
      74. {
      75. continue;
      76. }
      77. this.Datas[prpValue.Name] = prpValue.SerializedValue;
      78. }
      79. this.SaveBinary(this.DataFile, this.Datas);
      80. }
      81. public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
      82. {
      83. if (this.DataFile.Exists)
      84. {
      85. this.Datas = this.LoadBinary(DataFile);
      86. }
      87. SettingsPropertyValueCollection col = new SettingsPropertyValueCollection();
      88. foreach (SettingsProperty prp in collection)
      89. {
      90. SettingsPropertyValue Value = new SettingsPropertyValue(prp);
      91. if (this.Datas.ContainsKey(prp.Name))
      92. {
      93. Value = GetPropertyValue(prp);
      94. }
      95. // Wert von Datas einlesen
      96. col.Add(Value);
      97. }
      98. return col;
      99. }
      100. /// <summary>
      101. /// Nur benutzerspezifische Eigenschaften sind zulässig.
      102. /// Wenn eine Eigenschaft keinen Wert hat, wird der Defaultwert geladen.
      103. /// </summary>
      104. /// <param name="prp"></param>
      105. private SettingsPropertyValue GetPropertyValue(SettingsProperty prp)
      106. {
      107. SettingsPropertyValue value = new SettingsPropertyValue(prp);
      108. if (this.IsUserScoped(prp))
      109. {
      110. value.SerializedValue = this.Datas[prp.Name];
      111. }
      112. value.IsDirty = false;
      113. return value;
      114. }
      115. /// <summary>
      116. /// Test auf anwendungsspezifische Eigenschaft
      117. /// </summary>
      118. /// <param name="prop"></param>
      119. private bool IsApplicationScoped(SettingsProperty prop)
      120. {
      121. return this.HasSettingScope(prop, typeof(ApplicationScopedSettingAttribute));
      122. }
      123. /// <summary>
      124. /// Test auf benutzerdefinierte Eigenschaft
      125. /// </summary>
      126. /// <param name="prop"></param>
      127. private bool IsUserScoped(SettingsProperty prop)
      128. {
      129. return this.HasSettingScope(prop, typeof(UserScopedSettingAttribute));
      130. }
      131. /// <summary>
      132. /// prüft auf erlaubte Einstellung, so wie es der LocalFileSettingsProvider auch macht ...
      133. /// </summary>
      134. /// <param name="prop"></param>
      135. /// <param name="attributeType"></param>
      136. private bool HasSettingScope(SettingsProperty prop, Type attributeType)
      137. {
      138. bool isAppScoped = prop.Attributes[typeof(ApplicationScopedSettingAttribute)] != null;
      139. bool isUserScoped = prop.Attributes[typeof(UserScopedSettingAttribute)] != null;
      140. if (isUserScoped && isAppScoped)
      141. {
      142. throw new ConfigurationErrorsException("BothScopeAttributes: " + prop.Name);
      143. }
      144. if (!isUserScoped && !isAppScoped)
      145. {
      146. throw new ConfigurationErrorsException("NoScopeAttributes: " + prop.Name);
      147. }
      148. if (object.ReferenceEquals(attributeType, typeof(ApplicationScopedSettingAttribute)))
      149. {
      150. return isAppScoped;
      151. }
      152. if (object.ReferenceEquals(attributeType, typeof(UserScopedSettingAttribute)))
      153. {
      154. return isUserScoped;
      155. }
      156. return false;
      157. }
      158. /// <summary>
      159. /// Daten binär speichern
      160. /// </summary>
      161. /// <param name="DataFile"></param>
      162. /// <param name="Datas"></param>
      163. private void SaveBinary(FileInfo DataFile, Dictionary<string, object> Datas)
      164. {
      165. this.DeleteLocalPathUserSettings();
      166. using (Stream fs = File.Create(DataFile.FullName))
      167. {
      168. BinaryFormatter binfmt = new BinaryFormatter();
      169. binfmt.Serialize(fs, Datas);
      170. }
      171. }
      172. /// <summary>
      173. /// falls der LocalUserAppDataPath vorhanden ist,
      174. /// wird der Ordner mit allen untergeordneten Verzeichnissen gelöscht
      175. /// </summary>
      176. private void DeleteLocalPathUserSettings()
      177. {
      178. DirectoryInfo di = new DirectoryInfo(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath);
      179. di = di.Parent.Parent.Parent;
      180. if (di.Exists)
      181. {
      182. di.Delete(true);
      183. }
      184. }
      185. /// <summary>
      186. /// binäre Daten laden
      187. /// </summary>
      188. /// <param name="DataFile"></param>
      189. private Dictionary<string, object> LoadBinary(FileInfo DataFile)
      190. {
      191. using (Stream fs = File.Open(DataFile.FullName, FileMode.Open))
      192. {
      193. BinaryFormatter binfmt = new BinaryFormatter();
      194. return (Dictionary<string, object>)binfmt.Deserialize(fs);
      195. }
      196. }
      197. #region "IApplicationSettingsProvider Members"
      198. /// <summary>
      199. /// Abrufen der letzten Version der Settingseigenschaft
      200. /// </summary>
      201. /// <param name="context"></param>
      202. /// <param name="property"></param>
      203. public SettingsPropertyValue GetPreviousVersion(SettingsContext context, SettingsProperty property)
      204. {
      205. SettingsPropertyValue prpValue = new SettingsPropertyValue(property);
      206. if (this.Datas.ContainsKey(property.Name))
      207. {
      208. prpValue.PropertyValue = this.Datas[property.Name];
      209. }
      210. return prpValue;
      211. }
      212. /// <summary>
      213. /// wieder auf die Defaultwerte der Settings zurücksetzen!
      214. /// </summary>
      215. /// <param name="context"></param>
      216. public void Reset(SettingsContext context)
      217. {
      218. if (this.DataFile.Exists)
      219. {
      220. this.Datas.Clear();
      221. this.SaveBinary(this.DataFile, this.Datas);
      222. }
      223. }
      224. /// <summary>
      225. /// ErfinderDesRades
      226. /// fehlende Settings werden sofort der Sammlung entfernt
      227. /// neue hinzugekommene Settings werden beim nächsten Speichervorgang gesichert
      228. /// </summary>
      229. /// <param name="context"></param>
      230. /// <param name="properties"></param>
      231. public void Upgrade(SettingsContext context, SettingsPropertyCollection properties)
      232. {
      233. if (this.Datas.Count == 0 && this.DataFile.Exists)
      234. {
      235. this.Datas = this.LoadBinary(this.DataFile);
      236. }
      237. List<string> toDelete = this.Datas.Keys.Except(
      238. from SettingsProperty prop in properties
      239. select prop.Name).ToList<string>();
      240. toDelete.ForEach(delegate(string str)
      241. {
      242. this.Datas.Remove(str);
      243. });
      244. }
      245. #endregion
      246. }
      247. }
      MySettings.cs

      C#-Quellcode

      1. namespace TestApplication.Properties
      2. // Der Name dieses Namespaces muss an die Applikationangepasst werden
      3. {
      4. /// <summary>
      5. /// mit diesem Verweis wird der eigene UserSettingsProvider dem Programm untergeschoben
      6. /// </summary>
      7. [System.Configuration.SettingsProvider(typeof(MyProvider.UserSettingsProvider))]
      8. internal sealed partial class Settings
      9. {
      10. }
      11. }

      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      Hi Rod,
      ich habe heute zum ersten mal ein Formprojekt in C# erstellt und mit dem Designer einige Controls mit den Settings gebunden.
      Meinen (aber in VB generierten Provider) UserSettingsProvider.dll wurde in den Verweisen angegeben...

      Bekannt gegeben habe ich den Provider in Settings.cs wie folgt:

      C#-Quellcode

      1. namespace C_Test_Settings.Properties {
      2. [System.Configuration.SettingsProvider(typeof(UserSettingsProvider.MyProvider.UserSettingsProvider))]
      3. internal sealed partial class Settings {
      4. }
      5. }
      Mir fällt da auf, dass es bei mir UserSettingsProvider.MyProvider.UserSettingsProvider heißen muss und bei dir sehe ich MyProvider.UserSettingsProvider???
      Edit: sehe gerade, geht auch so...
      Des Weiteren musste ich aber meine Settings explizit beim Schließen der Form noch zum Speichern angeben.
      Das autom. Speichern der Settings beim Schließen der Form dürfte es in C# nicht so geben wie in VB (Diese Option habe ich nicht finden können...):

      C#-Quellcode

      1. private void Form1_FormClosing(object sender, FormClosingEventArgs e)
      2. {
      3. Properties.Settings.Default.Save();
      4. }

      Und es funktioniert, wie gewollt...
      PS: Ich glaube, es ist egal, in welcher .net-Sprache eine .dll verfasst wurde...

      RodFromGermany schrieb:

      Ergebnis: Die Settings in C# funktionieren etwas anders als in VB.NET. Das ist vielleicht für den @ErfinderDesRades interessant.
      Der linear übersetzte Code generiert auch mit SetDataFile() den DLL-Namen als Settings-File.

      bei mir nicht?
      Hier ist es wichtig, dass bei der AssemblyInfo der ProduktName verschieden angegeben ist.
      [assembly: AssemblyProduct("Fremdanwendung")]
      Ich habe jetzt ein C#-Sample angehängt und ein Bildchen, wie das bei mir aussieht...
      Bilder
      • UserSettingsProvider.PNG

        47,02 kB, 714×387, 228 mal angesehen
      Dateien

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „VB1963“ ()

      @VB1963
      Gug mal hier rein.
      Da werden die Settings lokal im XML-Format gespeichert.
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ ()

      Ich habe das Initialisieren des Providers nochmals überarbeitet - Vorlage war der standardmäßige Anbieter 'LocalFileSettingsProvider'...

      VB.NET-Quellcode

      1. Namespace MyProvider
      2. Public Class IrgendEinSettingsProvider
      3. Inherits SettingsProvider
      4. ''' <summary>
      5. ''' Initialisiert den Anbieter (Provider)
      6. ''' </summary>
      7. Public Overrides Sub Initialize(name As String, config As NameValueCollection)
      8. If String.IsNullOrEmpty(name) Then name = Me.Name
      9. MyBase.Initialize(name, config)
      10. End Sub
      11. ''' <summary>
      12. ''' Anbieternamen angeben (Provider)
      13. ''' </summary>
      14. ''' <value></value>
      15. Public Overrides ReadOnly Property Name As String
      16. Get
      17. Return "IrgendEinSettingsProvider"
      18. End Get
      19. End Property
      20. ''' <summary>
      21. ''' Der Provider ruft den Namen der aktuell ausgeführten Anwendung ab oder legt diesen fest.
      22. ''' </summary>
      23. Public Overrides Property ApplicationName() As String
      24. '
      25. '
      26. '
      funktioniert dein Settings in C# jetzt?

      VB1963 schrieb:

      dein Settings in C# jetzt?
      Dein "altes" ja, das "neue" noch nicht.
      Ich überarbeite da gerade den Code. Wenn ich haben fertich poste ich ihn.
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      @VB1963 Haben fertich.
      Ich hab mal die Namen der Klassen etwas angepasst. Deinen Provider hab ich umbenannt in UserSettingsProviderBin, den neuen analog in UserSettingsProviderXml.
      Nutzer bitte ich, den Link auf die Lizenzbedingungen im Code stehen zu lassen.
      UserSettingsProviderXml

      C#-Quellcode

      1. //************************************************************
      2. // PortableSettingsProvider.cs
      3. // Portable Settings Provider for C# applications
      4. //
      5. // 2010- Michael Nathan
      6. // http://www.Geek-Republic.com
      7. //
      8. // Licensed under Creative Commons CC BY-SA
      9. // http://creativecommons.org/licenses/by-sa/3.0/legalcode
      10. //
      11. //************************************************************
      12. // Original Sources from
      13. // http://www.geek-republic.com/2010/11/c-portable-settings-provider/
      14. //************************************************************
      15. using Microsoft.Win32;
      16. using System;
      17. using System.Collections.Generic;
      18. using System.Collections.Specialized;
      19. using System.Configuration;
      20. using System.IO;
      21. using System.Linq;
      22. using System.Reflection;
      23. using System.Text;
      24. using System.Xml;
      25. using System.Xml.Serialization;
      26. namespace MyProvider
      27. {
      28. public class UserSettingsProviderXml
      29. : SettingsProvider
      30. {
      31. // Define some static strings later used in our XML creation
      32. // XML Root node
      33. const string XmlRoot = "configuration";
      34. // Configuration declaration node
      35. const string ConfigNode = "configSections";
      36. // Configuration section group declaration node
      37. const string GroupNode = "sectionGroup";
      38. // User section node
      39. const string UserNode = "userSettings";
      40. // Application Specific Node
      41. private string AppNode;
      42. private XmlDocument DataXml; // XmlDoc
      43. // Override the ApplicationName property, returning the solution name. No need to set anything, we just need to
      44. // retrieve information, though the set method still needs to be defined.
      45. public override string ApplicationName { get; set; }
      46. public UserSettingsProviderXml()
      47. {
      48. dynamic fi = new FileInfo(Assembly.GetExecutingAssembly().Location);
      49. this.ApplicationName = fi.Name.Replace(fi.Extension, string.Empty);
      50. this.DataXml = null;
      51. this.AppNode = null;
      52. }
      53. private void InitAppNode()
      54. {
      55. this.AppNode = this.ApplicationName + ".Properties.Settings";
      56. }
      57. // Override the Initialize method
      58. public override void Initialize(string name, NameValueCollection config)
      59. {
      60. System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(ApplicationName), "ApplicationName is empty");
      61. base.Initialize(this.ApplicationName, config);
      62. }
      63. // Simply returns the name of the settings file, which is the solution name plus ".config"
      64. public virtual string GetSettingsFilename()
      65. {
      66. // hier den Namen der Exe reinschreiben
      67. return System.IO.Path.Combine(this.GetAppPath(), this.ApplicationName + ".exe.config");
      68. }
      69. // Gets current executable path in order to determine where to read and write the config file
      70. public virtual string GetAppPath()
      71. {
      72. return new System.IO.FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName;
      73. }
      74. // Retrieve settings from the configuration file
      75. public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext sContext, SettingsPropertyCollection settingsColl)
      76. {
      77. // Create a collection of values to return
      78. SettingsPropertyValueCollection retValues = new SettingsPropertyValueCollection();
      79. // Create a temporary SettingsPropertyValue to reuse
      80. SettingsPropertyValue propVal;
      81. // Loop through the list of settings that the application has requested and add them
      82. // to our collection of return values.
      83. foreach (SettingsProperty val in settingsColl)
      84. {
      85. propVal = new SettingsPropertyValue(val);
      86. propVal.IsDirty = false;
      87. propVal.SerializedValue = GetSetting(val);
      88. retValues.Add(propVal);
      89. }
      90. return retValues;
      91. }
      92. // Save any of the applications settings that have changed (flagged as "dirty")
      93. public override void SetPropertyValues(SettingsContext sContext, SettingsPropertyValueCollection settingsColl)
      94. {
      95. // Set the values in XML
      96. foreach (SettingsPropertyValue val in settingsColl)
      97. {
      98. this.SetSetting(val);
      99. }
      100. // Write the XML file to disk
      101. try
      102. {
      103. this.XMLConfig.Save(this.GetSettingsFilename());
      104. }
      105. catch (Exception ex)
      106. {
      107. // Create an informational message for the user if we cannot save the settings.
      108. // Enable whichever applies to your application type.
      109. // Uncomment the following line to enable a MessageBox for forms-based apps
      110. //System.Windows.Forms.MessageBox.Show(ex.Message, "Error writting configuration file to disk", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
      111. // Uncomment the following line to enable a console message for console-based apps
      112. Console.WriteLine("Error writing configuration file to disk: " + ex.Message);
      113. throw;
      114. }
      115. }
      116. private XmlDocument XMLConfig
      117. {
      118. get
      119. {
      120. // Check if we already have accessed the XML config file. If the xmlDoc object is empty, we have not.
      121. if (this.DataXml == null)
      122. {
      123. this.DataXml = new XmlDocument();
      124. // If we have not loaded the config, try reading the file from disk.
      125. try
      126. {
      127. // If the file does not exist on disk, catch the exception then create the XML template for the file.
      128. this.DataXml.Load(this.GetSettingsFilename());
      129. }
      130. catch
      131. {
      132. this.InitAppNode();
      133. // XML Declaration
      134. // <?xml version="1.0" encoding="utf-8"?>
      135. XmlDeclaration dec = this.DataXml.CreateXmlDeclaration("1.0", "utf-8", null);
      136. this.DataXml.AppendChild(dec);
      137. // Create root node and append to the document
      138. // <configuration>
      139. XmlElement rootNode = this.DataXml.CreateElement(XmlRoot);
      140. this.DataXml.AppendChild(rootNode);
      141. // Create Configuration Sections node and add as the first node under the root
      142. // <configSections>
      143. XmlElement configNode = this.DataXml.CreateElement(ConfigNode);
      144. this.DataXml.DocumentElement.PrependChild(configNode);
      145. // Create the user settings section group declaration and append to the config node above
      146. // <sectionGroup name="userSettings"...>
      147. XmlElement groupNode = this.DataXml.CreateElement(GroupNode);
      148. groupNode.SetAttribute("name", UserNode);
      149. groupNode.SetAttribute("type", "System.Configuration.UserSettingsGroup");
      150. configNode.AppendChild(groupNode);
      151. // Create the Application section declaration and append to the groupNode above
      152. // <section name="AppName.Properties.Settings"...>
      153. XmlElement newSection = this.DataXml.CreateElement("section");
      154. newSection.SetAttribute("name", this.AppNode);
      155. newSection.SetAttribute("type", "System.Configuration.ClientSettingsSection");
      156. groupNode.AppendChild(newSection);
      157. // Create the userSettings node and append to the root node
      158. // <userSettings>
      159. XmlElement userNode = this.DataXml.CreateElement(UserNode);
      160. this.DataXml.DocumentElement.AppendChild(userNode);
      161. // Create the Application settings node and append to the userNode above
      162. // <AppName.Properties.Settings>
      163. XmlElement appNode = this.DataXml.CreateElement(this.AppNode);
      164. userNode.AppendChild(appNode);
      165. }
      166. }
      167. return this.DataXml;
      168. }
      169. }
      170. // Retrieve values from the configuration file, or if the setting does not exist in the file,
      171. // retrieve the value from the application's default configuration
      172. private object GetSetting(SettingsProperty setProp)
      173. {
      174. object retVal;
      175. try
      176. {
      177. // Search for the specific settings node we are looking for in the configuration file.
      178. // If it exists, return the InnerText or InnerXML of its first child node, depending on the setting type.
      179. // If the setting is serialized as a string, return the text stored in the config
      180. if (setProp.SerializeAs.ToString() == "String")
      181. {
      182. return XMLConfig.SelectSingleNode("//setting[@name='" + setProp.Name + "']").FirstChild.InnerText;
      183. }
      184. // If the setting is stored as XML, deserialize it and return the proper object. This only supports
      185. // StringCollections at the moment - I will likely add other types as I use them in applications.
      186. else
      187. {
      188. string settingType = setProp.PropertyType.ToString();
      189. string xmlData = XMLConfig.SelectSingleNode("//setting[@name='" + setProp.Name + "']").FirstChild.InnerXml;
      190. XmlSerializer xs = new XmlSerializer(typeof(string[]));
      191. string[] data = (string[])xs.Deserialize(new XmlTextReader(xmlData, XmlNodeType.Element, null));
      192. switch (settingType)
      193. {
      194. case "System.Collections.Specialized.StringCollection":
      195. StringCollection sc = new StringCollection();
      196. sc.AddRange(data);
      197. return sc;
      198. default:
      199. return "";
      200. }
      201. }
      202. }
      203. catch
      204. {
      205. // Check to see if a default value is defined by the application.
      206. // If so, return that value, using the same rules for settings stored as Strings and XML as above
      207. if ((setProp.DefaultValue != null))
      208. {
      209. if (setProp.SerializeAs.ToString() == "String")
      210. {
      211. retVal = setProp.DefaultValue.ToString();
      212. }
      213. else
      214. {
      215. string settingType = setProp.PropertyType.ToString();
      216. string xmlData = setProp.DefaultValue.ToString();
      217. XmlSerializer xs = new XmlSerializer(typeof(string[]));
      218. string[] data = (string[])xs.Deserialize(new XmlTextReader(xmlData, XmlNodeType.Element, null));
      219. switch (settingType)
      220. {
      221. case "System.Collections.Specialized.StringCollection":
      222. StringCollection sc = new StringCollection();
      223. sc.AddRange(data);
      224. return sc;
      225. default:
      226. return "";
      227. }
      228. }
      229. }
      230. else
      231. {
      232. retVal = "";
      233. }
      234. }
      235. return retVal;
      236. }
      237. private void SetSetting(SettingsPropertyValue setProp)
      238. {
      239. // Define the XML path under which we want to write our settings if they do not already exist
      240. XmlNode SettingNode = null;
      241. try
      242. {
      243. // Search for the specific settings node we want to update.
      244. // If it exists, return its first child node, (the <value>data here</value> node)
      245. SettingNode = XMLConfig.SelectSingleNode("//setting[@name='" + setProp.Name + "']").FirstChild;
      246. }
      247. catch
      248. {
      249. SettingNode = null;
      250. }
      251. // If we have a pointer to an actual XML node, update the value stored there
      252. if (SettingNode != null)
      253. {
      254. if (setProp.Property.SerializeAs.ToString() == "String")
      255. {
      256. SettingNode.InnerText = setProp.SerializedValue.ToString();
      257. }
      258. else
      259. {
      260. // Write the object to the config serialized as Xml - we must remove the Xml declaration when writing
      261. // the value, otherwise .Net's configuration system complains about the additional declaration.
      262. SettingNode.InnerXml = setProp.SerializedValue.ToString().Replace(@"<?xml version=""1.0"" encoding=""utf-16""?>", "");
      263. }
      264. }
      265. else
      266. {
      267. this.InitAppNode();
      268. // If the value did not already exist in this settings file, create a new entry for this setting
      269. // Search for the application settings node (<Appname.Properties.Settings>) and store it.
      270. XmlNode tmpNode = XMLConfig.SelectSingleNode("//" + this.AppNode);
      271. // Create a new settings node and assign its name as well as how it will be serialized
      272. XmlElement newSetting = this.DataXml.CreateElement("setting");
      273. newSetting.SetAttribute("name", setProp.Name);
      274. if (setProp.Property.SerializeAs.ToString() == "String")
      275. {
      276. newSetting.SetAttribute("serializeAs", "String");
      277. }
      278. else
      279. {
      280. newSetting.SetAttribute("serializeAs", "Xml");
      281. }
      282. // Append this node to the application settings node (<Appname.Properties.Settings>)
      283. tmpNode.AppendChild(newSetting);
      284. // Create an element under our named settings node, and assign it the value we are trying to save
      285. XmlElement valueElement = this.DataXml.CreateElement("value");
      286. if (setProp.Property.SerializeAs.ToString() == "String")
      287. {
      288. valueElement.InnerText = setProp.SerializedValue.ToString();
      289. }
      290. else
      291. {
      292. // Write the object to the config serialized as Xml - we must remove the Xml declaration when writing
      293. // the value, otherwise .Net's configuration system complains about the additional declaration
      294. valueElement.InnerXml = setProp.SerializedValue.ToString().Replace(@"<?xml version=""1.0"" encoding=""utf-16""?>", "");
      295. }
      296. // Append this new element under the setting node we created above
      297. newSetting.AppendChild(valueElement);
      298. }
      299. }
      300. }
      301. }
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      Jetzt kann man den Speicherpfad, wo die Settings gehalten werden, selbst bestimmen...
      DirectorySettingsProvider

      VB.NET-Quellcode

      1. Imports System.Configuration
      2. Imports System.ComponentModel
      3. Imports System.Reflection
      4. Imports System.IO
      5. Imports System.Security.Cryptography
      6. Imports System.Runtime.Serialization.Formatters.Binary
      7. Imports System.Collections.Specialized
      8. Namespace MyProvider
      9. ''' <summary>
      10. ''' Dieser Provider speichert Settings-Einstellungen in einer eigenen .exe.config.dat-Datei.
      11. ''' Diese Datei liegt im Arbeitsverzeichnis der Anwendung.
      12. ''' Sie kann aber auch benutzerspezifisch abgelegt werden...
      13. ''' HINWEISE:
      14. ''' Die ApplicationName-Property erstellt die ProduktName-Eigenschaft von der My.Application.Info.ProduktName
      15. ''' Hier sollte in den Projekteigenschaften/Anwendung/Assemblyinformationen die Eigenschaft ProduktName=AssemblyName gestellt sein!
      16. '''
      17. ''' Es liegt dem Provider eine eigene DirectorySettings-Class zu Grunde, die die benutzerdefinierte Path-Einstellung bereit hält.
      18. ''' (siehe weiter unten in der #Region "Directory Settings")
      19. ''' </summary>
      20. Public Class DirectorySettingsProvider
      21. Inherits SettingsProvider
      22. Implements IApplicationSettingsProvider
      23. Private Enum IO
      24. Load = 0
      25. Save = 1
      26. End Enum
      27. Private Datas As New Dictionary(Of String, Object)()
      28. Private Property DataFile As FileInfo
      29. ''' <summary>
      30. ''' Initialisiert den Anbieter (Provider)
      31. ''' </summary>
      32. Public Overrides Sub Initialize(name As String, config As NameValueCollection)
      33. If String.IsNullOrEmpty(name) Then name = Me.Name
      34. MyBase.Initialize(name, config)
      35. End Sub
      36. ''' <summary>
      37. ''' Anbieternamen festlegen (Provider)
      38. ''' </summary>
      39. Public Overrides ReadOnly Property Name As String
      40. Get
      41. Return "DirectorySettingsProvider"
      42. End Get
      43. End Property
      44. ''' <summary>
      45. ''' Der Provider ruft den Namen der aktuell ausgeführten Anwendung ab oder legt diesen fest.
      46. ''' Hinweis: hier wird der Produktname von der AssemblyInfo ausgewertet
      47. ''' </summary>
      48. Public Overrides Property ApplicationName() As String
      49. ''' <summary>
      50. ''' SetPropertyValues wird erst abgearbeitet, wenn ApplicationSettingsBase.Save durchgeführt wird.
      51. ''' ApplicationSettingsBase stellt sicher, dass für jeden einzelnen Provider nur seine markierten Werte herangenommen werden.
      52. ''' Egal, ob der Provider auf einer Einzel-Einstellung angesetzt ist oder ob klassenweit die Einstellung angegeben wurde.
      53. ''' Wenn Einstellungen nicht verändert wurden, müssen sie nicht gespeichert werden!
      54. ''' Anwendungsspezifische Einstellungen können nicht geändert werden und werden daher auch nicht gespeichert!
      55. ''' </summary>
      56. ''' <param name="context"></param>
      57. ''' <param name="collection"></param>
      58. Public Overrides Sub SetPropertyValues(ByVal context As SettingsContext, ByVal collection As SettingsPropertyValueCollection)
      59. For Each prpValue As SettingsPropertyValue In collection
      60. If Not prpValue.IsDirty OrElse (prpValue.SerializedValue Is Nothing) Then Continue For
      61. If IsApplicationScoped(prpValue.Property) Then Continue For
      62. Datas(prpValue.Name) = prpValue.SerializedValue
      63. Next
      64. persistsFile(IO.Save, context)
      65. End Sub
      66. Public Overrides Function GetPropertyValues(ByVal context As SettingsContext, ByVal collection As SettingsPropertyCollection) As SettingsPropertyValueCollection
      67. persistsFile(IO.Load, context)
      68. Dim col As New SettingsPropertyValueCollection()
      69. For Each prp As SettingsProperty In collection
      70. Dim Value As New SettingsPropertyValue(prp)
      71. If Datas.ContainsKey(prp.Name) Then Value = GetPropertyValue(prp) ' Wert von Datas einlesen
      72. col.Add(Value)
      73. Next
      74. Return col
      75. End Function
      76. ''' <summary>
      77. ''' Nur benutzerspezifische Eigenschaften sind zulässig.
      78. ''' Wenn eine Eigenschaft keinen Wert hat, wird der Defaultwert geladen.
      79. ''' </summary>
      80. ''' <param name="prp"></param>
      81. Private Function GetPropertyValue(ByVal prp As SettingsProperty) As SettingsPropertyValue
      82. Dim value As New SettingsPropertyValue(prp)
      83. If IsUserScoped(prp) Then value.SerializedValue = Datas(prp.Name)
      84. value.IsDirty = False
      85. Return value
      86. End Function
      87. ''' <summary>
      88. ''' Test auf anwendungsspezifische Eigenschaft
      89. ''' </summary>
      90. ''' <param name="prop"></param>
      91. Private Function IsApplicationScoped(ByVal prop As SettingsProperty) As Boolean
      92. Return HasSettingScope(prop, GetType(ApplicationScopedSettingAttribute))
      93. End Function
      94. ''' <summary>
      95. ''' Test auf benutzerdefinierte Eigenschaft
      96. ''' </summary>
      97. ''' <param name="prop"></param>
      98. Private Function IsUserScoped(prop As SettingsProperty) As Boolean
      99. Return HasSettingScope(prop, GetType(UserScopedSettingAttribute))
      100. End Function
      101. ''' <summary>
      102. ''' prüft auf erlaubte Einstellung, so wie es der LocalFileSettingsProvider auch macht ...
      103. ''' </summary>
      104. ''' <param name="prop"></param>
      105. ''' <param name="attributeType"></param>
      106. Private Function HasSettingScope(ByVal prop As SettingsProperty, ByVal attributeType As Type) As Boolean
      107. Dim isAppScoped As Boolean = prop.Attributes(GetType(ApplicationScopedSettingAttribute)) IsNot Nothing
      108. Dim isUserScoped As Boolean = prop.Attributes(GetType(UserScopedSettingAttribute)) IsNot Nothing
      109. If isUserScoped AndAlso isAppScoped Then Throw New Exception("BothScopeAttributes: " & prop.Name)
      110. If Not isUserScoped AndAlso Not isAppScoped Then Throw New Exception("NoScopeAttributes: " & prop.Name)
      111. Select Case True
      112. Case attributeType Is GetType(ApplicationScopedSettingAttribute) : Return isAppScoped
      113. Case attributeType Is GetType(UserScopedSettingAttribute) : Return isUserScoped
      114. Case Else : Return False
      115. End Select
      116. End Function
      117. #Region "IApplicationSettingsProvider Members"
      118. ''' <summary>
      119. ''' Abrufen der letzten Version der Settingseigenschaft
      120. ''' </summary>
      121. ''' <param name="context"></param>
      122. ''' <param name="property"></param>
      123. Public Function GetPreviousVersion(ByVal context As SettingsContext, ByVal [property] As SettingsProperty) As SettingsPropertyValue Implements IApplicationSettingsProvider.GetPreviousVersion
      124. Dim prpValue As New SettingsPropertyValue([property])
      125. If Datas.ContainsKey([property].Name) Then prpValue.PropertyValue = Datas([property].Name)
      126. Return prpValue
      127. End Function
      128. ''' <summary>
      129. ''' Wieder auf die Defaultwerte der Settings zurücksetzen!
      130. ''' </summary>
      131. ''' <param name="context"></param>
      132. Public Sub Reset(ByVal context As SettingsContext) Implements IApplicationSettingsProvider.Reset
      133. SetDataFile(context)
      134. If DataFile.Exists Then Datas.Clear() : PersistsFile(IO.Save, context)
      135. End Sub
      136. ''' <summary>
      137. ''' ErfinderDesRades
      138. ''' Fehlende Settings werden sofort in der Sammlung entfernt
      139. ''' Neue hinzugekommene Settings werden beim nächsten Speichervorgang gesichert
      140. ''' </summary>
      141. ''' <param name="context"></param>
      142. ''' <param name="properties"></param>
      143. Public Sub Upgrade(ByVal context As SettingsContext, ByVal properties As SettingsPropertyCollection) Implements IApplicationSettingsProvider.Upgrade
      144. If Datas.Count = 0 Then PersistsFile(IO.Load, context)
      145. Dim toDelete = Datas.Keys.Except(properties.Cast(Of SettingsProperty).Select(Function(p) p.Name)).ToList
      146. toDelete.ForEach(AddressOf Datas.Remove)
      147. End Sub
      148. #End Region
      149. #Region "Load/Save Binary"
      150. ''' <summary>
      151. ''' Nach Inhalt des SettingsContext wird die Zuteilung des DataFiles bestimmt.
      152. ''' </summary>
      153. ''' <param name="context"></param>
      154. Private Sub SetDataFile(context As SettingsContext)
      155. Dim fi = New FileInfo(Assembly.GetExecutingAssembly.Location)
      156. Dim AppPath = fi.Directory.ToString
      157. Select Case True
      158. Case context("SettingsClassType").ToString.Contains("DirectorySettings") ' Directory-Daten werden im Anwendungspfad gehalten
      159. DataFile = New FileInfo(Path.Combine(AppPath, String.Concat(ApplicationName, ".dat")))
      160. Case Else ' Settings-Daten
      161. Dim dir As New DirectorySettings
      162. Dim tmpPath = dir.DirectoryName
      163. Dim tmpFile = dir.FileName
      164. If String.IsNullOrEmpty(tmpPath) OrElse String.IsNullOrEmpty(tmpFile) Then
      165. DataFile = New FileInfo(Path.Combine(AppPath, String.Concat(ApplicationName, ".exe.config.dat"))) ' Anwendungspfad
      166. Else
      167. DataFile = New FileInfo(Path.Combine(tmpPath, tmpFile)) ' benutzerdefinierter Pfad
      168. End If
      169. End Select
      170. End Sub
      171. Private Sub PersistsFile(Method As IO, context As SettingsContext)
      172. SetDataFile(context)
      173. If Method = IO.Load Then
      174. If DataFile.Exists Then Datas = LoadBinary(DataFile)
      175. ElseIf Method = IO.Save Then
      176. SaveBinary(DataFile, Datas)
      177. End If
      178. End Sub
      179. ''' <summary>
      180. ''' Daten binär speichern
      181. ''' </summary>
      182. ''' <param name="DataFile"></param>
      183. ''' <param name="Datas"></param>
      184. Private Sub SaveBinary(DataFile As FileInfo, Datas As Dictionary(Of String, Object))
      185. ' DeleteLocalPathUserSettings()
      186. Using fs As Stream = File.Create(DataFile.FullName)
      187. Dim binfmt As New BinaryFormatter()
      188. binfmt.Serialize(fs, Datas)
      189. End Using
      190. End Sub
      191. ' ''' <summary>
      192. ' ''' falls der LocalUserAppDataPath vorhanden ist,
      193. ' ''' wird der Ordner mit allen untergeordneten Verzeichnissen gelöscht
      194. ' ''' </summary>
      195. ' Private Sub DeleteLocalPathUserSettings()
      196. ' Dim di As New DirectoryInfo(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath)
      197. ' di = di.Parent.Parent.Parent
      198. ' If di.Exists Then di.Delete(True)
      199. ' End Sub
      200. ''' <summary>
      201. ''' binäre Daten laden
      202. ''' </summary>
      203. ''' <param name="DataFile"></param>
      204. Private Function LoadBinary(DataFile As FileInfo) As Dictionary(Of String, Object)
      205. Using fs As Stream = File.Open(DataFile.FullName, FileMode.Open)
      206. Dim binfmt As New BinaryFormatter()
      207. Return CType(binfmt.Deserialize(fs), Dictionary(Of String, Object))
      208. End Using
      209. End Function
      210. #End Region
      211. End Class
      212. End Namespace

      Dieser Provider benötigt noch eine extra Settings-Klasse, wo der benutzerdefinierte Pfad abgelegt wird:
      DirectorySettings-Class

      VB.NET-Quellcode

      1. Namespace MyProvider
      2. ''' <summary>
      3. ''' Diese Klasse versorgt den benutzerdefinierten SettingsPath.
      4. ''' </summary>
      5. <SettingsProvider(GetType(DirectorySettingsProvider))> _
      6. Public NotInheritable Class DirectorySettings
      7. Inherits ApplicationSettingsBase
      8. ''' <summary>
      9. ''' Hier wird das Verzeichnis gehalten
      10. ''' </summary>
      11. <UserScopedSettingAttribute(), DefaultSettingValueAttribute("")> _
      12. Public Property DirectoryName As String
      13. Get
      14. Return CType(Me("DirectoryName"), String)
      15. End Get
      16. Set(ByVal value As String)
      17. Me("DirectoryName") = value
      18. End Set
      19. End Property
      20. ''' <summary>
      21. ''' Hier wird der Filename gehalten
      22. ''' </summary>
      23. <UserScopedSettingAttribute(), DefaultSettingValueAttribute("")> _
      24. Public Property FileName As String
      25. Get
      26. Return CType(Me("FileName"), String)
      27. End Get
      28. Set(ByVal value As String)
      29. Me("FileName") = value
      30. End Set
      31. End Property
      32. End Class
      33. End Namespace

      Per Code kann man dann seinen selbstdefinierten Speicherpfad für die Settings angeben...
      (d.h. außerhalb des Providers...)

      VB.NET-Quellcode

      1. 'Beispiel:
      2. Dim mySettings As New DirectorySettings
      3. mySettings.DirectoryName = "C:\"
      4. mySettings.FileName = "DirectorySettingsTest.config.dat"
      5. mySettings.Save() ' muss explizit gepeichert werden...

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VB1963“ ()

      @VB1963 @ErfinderDesRades
      Das dürfte Euch beide interessieren.
      1. habe ich festgestellt, dass beim Binden des Settings FormSize an die Property ClientSize das Form-Design Anchor flöten geht.
        Leider hab ich da erst mal nix anderes gefunden, als das zu Fuß zu binden:

        C#-Quellcode

        1. Settings.Default.FormSize = this.Size; // Form_Closing
        2. Properties.Settings.Default.Save();
        3. // bzw.
        4. this.Size = Settings.Default.FormSize; // Form1_Load

      2. habe ich wieder mit XML-Settings experimentiert, weil mir die von mir vorgeschlagene Methode bei einem Projekt um die Ohren geflogen ist.
        Daraufhin habe ich den SettingsProviderBin von @VB1963 dahingehend modifiziert, dass ich das Dictionary<string, object> in ein SerializableDictionary<TKey, TValue> (Quelle hier) überführt habe.
      3. Desweiteren habe ich festgestellt, dass (wahrscheinlich) alle Typen von Settings als String serialisiert werden, auch eine System.Collections.Specialized.StringCollection, so dass bei Verwendung von XmlSerializer(typeof(string)) das ganze XML-Handling elementar einfach wird. Unter Weglassung der vielen XML-Start- und Endelemente sieht die Klasse SerializableDictionary nun so aus:
      Spoiler anzeigen

      C#-Quellcode

      1. /// <summary>
      2. /// Represents an XML serializable collection of keys and values.
      3. /// </summary>
      4. /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
      5. /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
      6. [XmlRoot("Settings")]
      7. public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
      8. {
      9. #region Constants
      10. /// <summary>
      11. /// The default XML tag name for an item.
      12. /// </summary>
      13. private const string ItemTagName = "Item";
      14. /// <summary>
      15. /// The default XML tag name for a key.
      16. /// </summary>
      17. private const string KeyTagName = "Key";
      18. /// <summary>
      19. /// The default XML tag name for a value.
      20. /// </summary>
      21. private const string ValueTagName = "Value";
      22. #endregion
      23. #region Public Methods
      24. /// <summary>
      25. /// Gets the XML schema for the XML serialization.
      26. /// </summary>
      27. /// <returns>An XML schema for the serialized object.</returns>
      28. public XmlSchema GetSchema()
      29. {
      30. // für IXmlSerializable
      31. return null;
      32. }
      33. /// <summary>
      34. /// Deserializes the object from XML.
      35. /// </summary>
      36. /// <param name="reader">The XML representation of the object.</param>
      37. public void ReadXml(XmlReader reader)
      38. {
      39. XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
      40. //XmlSerializer valueSerialObject = new XmlSerializer(typeof(TValue));
      41. XmlSerializer valueSerialString = new XmlSerializer(typeof(string));
      42. object obj;
      43. while (reader.NodeType != XmlNodeType.EndElement)
      44. {
      45. reader.ReadStartElement(ItemTagName);
      46. obj = reader.ReadElementString(KeyTagName);
      47. TKey key = (TKey)obj;
      48. obj = reader.ReadElementString(ValueTagName);
      49. TValue value = (TValue)obj;
      50. this.Add(key, value);
      51. reader.ReadEndElement();
      52. reader.MoveToContent();
      53. }
      54. reader.ReadEndElement();
      55. }
      56. /// <summary>
      57. /// Serializes this instance to XML.
      58. /// </summary>
      59. /// <param name="writer">The writer to serialize to.</param>
      60. public void WriteXml(XmlWriter writer)
      61. {
      62. XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
      63. //XmlSerializer valueSerialObject = new XmlSerializer(typeof(TValue));
      64. XmlSerializer valueSerialString = new XmlSerializer(typeof(string));
      65. foreach (TKey key in this.Keys)
      66. {
      67. TValue value = this[key];
      68. Type T = value.GetType();
      69. writer.WriteStartElement(ItemTagName);
      70. writer.WriteElementString(KeyTagName, key.ToString());
      71. writer.WriteElementString(ValueTagName, value.ToString());
      72. writer.WriteEndElement();
      73. }
      74. }
      75. #endregion
      76. }
      Die generierte XML-Datei sieht so aus:
      TestUserSettingsPovider.exe.config.xml
      -------
      Sorry, die ganze neue Klasse UserSettingsProviderXml mit Serialisierung und Deserialisierung:
      Spoiler anzeigen

      C#-Quellcode

      1. using System;
      2. using System.Configuration;
      3. using System.Collections.Generic;
      4. using System.Collections.Specialized;
      5. using System.IO;
      6. using System.Reflection;
      7. using System.Linq;
      8. using System.Xml;
      9. using System.Xml.Serialization;
      10. using System.Xml.Schema;
      11. // http://stackoverflow.com/questions/12554186/how-to-serialize-deserialize-to-dictionaryint-string-from-custom-xml-not-us
      12. namespace UserSettingsProvider
      13. {
      14. /// <summary>
      15. /// Dieser Provider speichert die betroffenen Settings-Einstellungen in eine eigene .exe.config.dat-Datei.
      16. /// Diese Datei liegt im Arbeitsverzeichnis der Anwendung.
      17. /// HINWEISE:
      18. /// Die ApplicationName-Property erstellt die ProduktName-Eigenschaft von der My.Application.Info.ProduktName
      19. /// Hier sollte in den Projekteigenschaften/Anwendung/Assemblyinformationen die Eigenschaft ProduktName = AssemblyName gestellt sein!
      20. /// </summary>
      21. /// <remarks></remarks>
      22. public class UserSettingsProviderXml
      23. : SettingsProvider, IApplicationSettingsProvider
      24. {
      25. /// <summary>
      26. /// Represents an XML serializable collection of keys and values.
      27. /// </summary>
      28. /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
      29. /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
      30. [XmlRoot("Settings")]
      31. public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
      32. {
      33. #region Constants
      34. /// <summary>
      35. /// The default XML tag name for an item.
      36. /// </summary>
      37. private const string ItemTagName = "Item";
      38. /// <summary>
      39. /// The default XML tag name for a key.
      40. /// </summary>
      41. private const string KeyTagName = "Key";
      42. /// <summary>
      43. /// The default XML tag name for a value.
      44. /// </summary>
      45. private const string ValueTagName = "Value";
      46. #endregion
      47. #region Public Methods
      48. /// <summary>
      49. /// Gets the XML schema for the XML serialization.
      50. /// </summary>
      51. /// <returns>An XML schema for the serialized object.</returns>
      52. public XmlSchema GetSchema()
      53. {
      54. // für IXmlSerializable
      55. return null;
      56. }
      57. /// <summary>
      58. /// Deserializes the object from XML.
      59. /// </summary>
      60. /// <param name="reader">The XML representation of the object.</param>
      61. public void ReadXml(XmlReader reader)
      62. {
      63. XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
      64. //XmlSerializer valueSerialObject = new XmlSerializer(typeof(TValue));
      65. XmlSerializer valueSerialString = new XmlSerializer(typeof(string));
      66. object obj;
      67. while (reader.NodeType != XmlNodeType.EndElement)
      68. {
      69. reader.ReadStartElement(ItemTagName);
      70. obj = reader.ReadElementString(KeyTagName);
      71. TKey key = (TKey)obj;
      72. obj = reader.ReadElementString(ValueTagName);
      73. TValue value = (TValue)obj;
      74. this.Add(key, value);
      75. reader.ReadEndElement();
      76. reader.MoveToContent();
      77. }
      78. reader.ReadEndElement();
      79. }
      80. /// <summary>
      81. /// Serializes this instance to XML.
      82. /// </summary>
      83. /// <param name="writer">The writer to serialize to.</param>
      84. public void WriteXml(XmlWriter writer)
      85. {
      86. XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
      87. //XmlSerializer valueSerialObject = new XmlSerializer(typeof(TValue));
      88. XmlSerializer valueSerialString = new XmlSerializer(typeof(string));
      89. foreach (TKey key in this.Keys)
      90. {
      91. TValue value = this[key];
      92. //Type T = value.GetType();
      93. writer.WriteStartElement(ItemTagName);
      94. writer.WriteElementString(KeyTagName, key.ToString());
      95. writer.WriteElementString(ValueTagName, value.ToString());
      96. writer.WriteEndElement();
      97. }
      98. }
      99. #endregion
      100. }
      101. private SerializableDictionary<string, object> DataXml;
      102. // hier wird der Produktname von der Assembly angegeben
      103. public override string ApplicationName { get; set; }
      104. private FileInfo DataFile { get; set; }
      105. private string AppPath { get; set; }
      106. public UserSettingsProviderXml()
      107. {
      108. dynamic fi = new FileInfo(Assembly.GetExecutingAssembly().Location);
      109. this.ApplicationName = fi.Name.Replace(fi.Extension, string.Empty);
      110. this.DataXml = new SerializableDictionary<string, object>();
      111. this.DataFile = null;
      112. }
      113. public override void Initialize(string name, NameValueCollection config)
      114. {
      115. System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(ApplicationName), "ApplicationName is empty");
      116. base.Initialize(this.ApplicationName, config);
      117. }
      118. /// <summary>
      119. /// SetPropertyValues wird erst abgearbeitet, wenn ApplicationSettingsBase.Save durchgeführt wird.
      120. /// ApplicationSettingsBase stellt sicher, dass für jeden einzelnen Provider nur seine markierten Werte herangenommen werden.
      121. /// Egal, ob der Provider auf einer Einzel-Einstellung angesetzt ist oder ob klassenweit die Einstellung angegeben wurde.
      122. /// Wenn Einstellungen nicht verändert wurden, müssen sie nicht gespeichert werden!
      123. /// Anwendungsspezifische Einstellungen können nicht geändert werden und werden daher auch nicht gespeichert!
      124. /// </summary>
      125. /// <param name="context"></param>
      126. /// <param name="collection"></param>
      127. public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
      128. {
      129. foreach (SettingsPropertyValue prpValue in collection)
      130. {
      131. if (!prpValue.IsDirty || (prpValue.SerializedValue == null))
      132. {
      133. continue;
      134. }
      135. if (this.IsApplicationScoped(prpValue.Property))
      136. {
      137. continue;
      138. }
      139. this.DataXml[prpValue.Name] = prpValue.SerializedValue;
      140. }
      141. this.SaveSettings();
      142. }
      143. public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
      144. {
      145. this.SetDataFile();
      146. if (this.DataFile.Exists)
      147. {
      148. this.LoadSettings();
      149. }
      150. SettingsPropertyValueCollection col = new SettingsPropertyValueCollection();
      151. foreach (SettingsProperty prp in collection)
      152. {
      153. SettingsPropertyValue value = new SettingsPropertyValue(prp);
      154. if (this.DataXml.ContainsKey(prp.Name))
      155. {
      156. value = GetPropertyValue(prp);
      157. }
      158. // Wert von Datas einlesen
      159. col.Add(value);
      160. }
      161. return col;
      162. }
      163. /// <summary>
      164. /// Nur benutzerspezifische Eigenschaften sind zulässig.
      165. /// Wenn eine Eigenschaft keinen Wert hat, wird der Defaultwert geladen.
      166. /// </summary>
      167. /// <param name="prp"></param>
      168. private SettingsPropertyValue GetPropertyValue(SettingsProperty prp)
      169. {
      170. SettingsPropertyValue value = new SettingsPropertyValue(prp);
      171. if (this.IsUserScoped(prp))
      172. {
      173. value.SerializedValue = this.DataXml[prp.Name];
      174. }
      175. value.IsDirty = false;
      176. return value;
      177. }
      178. /// <summary>
      179. /// Test auf anwendungsspezifische Eigenschaft
      180. /// </summary>
      181. /// <param name="prop"></param>
      182. private bool IsApplicationScoped(SettingsProperty prop)
      183. {
      184. return HasSettingScope(prop, typeof(ApplicationScopedSettingAttribute));
      185. }
      186. /// <summary>
      187. /// Test auf benutzerdefinierte Eigenschaft
      188. /// </summary>
      189. /// <param name="prop"></param>
      190. private bool IsUserScoped(SettingsProperty prop)
      191. {
      192. return HasSettingScope(prop, typeof(UserScopedSettingAttribute));
      193. }
      194. /// <summary>
      195. /// prüft auf erlaubte Einstellung, so wie es der LocalFileSettingsProvider auch macht ...
      196. /// </summary>
      197. /// <param name="prop"></param>
      198. /// <param name="attributeType"></param>
      199. private bool HasSettingScope(SettingsProperty prop, Type attributeType)
      200. {
      201. bool isAppScoped = prop.Attributes[typeof(ApplicationScopedSettingAttribute)] != null;
      202. bool isUserScoped = prop.Attributes[typeof(UserScopedSettingAttribute)] != null;
      203. if (isUserScoped && isAppScoped)
      204. {
      205. throw new ConfigurationErrorsException("Both Scope Attributes: " + prop.Name);
      206. }
      207. if (!isUserScoped && !isAppScoped)
      208. {
      209. throw new ConfigurationErrorsException("No Scope Attributes: " + prop.Name);
      210. }
      211. if (object.ReferenceEquals(attributeType, typeof(ApplicationScopedSettingAttribute)))
      212. {
      213. return isAppScoped;
      214. }
      215. if (object.ReferenceEquals(attributeType, typeof(UserScopedSettingAttribute)))
      216. {
      217. return isUserScoped;
      218. }
      219. return false;
      220. }
      221. /// <summary>
      222. /// Datenfile der gerade aktiven Anwendung bestimmen
      223. /// </summary>
      224. /// <remarks></remarks>
      225. private void SetDataFile()
      226. {
      227. FileInfo fi = new FileInfo(Assembly.GetExecutingAssembly().Location);
      228. this.AppPath = fi.DirectoryName;
      229. this.DataFile = new FileInfo(Path.Combine(this.AppPath, string.Concat(this.ApplicationName, ".config.xml")));
      230. }
      231. /// <summary>
      232. /// binäre Daten laden
      233. /// </summary>
      234. /// <param name="DataFile"></param>
      235. private void LoadSettings()
      236. {
      237. this.SetDataFile();
      238. using (XmlReader reader = XmlReader.Create(this.DataFile.FullName))
      239. {
      240. reader.ReadStartElement("Settings");
      241. this.DataXml.ReadXml(reader);
      242. }
      243. }
      244. /// <summary>
      245. /// Daten binär speichern
      246. /// </summary>
      247. /// <param name="DataFile"></param>
      248. /// <param name="Datas"></param>
      249. private void SaveSettings()
      250. {
      251. this.SetDataFile();
      252. this.DeleteLocalPathUserSettings();
      253. using (XmlWriter writer = XmlWriter.Create(this.DataFile.FullName))
      254. {
      255. writer.WriteStartElement("Settings");
      256. this.DataXml.WriteXml(writer);
      257. writer.WriteEndElement();
      258. writer.Flush();
      259. }
      260. }
      261. /// <summary>
      262. /// falls der LocalUserAppDataPath vorhanden ist,
      263. /// wird der Ordner mit allen untergeordneten Verzeichnissen gelöscht
      264. /// </summary>
      265. /// <remarks></remarks>
      266. private void DeleteLocalPathUserSettings()
      267. {
      268. DirectoryInfo di = new DirectoryInfo(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath);
      269. di = di.Parent.Parent.Parent;
      270. if (di.Exists)
      271. {
      272. di.Delete(true);
      273. }
      274. }
      275. #region "IApplicationSettingsProvider Members"
      276. /// <summary>
      277. /// Abrufen der letzten Version der Settingseigenschaft
      278. /// </summary>
      279. /// <param name="context"></param>
      280. /// <param name="property"></param>
      281. public SettingsPropertyValue GetPreviousVersion(SettingsContext context, SettingsProperty property)
      282. {
      283. SettingsPropertyValue prpValue = new SettingsPropertyValue(property);
      284. if (this.DataXml.ContainsKey(property.Name))
      285. {
      286. prpValue.PropertyValue = this.DataXml[property.Name];
      287. }
      288. return prpValue;
      289. }
      290. /// <summary>
      291. /// wieder auf die Defaultwerte der Settings zurücksetzen!
      292. /// </summary>
      293. /// <param name="context"></param>
      294. public void Reset(SettingsContext context)
      295. {
      296. if (this.DataFile.Exists)
      297. {
      298. this.DataXml.Clear();
      299. this.SaveSettings();
      300. }
      301. }
      302. /// <summary>
      303. /// ErfinderDesRades
      304. /// fehlende Settings werden sofort der Sammlung entfernt
      305. /// neue hinzugekommene Settings werden beim nächsten Speichervorgang gesichert
      306. /// </summary>
      307. /// <param name="context"></param>
      308. /// <param name="properties"></param>
      309. public void Upgrade(SettingsContext context, SettingsPropertyCollection properties)
      310. {
      311. if (this.DataXml.Count == 0 && this.DataFile.Exists)
      312. {
      313. this.LoadSettings();
      314. }
      315. List<string> toDelete = this.DataXml.Keys.Except(from SettingsProperty prop in properties select prop.Name).ToList<string>();
      316. toDelete.ForEach(delegate(string str)
      317. {
      318. this.DataXml.Remove(str);
      319. });
      320. }
      321. #endregion
      322. }
      323. }

      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ ()