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


84

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

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

问题2 –如果是数据库:什么是数据库表设计?针对不同的数据类型列的表是一个可能(例如stringlongDateTime等),或者只是一个用于在您拥有序列化和反序列化值的值的字符串表?我想第一种会比较容易,如果没有太多设置,那么开销会不会太大?

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


@所有新用户如果可以发布单独的问题而不是将问题合并为一个问题,则是首选方法。这样,它可以帮助人们回答您的问题,也帮助其他人寻找您的至少一个问题。谢谢!
Hille

Answers:



22

更新:如今,我将使用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);
    }
}

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


1
不利的是,这可能是因为您不再具有设置设计器,因此当两者都可以使用时,它对用户的友好程度就会降低。
Phil1970年

3
完全同意“它们存储的非常奇怪的行为”,正因为如此,我正在使用您的方法。+1。
Hannish

如果您有新问题,请单击“提问”按钮进行提问
马太福音

12

您可以将设置信息Strings从XML开始存储在中Settings.Default。创建一些类来存储您的配置数据,并确保它们是正确的[Serializable]。然后,使用以下帮助程序,您可以将这些对象的实例List<T>(或T[]它们的(或数组等))序列化为String。将这些各种字符串中的每一个存储Settings.Default在WPF应用程序的各自位置中Settings

要在下次应用启动时恢复对象,请读取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);
}

6

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

将您的控件状态序列化为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);
        }

5

除数据库外,您还可以使用以下选项来保存与用户相关的设置

  1. 注册表下 HKEY_CURRENT_USER

  2. 在文件AppData夹中的文件中

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


2
建议1是应用程序降低Windows速度的原因,最好不要在文件IMO中使用更好的方法填充注册表项。
2014年

1
@控制台,在磁盘上写入文件会减慢(磨损)SSD,在数据库中写入数据会减慢数据库的速度。那您有什么选择呢?Windows注册表旨在用作保存设置的地方之一。
Sinatr 2014年

1
没错,我认为必须提及的一点是,如果每个应用程序都在那里保存拙劣的用户首选项,则注册表会有一些弊端。
2014年

@Sinatr最初,注册表是用于此目的的……但它并不是为了处理大量数据而设计的,因此在历史上的某个时候,Microsoft建议停止使用它。据我所知,Windows会在登录时加载整个注册表,并且还会在出现严重崩溃后复制以进行漫游或加载最后一个已知的良好配置。因此,即使从未使用过该应用程序,使用注册表也会影响系统。
Phil1970年

另外,注册表有大小限制,如果应用程序仍在使用它,则在大多数计算机上可能会超过该限制。该注册表是在系统内存为MB的今天少于计算机今天为GB的时候设计的。该注册表并不是设计得太大,因此即使限制有所增加,也并未针对我们当前的需求进行优化。
Phil1970

3

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


当连接不可用时,将它们远程存储也是一个大问题。许多编写为在线工作的应用程序在离线工作时或某些时候的体验都不尽如人意,即使它们存在使某些功能无法离线工作的错误,即使它应该除了“监视”设备的使用方式外,没有其他影响。
Phil1970 '16

1

我通常通过定义自定义[ Serializable]设置类并将其序列化到磁盘来完成这种事情。在您的情况下,您可以像在SQLite数据库中一样轻松地将其存储为字符串blob。


0
  1. 在我工作过的所有地方,由于应用程序的支持,数据库都是必需的。正如Adam所说,用户可能不在办公桌前,或者机器可能不在办公室,或者您可能想要快速更改某人的配置或为新加入者分配默认(或团队成员)的配置。

  2. 如果设置可能随着应用程序新版本的发布而增长,则您可能希望将数据存储为Blob,然后可以由应用程序反序列化。如果您使用诸如Prism之类的东西来发现模块,这将特别有用,因为您不知道模块将返回什么设置。斑点可以由用户名/机器复合键来键入。这样,您可以为每台机器设置不同的设置。

  3. 我没有太多使用内置的Settings类,因此我将放弃评论。:)


0

我想为我的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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.