简体   繁体   English

C# - 在 WPF 应用程序中保存用户设置的方法?

[英]C# - approach for saving user settings in a WPF application?

What approach do you recommend for persisting user settings in a WPF windows (desktop) application?对于在 WPF windows(桌面)应用程序中持久保存用户设置,您推荐什么方法? Note that the idea is that the user can change their settings at run time, and then can close down the application, then when starting up the application later the application will use the current settings.请注意,这个想法是用户可以在运行时更改他们的设置,然后可以关闭应用程序,然后在稍后启动应用程序时应用程序将使用当前设置。 Effectively then it will appear as if the application settings do not change.实际上,应用程序设置似乎没有更改。

Q1 - Database or other approach? Q1 - 数据库或其他方法? I do have a sqlite database that I will be using anyway hence using a table in the database would be as good as any approach?我确实有一个 sqlite 数据库,无论如何我都会使用它,因此在数据库中使用一个表会和任何方法一样好吗?

Q2 - If Database: What database table design? Q2 - 如果数据库:什么数据库表设计? One table with columns for different data types that one might have (eg string , long , DateTime etc) OR just a table with a string for the value upon which you have to serialize and de-serialize the values?一个包含不同数据类型的列的表(例如stringlongDateTime等),或者只是一个包含字符串的表,您必须根据该字符串对值进行序列化和反序列化? I'm thinking the first would be easier, and if there aren't many settings the overhead isn't much?我认为第一个会更容易,如果设置不多,开销也不大?

Q3 - Could Application Settings be used for this? Q3 - 应用程序设置可以用于此吗? If so are there any special tasks required to enable the persistence here?如果是这样,是否需要执行任何特殊任务才能在此处启用持久性? Also what would happen regarding usage of the "default" value in the Application Settings designer in this case?此外,在这种情况下,在应用程序设置设计器中使用“默认”值会发生什么情况? Would the default override any settings that were saved between running the application?默认值是否会覆盖在运行应用程序之间保存的任何设置? (or would you need to NOT use the default value) (或者您是否需要不使用默认值)

You can use Application Settings for this, using database is not the best option considering the time consumed to read and write the settings(specially if you use web services).您可以为此使用应用程序设置,考虑到读取和写入设置所花费的时间(特别是如果您使用 Web 服务),使用数据库并不是最佳选择。

Here are few links which explains how to achieve this and use them in WPF -这里有几个链接解释了如何实现这一点并在 WPF 中使用它们 -

User Settings in WPF WPF 中的用户设置

Quick WPF Tip: How to bind to WPF application resources and settings? WPF 快速提示:如何绑定到 WPF 应用程序资源和设置?

A Configurable Window for WPF WPF 的可配置窗口

Update : Nowadays I would use JSON.更新:现在我会使用 JSON。

I also prefer to go with serialization to file.我也更喜欢序列化到文件。 XML files fits mostly all requirements. XML 文件几乎可以满足所有要求。 You can use the ApplicationSettings build in but those have some restrictions and a defined but (for me) very strange behavior where they stored.您可以使用ApplicationSettings内置,但它们有一些限制和定义但(对我而言)它们存储的非常奇怪的行为。 I used them a lot and they work.我经常使用它们并且它们有效。 But if you want to have full control how and where they stored I use another approach.但是如果你想完全控制它们存储的方式和位置,我会使用另一种方法。

  1. Make a class Somewhere with all your settings.使用您的所有设置在某处开设课程。 I named it MySettings我将其命名为MySettings
  2. Implement Save and Read for persistence实现保存和读取以实现持久性
  3. Use them in you application-code在您的应用程序代码中使用它们

Advantages:优点:

  • Very Simple approach.非常简单的方法。
  • One Class for Settings.一类设置。 Load.负载。 Save.保存。
  • All your Settings are type safe.您的所有设置都是类型安全的。
  • You can simplify or extend the logic to your needs (Versioning, many Profiles per User, etc.)您可以根据需要简化或扩展逻辑(版本控制、每个用户的多个配置文件等)
  • It works very well in any case (Database, WinForms, WPF, Service, etc...)它在任何情况下都能很好地工作(数据库、WinForms、WPF、服务等...)
  • You can define where to store the XML files.您可以定义存储 XML 文件的位置。
  • You can find them and manipulate them either by code or manual您可以通过代码或手动找到它们并对其进行操作
  • It works for any deployment method I can imagine.它适用于我能想象的任何部署方法。

Disadvantages: - You have to think about where to store your settings files.缺点: - 您必须考虑在哪里存储您的设置文件。 (But you can just use your installation folder) (但你可以只使用你的安装文件夹)

Here is a simple example (not tested)-这是一个简单的例子(未测试)-

public class MySettings
{
    public string Setting1 { get; set; }
    public List<string> Setting2 { get; set; }

    public void Save(string filename)
    {
        using (StreamWriter sw = new StreamWriter(filename))
        {
            XmlSerializer xmls = new XmlSerializer(typeof(MySettings));
            xmls.Serialize(sw, this);
        }
    }
    public MySettings Read(string filename)
    {
        using (StreamReader sw = new StreamReader(filename))
        {
            XmlSerializer xmls = new XmlSerializer(typeof(MySettings));
            return xmls.Deserialize(sw) as MySettings;
        }
    }
}

And here is how to use it.这是如何使用它。 It's possible to load default values or override them with the user's settings by just checking if user settings exist:只需检查用户设置是否存在,就可以加载默认值或使用用户设置覆盖它们:

public class MyApplicationLogic
{
    public const string UserSettingsFilename = "settings.xml";
    public string _DefaultSettingspath = 
        Assembly.GetEntryAssembly().Location + 
        "\\Settings\\" + UserSettingsFilename;

    public string _UserSettingsPath = 
        Assembly.GetEntryAssembly().Location + 
        "\\Settings\\UserSettings\\" + 
        UserSettingsFilename;

    public MyApplicationLogic()
    {
        // if default settings exist
        if (File.Exists(_UserSettingsPath))
            this.Settings = Settings.Read(_UserSettingsPath);
        else
            this.Settings = Settings.Read(_DefaultSettingspath);
    }
    public MySettings Settings { get; private set; }

    public void SaveUserSettings()
    {
        Settings.Save(_UserSettingsPath);
    }
}

maybe someone get's inspired by this approach.也许有人会受到这种方法的启发。 This is how I do it now for many years and I'm quite happy with that.这就是我多年来一直这样做的方式,我对此感到非常满意。

You can store your settings info as Strings of XML in the Settings.Default .您可以将设置信息作为 XML Strings存储在Settings.Default Create some classes to store your configuration data and make sure they are [Serializable] .创建一些类来存储您的配置数据并确保它们是[Serializable] Then, with the following helpers, you can serialize instances of these objects--or List<T> (or arrays T[] , etc.) of them--to String .然后,使用以下帮助程序,您可以将这些对象的实例(或它们的List<T> (或数组T[]等)序列化为String Store each of these various strings in its own respective Settings.Default slot in your WPF application's Settings .将这些不同的字符串中的每一个存储在 WPF 应用程序的Settings各自的Settings.Default插槽中。

To recover the objects the next time the app starts, read the Settings string of interest and Deserialize to the expected type T (which this time must be explcitly specified as a type argument to Deserialize<T> ).要在应用程序下次启动时恢复对象,请读取感兴趣的Settings字符串并将其Deserialize化为预期类型T (这次必须明确指定为Deserialize<T>的类型参数)。

public static String Serialize<T>(T t)
{
    using (StringWriter sw = new StringWriter())
    using (XmlWriter xw = XmlWriter.Create(sw))
    {
        new XmlSerializer(typeof(T)).Serialize(xw, t);
        return sw.GetStringBuilder().ToString();
    }
}

public static T Deserialize<T>(String s_xml)
{
    using (XmlReader xw = XmlReader.Create(new StringReader(s_xml)))
        return (T)new XmlSerializer(typeof(T)).Deserialize(xw);
}

The long running most typical approach to this question is: Isolated Storage.解决这个问题的长期最典型的方法是:隔离存储。

Serialize your control state to XML or some other format (especially easily if you're saving Dependency Properties with WPF), then save the file to the user's isolated storage.将您的控件状态序列化为 XML 或某种其他格式(如果您使用 WPF 保存依赖属性,则尤其容易),然后将文件保存到用户的独立存储中。

If you do want to go the app setting route, I tried something similar at one point myself...though the below approach could easily be adapted to use Isolated Storage:如果你确实想走应用程序设置路线,我自己曾经尝试过类似的东西......尽管下面的方法可以很容易地适应使用隔离存储:

class SettingsManager
{
    public static void LoadSettings(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
    {
        EnsureProperties(sender, savedElements);
        foreach (FrameworkElement element in savedElements.Keys)
        {
            try
            {
                element.SetValue(savedElements[element], Properties.Settings.Default[sender.Name + "." + element.Name]);
            }
            catch (Exception ex) { }
        }
    }

    public static void SaveSettings(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
    {
        EnsureProperties(sender, savedElements);
        foreach (FrameworkElement element in savedElements.Keys)
        {
            Properties.Settings.Default[sender.Name + "." + element.Name] = element.GetValue(savedElements[element]);
        }
        Properties.Settings.Default.Save();
    }

    public static void EnsureProperties(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
    {
        foreach (FrameworkElement element in savedElements.Keys)
        {
            bool hasProperty =
                Properties.Settings.Default.Properties[sender.Name + "." + element.Name] != null;

            if (!hasProperty)
            {
                SettingsAttributeDictionary attributes = new SettingsAttributeDictionary();
                UserScopedSettingAttribute attribute = new UserScopedSettingAttribute();
                attributes.Add(attribute.GetType(), attribute);

                SettingsProperty property = new SettingsProperty(sender.Name + "." + element.Name,
                    savedElements[element].DefaultMetadata.DefaultValue.GetType(), Properties.Settings.Default.Providers["LocalFileSettingsProvider"], false, null, SettingsSerializeAs.String, attributes, true, true);
                Properties.Settings.Default.Properties.Add(property);
            }
        }
        Properties.Settings.Default.Reload();
    }
}

.....and.... ……还有……

  Dictionary<FrameworkElement, DependencyProperty> savedElements = new Dictionary<FrameworkElement, DependencyProperty>();

public Window_Load(object sender, EventArgs e) {
           savedElements.Add(firstNameText, TextBox.TextProperty);
                savedElements.Add(lastNameText, TextBox.TextProperty);

            SettingsManager.LoadSettings(this, savedElements);
}

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            SettingsManager.SaveSettings(this, savedElements);
        }

Apart from a database, you can also have following options to save user related settings除了数据库,您还可以使用以下选项来保存用户相关设置

  1. registry under HKEY_CURRENT_USER HKEY_CURRENT_USER下的HKEY_CURRENT_USER

  2. in a file in AppData folderAppData文件夹中的文件中

  3. using Settings file in WPF and by setting its scope as User在 WPF 中使用Settings文件并将其范围设置为用户

In my experience storing all the settings in a database table is the best solution.根据我的经验,将所有设置存储在数据库表中是最好的解决方案。 Don't even worry about performance.甚至不用担心性能。 Today's databases are fast and can easily store thousands columns in a table.今天的数据库速度很快,可以轻松地在一个表中存储数千列。 I learned this the hard way - before I was serilizing/deserializing - nightmare.在我进行序列化/反序列化之前,我通过艰难的方式学到了这一点——噩梦。 Storing it in local file or registry has one big problem - if you have to support your app and computer is off - user is not in front of it - there is nothing you can do.... if setings are in DB - you can changed them and viola not to mention that you can compare the settings....将它存储在本地文件或注册表中有一个大问题 - 如果您必须支持您的应用程序并且计算机已关闭 - 用户不在它面前 - 您无能为力.... 如果设置在 DB 中 - 您可以更改了它们和中提琴,更不用说您可以比较设置....

You can use SQLite, a small, fast, self-contained, full-featured, SQL database engine.您可以使用 SQLite,一个小型、快速、独立、功能齐全的 SQL 数据库引擎。 I personally recommend it after trying settings file and XML file approach.在尝试设置文件和 XML 文件方法后,我个人推荐它。

Install NuGet package System.Data.SQLite which is an ADO.NET provider for SQLite. The package includes support for LINQ and Entity Framework Overall you can do many things with such supporting features to your settings window.安装NuGet package SYSTEM.DATA.SQLite它是88393605555588提供商SQLite的提供程序。

1.Install SQLite 2.Create your database file 3.Create tables to save your settings 4.Access database file in your application to read and edit settings. 1.安装 SQLite 2.创建数据库文件 3.创建表以保存设置 4.访问应用程序中的数据库文件以读取和编辑设置。

I felt this approach very much helpful for application settings, since i can do adjustments to database and also take advantage of ADO.Net and LINQ features我觉得这种方法对应用程序设置非常有帮助,因为我可以对数据库进行调整,还可以利用 ADO.Net 和 LINQ 功能

I typically do this sort of thing by defining a custom [ Serializable ] settings class and simply serializing it to disk.我通常通过定义自定义 [ Serializable ] 设置类并将其序列化到磁盘来完成此类操作。 In your case you could just as easily store it as a string blob in your SQLite database.在您的情况下,您可以像将字符串 blob 一样轻松地存储在 SQLite 数据库中。

  1. In all the places I've worked, database has been mandatory because of application support.在我工作过的所有地方,由于应用程序支持,数据库一直是强制性的。 As Adam said, the user might not be at his desk or the machine might be off, or you might want to quickly change someone's configuration or assign a new-joiner a default (or team member's) config.正如 Adam 所说,用户可能不在他的办公桌前或机器可能关闭,或者您可能想要快速更改某人的配置或为新加入者分配默认(或团队成员)配置。

  2. If the settings are likely to grow as new versions of the application are released, you might want to store the data as blobs which can then be deserialized by the application.如果设置可能会随着应用程序新版本的发布而增加,您可能希望将数据存储为 blob,然后应用程序可以反序列化这些数据。 This is especially useful if you use something like Prism which discovers modules, as you can't know what settings a module will return.如果您使用诸如发现模块的 Prism 之类的东西,这将特别有用,因为您无法知道模块将返回哪些设置。 The blobs could be keyed by username/machine composite key. blob 可以通过用户名/机器组合键进行键控。 That way you can have different settings for every machine.这样您就可以为每台机器设置不同的设置。

  3. I've not used the in-built Settings class much so I'll abstain from commenting.我很少使用内置的 Settings 类,所以我将避免发表评论。 :) :)

I wanted to use an xml control file based on a class for my VB.net desktop WPF application.我想为我的 VB.net 桌面 WPF 应用程序使用基于类的 xml 控制文件。 The above code to do this all in one is excellent and set me in the right direction.上面的代码合二为一地完成了这一切,它让我朝着正确的方向前进。 In case anyone is searching for a VB.net solution here is the class I built:如果有人正在寻找 VB.net 解决方案,这里是我构建的类:

Imports System.IO
Imports System.Xml.Serialization

Public Class XControl

Private _person_ID As Integer
Private _person_UID As Guid

'load from file
Public Function XCRead(filename As String) As XControl
    Using sr As StreamReader = New StreamReader(filename)
        Dim xmls As New XmlSerializer(GetType(XControl))
        Return CType(xmls.Deserialize(sr), XControl)
    End Using
End Function

'save to file
Public Sub XCSave(filename As String)
    Using sw As StreamWriter = New StreamWriter(filename)
        Dim xmls As New XmlSerializer(GetType(XControl))
        xmls.Serialize(sw, Me)
    End Using
End Sub

'all the get/set is below here

Public Property Person_ID() As Integer
    Get
        Return _person_ID
    End Get
    Set(value As Integer)
        _person_ID = value
    End Set
End Property

Public Property Person_UID As Guid
    Get
        Return _person_UID
    End Get
    Set(value As Guid)
        _person_UID = value
    End Set
End Property

End Class

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM