2009-01-17 30 views
489

我想實現的非常簡單:我有一個使用路徑讀取信息的Windows窗體(.NET 3.5)應用程序。該路徑可以由用戶通過使用我提供的選項來修改。在Windows窗體應用程序中保存應用程序設置的最佳實踐

現在,我想的路徑值保存到以後使用的文件。這將是保存到此文件的許多設置之一。該文件將直接位於應用程序文件夾中。

我理解有三種選擇:

  • ConfigurationSettings文件(appname.exe.config)
  • 註冊
  • 自定義XML文件

我讀了.NET配置文件沒有預見到將價值儲蓄回去。 至於註冊表,我想盡可能遠離它。

這是否意味着我應該使用自定義XML文件來保存配置設置? 如果是這樣,我想看看(C#)的代碼示例。

我已經看到了關於這個問題的其他討論,但它仍然是我不清楚。

+0

這是一個.NET WinForms應用程序嗎?如果是這樣,你在開發什麼版本的.NET? – Portman 2009-01-17 13:20:38

+1

是的,它是一個.NET Framework版本3.5 WinForms應用程序。 – Fueled 2009-01-17 15:15:40

+0

你需要保存**密碼或祕密值**嗎?可能需要任何_encryption_ – Kiquenet 2015-09-21 06:31:41

回答

506

如果您使用Visual Studio,那麼獲取持久設置非常容易。在解決方案資源管理器中右鍵單擊項目,選擇屬性。選擇設置選項卡,如果設置不存在,請單擊超鏈接。使用設置選項卡來創建應用程序設置。 Visual Studio創建文件Settings.settingsSettings.Designer.settings,其中包含從ApplicationSettingsBase繼承的單例類Settings。你可以從你的代碼中訪問這個類來讀/寫應用程序設置:

Properties.Settings.Default["SomeProperty"] = "Some Value"; 
Properties.Settings.Default.Save(); // Saves settings in application configuration file 

這種技術既適用於控制檯,Windows Forms和其他項目類型。

請注意,您需要設置您的設置的範圍屬性。如果您選擇應用程序範圍,則選擇Settings.Default。 <你的財產>將是隻讀的。

63

該註冊表是一個禁行。您不確定使用您的應用程序的用戶是否具有足夠的權限來寫入註冊表。

您可以使用app.config文件保存應用程序級的設置(這是誰使用您的應用程序的每個用戶相同)。

我會將用戶特定的設置存儲在XML文件中,該文件將保存在Isolated StorageSpecialFolder.ApplicationData目錄中。

下一步,正如從.NET 2.0,它是能夠存儲值回app.config文件。

+7

但是,如果您想要每個登錄名/用戶設置,請使用註冊表。 – thenonhacker 2009-01-17 15:31:40

+16

註冊表不可僞裝 – 2009-01-17 15:35:45

+10

@thenonhacker:或使用Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) – 2009-09-10 14:02:46

2

據我所知,.NET確實使用內置的應用程序設置設施的支持堅持設置:

Windows窗體應用程序設置功能可以輕鬆地創建自定義的維護,存儲和應用程序和用戶首選項。使用Windows Forms應用程序設置,不僅可以存儲應用程序數據(如數據庫連接字符串),還可以存儲用戶特定的數據(如用戶應用程序首選項)。使用Visual Studio或自定義託管代碼,您可以創建新設置,將它們讀取並寫入磁盤,將它們綁定到表單上的屬性,並在加載和保存之前驗證設置數據。 - http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx

17

ApplicationSettings類不支持將設置保存到app.config文件。這非常符合設計,使用正確安全的用戶帳戶運行的應用程序(認爲Vista UAC)不具有對程序安裝文件夾的寫入權限。

你可以用ConfigurationManager類來對抗系統。但微不足道的解決方法是進入設置設計器並將設置的範圍更改爲用戶。如果這會造成困難(例如,該設置與每個用戶相關),則應該將您的選項功能放在單獨的程序中,以便您可以詢問特權提升提示。或者放棄使用設置。

70

如果您在保存到文件同一目錄下的可執行文件中的規劃,這裏是一個使用JSON格式的很好的解決方案:

using System; 
using System.IO; 
using System.Web.Script.Serialization; 

namespace MiscConsole 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      MySettings settings = MySettings.Load(); 
      Console.WriteLine("Current value of 'myInteger': " + settings.myInteger); 
      Console.WriteLine("Incrementing 'myInteger'..."); 
      settings.myInteger++; 
      Console.WriteLine("Saving settings..."); 
      settings.Save(); 
      Console.WriteLine("Done."); 
      Console.ReadKey(); 
     } 

     class MySettings : AppSettings<MySettings> 
     { 
      public string myString = "Hello World"; 
      public int myInteger = 1; 
     } 
    } 

    public class AppSettings<T> where T : new() 
    { 
     private const string DEFAULT_FILENAME = "settings.json"; 

     public void Save(string fileName = DEFAULT_FILENAME) 
     { 
      File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(this)); 
     } 

     public static void Save(T pSettings, string fileName = DEFAULT_FILENAME) 
     { 
      File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(pSettings)); 
     } 

     public static T Load(string fileName = DEFAULT_FILENAME) 
     { 
      T t = new T(); 
      if(File.Exists(fileName)) 
       t = (new JavaScriptSerializer()).Deserialize<T>(File.ReadAllText(fileName)); 
      return t; 
     } 
    } 
} 
12

