簡體   English   中英

如何在 Windows 窗體應用程序中保存應用程序設置?

[英]How can I save application settings in a Windows Forms application?

我想要實現的目標非常簡單:我有一個使用路徑讀取信息的 Windows 窗體 (.NET 3.5) 應用程序。 用戶可以使用我提供的選項表單修改此路徑。

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

我知道有三個選項可用:

  • 配置設置文件 (appname.exe.config)
  • 注冊表
  • 自定義 XML 文件

我讀到 .NET 配置文件無法將值保存回它。 至於注冊表,我想盡可能遠離它。

這是否意味着我應該使用自定義 XML 文件來保存配置設置?

如果是這樣,我想看看它的代碼示例(C#)。

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

如果您使用 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 窗體和其他項目類型。

請注意,您需要設置設置的范圍屬性。 如果您選擇應用程序范圍,則 Settings.Default.<your property> 將是只讀的。

參考: 如何:使用 C# 在運行時編寫用戶設置- Microsoft Docs

如果您打算保存到與可執行文件位於同一目錄中的文件,這里有一個使用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;
        }
    }
}

注冊表是不行的。 您不確定使用您的應用程序的用戶是否具有寫入注冊表的足夠權限。

您可以使用app.config文件保存應用程序級別的設置(對於使用您的應用程序的每個用戶都相同)。

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

其次,從 .NET 2.0 開始,可以將值存儲回app.config文件。

ApplicationSettings類不支持將設置保存到app.config文件。 這在很大程度上是設計使然; 使用適當保護的用戶帳戶(例如 Vista UAC)運行的應用程序對程序的安裝文件夾沒有寫訪問權限。

您可以使用ConfigurationManager類來對抗系統。 但簡單的解決方法是進入設置設計器並將設置的范圍更改為用戶。 如果這造成困難(例如,該設置與每個用戶相關),您應該將您的選項功能放在一個單獨的程序中,以便您可以要求權限提升提示。 或者放棄使用設置。

我想分享一個我為此構建的庫。 這是一個很小的庫,但對 .settings 文件有很大的改進(恕我直言)。

該庫名為Jot (GitHub) 這是我寫的一篇關於 代碼項目的舊 文章

以下是如何使用它來跟蹤窗口的大小和位置:

public MainWindow()
{
    InitializeComponent();

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

與 .settings 文件相比的好處:代碼少得多,而且不容易出錯,因為您只需要提及每個屬性一次

對於設置文件,您需要提及每個屬性五次:一次是在您明確創建屬性時,另外四次是在來回復制值的代碼中。

存儲、序列化等是完全可配置的。 當目標對象由IoC容器創建時,您可以 [hook it up][] 以便它自動將跟蹤應用於它解析的所有對象,因此您需要做的就是使屬性持久化,只需拍一個 [Trackable]屬性在它上面。

它是高度可配置的,您可以配置: - 當數據在全局或每個跟蹤對象上持久化和應用時 - 它是如何序列化的 - 存儲位置(例如文件、數據庫、在線、隔離存儲、注冊表) - 可以取消應用的規則/持久化屬性的數據

相信我,圖書館是一流的!

registry/configurationSettings/XML 參數似乎仍然非常活躍。 隨着技術的進步,我已經全部使用了它們,但我最喜歡的是基於Threed 的系統隔離存儲

以下示例允許將名為 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) { }
        }
    }
}

一個簡單的方法是使用配置數據對象,將它保存為一個 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>

我不喜歡使用web.configapp.config的建議解決方案。 嘗試閱讀您自己的 XML。 看看XML 設置文件 - 不再是 web.config

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

  • 如果你使用 .NET 4.0 動態,這個庫真的很容易使用(序列化,反序列化,嵌套對象支持和排序輸出,如你所願+將多個設置合並為一個) JsonConfig (用法相當於ApplicationSettingsBase)
  • 對於.NET YAML 配置庫...我還沒有找到像 JsonConfig 一樣容易使用的

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

如果您選擇使用多個設置,則可以合並這些設置:例如,合並 default + BasicUser + AdminUser 的設置。 您可以使用自己的規則:最后一個覆蓋值等。

“這是否意味着我應該使用自定義 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

是的,可以保存配置 - 但這在很大程度上取決於您選擇的方式。 讓我描述技術差異,以便您了解您擁有的選項:

首先,您需要區分是要在*.exe.config (在 Visual Studio 中也稱為App.config )文件中使用applicationSettings還是AppSettings - 存在根本差異,在此處進行描述

兩者都提供了不同的保存更改的方法:

  • AppSettings允許您直接讀取和寫入配置文件(通過config.Save(ConfigurationSaveMode.Modified); ,其中 config 定義為config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); )。

  • applicationSettings允許讀取,但如果您寫入更改(通過Properties.Settings.Default.Save(); ),它將基於每個用戶寫入,存儲在特殊位置(例如C:\\Documents and Settings\\USERID\\Local Settings\\Application Data\\FIRMNAME\\WindowsFormsTestApplicati_Url_tdq2oylz33rzq00sxhvxucu5edw2oghw\\1.0.0.0 )。 正如Hans Passant在他的回答中提到的,這是因為用戶通常對 Program Files 擁有有限的權限,並且在不調用 UAC 提示的情況下無法寫入。 一個缺點是,如果您將來要添加配置密鑰,則需要將它們與每個用戶配置文件同步。

注意:正如問題中提到的,還有第三種選擇:如果您將配置文件視為XML 文檔,則可以使用System.Xml.Linq.XDocument類加載、修改和保存它。 不需要使用自定義的 XML 文件,您可以讀取現有的配置文件; 對於查詢元素,您甚至可以使用 Linq 查詢。 我在這里給出了一個例子,請查看答案中的GetApplicationSetting函數。

如果您需要加密來保護您的值,請查看答案。 它描述了如何使用 Microsoft 的 DPAPI 來存儲加密的值。

據我所知,.NET 確實支持使用內置應用程序設置工具持久化設置:

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

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

我可以想象兩個不錯的選擇:

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

強類型版本的優點是強類型設置名稱和值。 沒有混合名稱或數據類型的風險。 缺點是需要編碼更多的設置,不能在運行時添加。

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

您可以在此處找到兩個全功能實現的代碼。

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();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM