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

发布于 2024-09-24 14:04:54 字数 491 浏览 1 评论 0原文

您建议采用什么方法在 WPF Windows(桌面)应用程序中保留用户设置?请注意,其想法是用户可以在运行时更改其设置,然后可以关闭应用程序,然后在稍后启动应用程序时,应用程序将使用当前设置。实际上,应用程序设置看起来好像没有更改。

Q1 - 数据库还是其他方法?我确实有一个我将使用的 sqlite 数据库,因此使用数据库中的表与任何方法一样好?

Q2 - 如果数据库:什么数据库表设计?一张表,其中包含可能具有的不同数据类型的列(例如,stringlongDateTime 等),或者只是一张包含字符串的表您必须序列化和反序列化值的值?我认为第一个会更容易,如果设置不多,开销不会太大吗?

Q3 - 应用程序设置可以用于此目的吗?如果是这样,是否需要执行任何特殊任务才能在此处启用持久性?在这种情况下,在应用程序设置设计器中使用“默认”值会发生什么情况?默认值会覆盖运行应用程序之间保存的任何设置吗? (或者您不需要使用默认值)

What approach do you recommend for persisting user settings in a WPF windows (desktop) application? 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? 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?

Q2 - If Database: What database table design? One table with columns for different data types that one might have (e.g. 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? 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? 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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

挽袖吟 2024-10-01 14:04:54

您可以使用应用程序设置来实现此目的,使用考虑到读取和写入设置所消耗的时间(特别是如果您使用 Web 服务),数据库不是最佳选择。

以下几个链接解释了如何实现此目的并在 WPF 中使用设置:

You can use application settings for this, using a database is not the best option considering the time consumed to read and write the settings (especially if you use web services).

Here are few links which explain how to achieve this and use settings in WPF:

萝莉病 2024-10-01 14:04:54

更新:现在我会使用 JSON。

我也更喜欢序列化到文件。 XML 文件几乎可以满足所有要求。您可以使用 ApplicationSettings 构建,但它们有一些限制和定义的但(对我来说)非常奇怪的存储行为。我经常使用它们并且它们很有效。但如果您想完全控制它们的存储方式和位置,我会使用另一种方法。

  1. 在某个地方创建一个包含所有设置的类。我将其命名为 MySettings
  2. 实现保存和读取以实现持久性
  3. 在应用程序代码中使用它们

优点:

  • 非常简单的方法。
  • 一类设置。加载。节省。
  • 您的所有设置都是类型安全的。
  • 您可以根据您的需求简化或扩展逻辑(版本控制、每个用户的许多配置文件等)。
  • 它在任何情况下都运行良好(数据库、WinForms、WPF、服务等...)
  • 您可以定义存储 XML 的位置文件。
  • 您可以通过代码或手动找到它们并操作它们。
  • 它适用于我能想象到的任何部署方法。

缺点:
- 您必须考虑在哪里存储您的设置文件。 (但您可以只使用您的安装文件夹)

这是一个简单的示例(未经测试)-

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

以下是如何使用它。只需检查用户设置是否存在,就可以加载默认值或用用户的设置覆盖它们:

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

也许有人会受到这种方法的启发。这就是我多年来的做法,我对此感到非常满意。

Update: Nowadays I would use JSON.

I also prefer to go with serialization to file. XML files fits mostly all requirements. You can use the ApplicationSettings build in but those have some restrictions and a defined but (for me) very strange behavior where they stored. 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
  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...)
  • You can define where to store the XML files.
  • 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.

坏尐絯℡ 2024-10-01 14:04:54

您可以将设置信息以 XML 的字符串形式存储在Settings.Default中。创建一些类来存储您的配置数据并确保它们是[Serializable]。然后,使用以下帮助程序,您可以序列化这些对象的实例 - 或它们的 List (或数组 T[] 等) - 以字符串。将每个不同的字符串存储在 WPF 应用程序的 Settings 中其各自的 Settings.Default 槽中。

要在下次应用启动时恢复对象,请读取感兴趣的 Settings 字符串,并将 Deserialize 转换为预期类型 T(这次必须明确指定为 Deserialize 的类型参数)。

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

You can store your settings info as Strings of XML in the Settings.Default. Create some classes to store your configuration data and make sure they are [Serializable]. Then, with the following helpers, you can serialize instances of these objects--or List<T> (or arrays T[], etc.) of them--to String. Store each of these various strings in its own respective Settings.Default slot in your WPF application's Settings.

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>).

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);
}
孤檠 2024-10-01 14:04:54

除了数据库之外,您还可以使用以下选项将用户相关设置保存

  1. HKEY_CURRENT_USER 下的

    注册表 HKEY_CURRENT_USER

  2. 文件中AppData 文件夹

  3. 使用 WPF 中的 Settings 文件并将其范围设置为 User

Apart from a database, you can also have following options to save user related settings

  1. registry under HKEY_CURRENT_USER

  2. in a file in AppData folder

  3. using Settings file in WPF and by setting its scope as User

云醉月微眠 2024-10-01 14:04:54

长期以来,解决这个问题最典型的方法是:隔离存储。

将控件状态序列化为 XML 或某种其他格式(如果您使用 WPF 保存依赖项属性,则尤其容易),然后将文件保存到用户的独立存储中。

如果您确实想走应用程序设置路线,我自己曾尝试过类似的方法...尽管下面的方法可以轻松地适应使用隔离存储:

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

...并且...

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

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.

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);
        }
自由如风 2024-10-01 14:04:54

根据我的经验,将所有设置存储在数据库表中是最好的解决方案。甚至不用担心性能。当今的数据库速度很快,可以轻松地在表中存储数千列。在我进行序列化/反序列化之前,我以惨痛的方式了解到了这一点——噩梦。将其存储在本地文件或注册表中存在一个大问题 - 如果您必须支持您的应用程序并且计算机已关闭 - 用户不在它前面 - 您无能为力......如果设置位于数据库中 - 您可以改变了它们和中提琴更不用说你可以比较设置......

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....

深海里的那抹蓝 2024-10-01 14:04:54

您可以使用 SQLite,这是一个小型、快速、独立、功能齐全的 SQL 数据库引擎。在尝试了设置文件和 XML 文件方法之后,我个人推荐它。

安装NuGet包System.Data.SQLite
它是 SQLite 的 ADO.NET 提供程序。
该软件包包括对 LINQ 和实体框架的支持
总的来说,您可以使用设置窗口的此类支持功能做很多事情。

1.安装SQLite
2.创建数据库文件
3.创建表来保存您的设置
4.访问应用程序中的数据库文件以读取和编辑设置。

我觉得这种方法对于应用程序设置非常有帮助,因为我可以对数据库进行调整,还可以利用 ADO.Net 和 LINQ 功能

You can use SQLite, a small, fast, self-contained, full-featured, SQL database engine. I personally recommend it after trying settings file and XML file approach.

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.

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.

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

凉城 2024-10-01 14:04:54

我通常通过定义自定义 [Serialized] 设置类并简单地将其序列化到磁盘来完成此类操作。在您的情况下,您可以轻松地将其作为字符串 blob 存储在 SQLite 数据库中。

I typically do this sort of thing by defining a custom [Serializable] settings class and simply serializing it to disk. In your case you could just as easily store it as a string blob in your SQLite database.

南笙 2024-10-01 14:04:54
  1. 在我工作过的所有地方,由于应用程序支持,数据库都是必需的。正如 Adam 所说,用户可能不在办公桌前,或者计算机可能已关闭,或者您可能希望快速更改某人的配置或为新加入者分配默认(或团队成员)配置。

  2. 如果设置可能会随着应用程序新版本的发布而增加,您可能希望将数据存储为 blob,然后可由应用程序反序列化。如果您使用像 Prism 这样的工具来发现模块,这尤其有用,因为您无法知道模块将返回什么设置。
    Blob 可以通过用户名/计算机复合密钥来键入。这样,您就可以为每台计算机设置不同的设置。

  3. 我没有太多使用内置的 Settings 类,所以我不会发表评论。 :)

  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.

  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. This is especially useful if you use something like Prism which discovers modules, as you can't know what settings a module will return.
    The blobs could be keyed by username/machine composite key. 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. :)

燃情 2024-10-01 14:04:54

我想为我的 VB.net 桌面 WPF 应用程序使用基于类的 xml 控制文件。上面的代码可以将所有这些全部完成,非常出色,并为我指明了正确的方向。如果有人正在寻找 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

I wanted to use an xml control file based on a class for my VB.net desktop WPF application. 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:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文