註冊表/ configurationSettings/XML的說法似乎仍然非常活躍。隨着技術的發展,我已經全部使用了它們,但我最喜歡的是基於Threed's systemIsolated Storage的組合。

以下示例允許將名爲properties的對象存儲到獨立存儲中的文件中。如:

AppSettings.Save(myobject, "Prop1,Prop2", "myFile.jsn"); 

屬性可以使用恢復:

AppSettings.Load(myobject, "myFile.jsn"); 

這僅僅是一個樣品,不提示的最佳做法。

internal static class AppSettings 
{ 
    internal static void Save(object src, string targ, string fileName) 
    { 
     Dictionary<string, object> items = new Dictionary<string, object>(); 
     Type type = src.GetType(); 

     string[] paramList = targ.Split(new char[] { ',' }); 
     foreach (string paramName in paramList) 
      items.Add(paramName, type.GetProperty(paramName.Trim()).GetValue(src, null)); 

     try 
     { 
      // GetUserStoreForApplication doesn't work - can't identify. 
      // application unless published by ClickOnce or Silverlight 
      IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly(); 
      using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Create, storage)) 
      using (StreamWriter writer = new StreamWriter(stream)) 
      { 
       writer.Write((new JavaScriptSerializer()).Serialize(items)); 
      } 

     } 
     catch (Exception) { } // If fails - just don't use preferences 
    } 

    internal static void Load(object tar, string fileName) 
    { 
     Dictionary<string, object> items = new Dictionary<string, object>(); 
     Type type = tar.GetType(); 

     try 
     { 
      // GetUserStoreForApplication doesn't work - can't identify 
      // application unless published by ClickOnce or Silverlight 
      IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly(); 
      using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Open, storage)) 
      using (StreamReader reader = new StreamReader(stream)) 
      { 
       items = (new JavaScriptSerializer()).Deserialize<Dictionary<string, object>>(reader.ReadToEnd()); 
      } 
     } 
     catch (Exception) { return; } // If fails - just don't use preferences. 

     foreach (KeyValuePair<string, object> obj in items) 
     { 
      try 
      { 
       tar.GetType().GetProperty(obj.Key).SetValue(tar, obj.Value, null); 
      } 
      catch (Exception) { } 
     } 
    } 
} 
1

有時你想擺脫保留在傳統web.config或app.config文件中的那些設置。您希望對部署設置條目和分離的數據設計進行更細緻的控制。或者需要在運行時啓用添加新條目。

我能想象兩個很好的選擇:

  • 強類型版本和
  • 的面向對象的版本。

強類型版本的優點是強類型設置名稱和值。不存在混用名稱或數據類型的風險。缺點是更多設置必須進行編碼,不能在運行時添加。

隨着面向對象版本的優點是,新的設置可以在運行時添加。但是你沒有強類型名稱和值。必須小心字符串標識符。獲取值時必須知道之前保存的數據類型。

你可以找到兩個全功能的實現HERE的代碼。

1

其他選項,而不是使用自定義XML文件,我們可以用更人性化的文件格式:JSON或YAML文件。

  • 如果你使用.NET 4.0的動態,這個庫是很容易使用 (序列化,反序列化,嵌套對象支持和訂購輸出 如你所願+合併多個設置一個)JsonConfig(使用相當於ApplicationSettingsBase)
  • 對於.NET YAML配置庫...我還沒有發現一個是作爲 容易,因爲JsonConfig使用

您可以將您的設置文件中有多個特殊文件夾(所有用戶和每用戶)如此處列出Environment.SpecialFolder Enumeration個多個文件(默認只讀,每個角色,每個用戶等)

如果您選擇使用多種設置,可以合併這些設置:例如,合併默認設置+ BasicUser + AdminUser。你可以用你自己的規則:最後一個覆蓋值等

8

的簡單方法是使用一種配置,數據對象,它保存爲本地文件夾並在啓動應用程序的名稱的XML文件讀回來。

這裏是爲存儲形式的位置和尺寸的例子。

配置數據對象是強類型的,易於使用:

[Serializable()] 
public class CConfigDO 
{ 
    private System.Drawing.Point m_oStartPos; 
    private System.Drawing.Size m_oStartSize; 

    public System.Drawing.Point StartPos 
    { 
     get { return m_oStartPos; } 
     set { m_oStartPos = value; } 
    } 

    public System.Drawing.Size StartSize 
    { 
     get { return m_oStartSize; } 
     set { m_oStartSize = value; } 
    } 
} 

保存和加載管理器類:

public class CConfigMng 
{ 
    private string m_sConfigFileName = System.IO.Path.GetFileNameWithoutExtension(System.Windows.Forms.Application.ExecutablePath) + ".xml"; 
    private CConfigDO m_oConfig = new CConfigDO(); 

    public CConfigDO Config 
    { 
     get { return m_oConfig; } 
     set { m_oConfig = value; } 
    } 

    // Load configuration file 
    public void LoadConfig() 
    { 
     if (System.IO.File.Exists(m_sConfigFileName)) 
     { 
      System.IO.StreamReader srReader = System.IO.File.OpenText(m_sConfigFileName); 
      Type tType = m_oConfig.GetType(); 
      System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType); 
      object oData = xsSerializer.Deserialize(srReader); 
      m_oConfig = (CConfigDO)oData; 
      srReader.Close(); 
     } 
    } 

    // Save configuration file 
    public void SaveConfig() 
    { 
     System.IO.StreamWriter swWriter = System.IO.File.CreateText(m_sConfigFileName); 
     Type tType = m_oConfig.GetType(); 
     if (tType.IsSerializable) 
     { 
      System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType); 
      xsSerializer.Serialize(swWriter, m_oConfig); 
      swWriter.Close(); 
     } 
    } 
} 

現在,您可以創建一個實例並在窗體的負載使用,關閉事件:

private CConfigMng oConfigMng = new CConfigMng(); 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     // Load configuration 
     oConfigMng.LoadConfig(); 
     if (oConfigMng.Config.StartPos.X != 0 || oConfigMng.Config.StartPos.Y != 0) 
     { 
      Location = oConfigMng.Config.StartPos; 
      Size = oConfigMng.Config.StartSize; 
     } 
    } 

    private void Form1_FormClosed(object sender, FormClosedEventArgs e) 
    { 
     // Save configuration 
     oConfigMng.Config.StartPos = Location; 
     oConfigMng.Config.StartSize = Size; 
     oConfigMng.SaveConfig(); 
    } 

而且生成的XML文件也是可讀的:

<?xml version="1.0" encoding="utf-8"?> 
<CConfigDO xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <StartPos> 
    <X>70</X> 
    <Y>278</Y> 
    </StartPos> 
    <StartSize> 
    <Width>253</Width> 
    <Height>229</Height> 
    </StartSize> 
</CConfigDO> 
10

我想共享我爲此構建的庫。這是一個小型圖書館,但是對於.settings文件來說是一個很大的改進(恕我直言)。

庫被稱爲Jot (GitHub),這裏是一個老The Code Project article我寫一下吧。

這裏是你如何使用它來跟蹤一個窗口的大小和位置:

public MainWindow() 
{ 
    InitializeComponent(); 

    _stateTracker.Configure(this) 
     .IdentifyAs("MyMainWindow") 
     .AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState)) 
     .RegisterPersistTrigger(nameof(Closed)) 
     .Apply(); 
} 

的好處相比,.settings文件:有相當少的代碼,它是無差錯少了很多因爲你只需要提及每個屬性一次

使用設置文件,您需要提及每個屬性五個次:一次顯式創建屬性並在代碼中另外四次來回複製值。

存儲,序列化等是完全可配置的。使用inversion of control時,可以將其掛接,以便自動將跟蹤應用於其解析的所有對象,以便您只需要使屬性持久就可以掌握[Trackable]屬性。

我在寫這一切,因爲我認爲圖書館是一流的,我想推廣它:)

0
public static class SettingsExtensions 
{ 
    public static bool TryGetValue<T>(this Settings settings, string key, out T value) 
    { 
     if (settings.Properties[key] != null) 
     { 
      value = (T) settings[key]; 
      return true; 
     } 

     value = default(T); 
     return false; 
    } 

    public static bool ContainsKey(this Settings settings, string key) 
    { 
     return settings.Properties[key] != null; 
    } 

    public static void SetValue<T>(this Settings settings, string key, T value) 
    { 
     if (settings.Properties[key] == null) 
     { 
      var p = new SettingsProperty(key) 
      { 
       PropertyType = typeof(T), 
       Provider = settings.Providers["LocalFileSettingsProvider"], 
       SerializeAs = SettingsSerializeAs.Xml 
      }; 
      p.Attributes.Add(typeof(UserScopedSettingAttribute), new UserScopedSettingAttribute()); 
      var v = new SettingsPropertyValue(p); 
      settings.Properties.Add(p); 
      settings.Reload(); 
     } 
     settings[key] = value; 
     settings.Save(); 
    } 
} 
0

「,這是否意味着我應該使用自定義XML文件保存配置設置?「不,不一定。我們使用SharpConfig進行此類操作。

例如,如果配置文件就是這樣

[General] 
# a comment 
SomeString = Hello World! 
SomeInteger = 10 # an inline comment 

我們可以檢索這樣

var config = Configuration.LoadFromFile("sample.cfg"); 
var section = config["General"]; 

string someString = section["SomeString"].StringValue; 
int someInteger = section["SomeInteger"].IntValue; 

這是與.net 2.0和更高版本兼容的值。我們可以即時創建配置文件,稍後可以保存。 來源:http://sharpconfig.net/ Github:https://github.com/cemdervis/SharpConfig

我希望它有幫助。

相關問題