c#-WPFアプリケーションにユーザー設定を保存するためのアプローチ?


84

WPFウィンドウ(デスクトップ)アプリケーションでユーザー設定を永続化するために、どのようなアプローチをお勧めしますか?ユーザーは実行時に設定を変更してからアプリケーションを閉じることができ、後でアプリケーションを起動すると、アプリケーションは現在の設定を使用するという考え方に注意してください。事実上、アプリケーションの設定が変更されていないように見えます。

Q1-データベースまたは他のアプローチ?私はとにかく使用するsqliteデータベースを持っているので、データベースでテーブルを使用することは他のアプローチと同じくらい良いでしょうか?

Q2-データベースの場合:どのデータベーステーブルの設計ですか?1は、(例えば持っている可能性があることを別のデータ型の列で一つのテーブルstringlongDateTimeなど)か、値をシリアライズとデシリアライズする必要があり、その上に値の文字列を使用してちょうどテーブル?最初の方が簡単だと思います。設定が少ない場合、オーバーヘッドはそれほど多くありませんか?

Q3-これにアプリケーション設定を使用できますか?もしそうなら、ここで永続性を有効にするために必要な特別なタスクはありますか?また、この場合、アプリケーション設定デザイナでの「デフォルト」値の使用に関してはどうなりますか?デフォルトでは、アプリケーションの実行中に保存された設定が上書きされますか?(または、デフォルト値を使用する必要はありません)


@すべての新規ユーザー質問を1つにまとめるのではなく、個別の質問を投稿できることが望ましいです。そうすれば、あなたの質問に答える人々や、あなたの質問の少なくとも1つを探している他の人々を助けることができます。ありがとう!
ヒル

回答:


80

これにはアプリケーション設定を使用できます。設定の読み取りと書き込みにかかる時間を考慮すると、データベースの使用は最適なオプションではありません(特にWebサービスを使用する場合)。

これを実現し、WPFで使用する方法を説明するリンクをいくつか示します-

WPFのユーザー設定

クイックWPFのヒント:WPFアプリケーションのリソースと設定にバインドする方法は?

WPFの構成可能なウィンドウ


22

更新:最近はJSONを使用します。

また、ファイルへのシリアル化を使用することを好みます。XMLファイルはほとんどすべての要件に適合します。ビルドインを使用できますが、ApplicationSettingsいくつかの制限があり、定義されていますが(私にとっては)非常に奇妙な動作が保存されています。私はそれらをよく使用し、それらは機能します。ただし、保存方法と保存場所を完全に制御したい場合は、別のアプローチを使用します。

  1. すべての設定でクラスをどこかに作成します。名前を付けましたMySettings
  2. 永続性のために保存と読み取りを実装する
  3. アプリケーションコードでそれらを使用してください

利点:

  • 非常にシンプルなアプローチ。
  • 設定用の1つのクラス。負荷。セーブ。
  • すべての設定はタイプセーフです。
  • ロジックを単純化またはニーズに合わせて拡張できます(バージョン管理、ユーザーごとの多数のプロファイルなど)。
  • それはどんな場合でも非常にうまく機能します(データベース、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 2016

3
「それらが保存される非常に奇妙な振る舞い」に完全に同意します、私はまさにこれのためにあなたのアプローチを使用しています。+1。
ハニッシュ2017

新しい質問がある場合は、[質問する]ボタンをクリックして質問してください。
マット

12

StringsXMLの設定情報をに保存できます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. SettingsWPFでファイルを使用し、そのスコープをユーザーとして設定する


2
提案1は、アプリケーションがWindowsの速度を低下させる理由であり、ファイルIMOでより適切に実行されたものでレジストリキーを埋めない方がよいでしょう。
コンソール

1
@Console、ディスクへのファイルの書き込みはSSDの速度を低下させ(摩耗)、データベースへのデータの書き込みはデータベースの速度を低下させます。それではあなたの選択肢は何ですか?Windowsレジストリは、設定を保存する場所の1つとして使用すること目的としています
シナトラ2014年

1
その通りです。すべてのアプリケーションが膨大なユーザー設定をそこに保存する場合、レジストリにはいくつかの欠点があることに言及することが重要だと思います。
コンソール

@Sinatr元々、レジストリはその目的を目的としていました...しかし、大量のデータを処理するようには設計されていなかったため、履歴のある時点で、Microsoftはその使用を停止することを推奨しました。私の知る限り、Windowsはログオン時にレジストリ全体をロードし、ローミング用にコピーを作成するか、メジャークラッシュ後に最後の既知の適切な構成をロードできるようにします。したがって、レジストリを使用すると、アプリケーションが使用されない場合でもシステムに影響します。
Phil1970 2016

また、レジストリにはサイズ制限があり、それがまだアプリケーションによって使用されている場合は、ほとんどのコンピュータで制限を超えている可能性があります。レジストリは、システムのメモリが現在のコンピュータのGBよりもMBのメモリが少ないときに設計されました。レジストリはそれほど大きくなるように設計されていないため、制限が増えたとしても、現在のニーズに合わせて最適化されていません。
Phil1970 2016

3

私の経験では、すべての設定をデータベーステーブルに保存することが最善の解決策です。パフォーマンスについても心配する必要はありません。今日のデータベースは高速で、テーブルに数千の列を簡単に格納できます。私はこれを難しい方法で学びました-私がセリライズ/デシリアライズする前に-悪夢。ローカルファイルまたはレジストリに保存することには、1つの大きな問題があります-アプリをサポートする必要があり、コンピューターがオフになっている場合-ユーザーがその前にいない場合-何もできません....設定がDBにある場合-できます設定を比較できることは言うまでもなく、それらとビオラを変更しました。


また、接続が利用できない場合、それらをリモートに保存することにも1つの大きな問題があります...オンラインで動作するように作成された多くのアプリケーションは、オフラインで動作するときに理想的とは言えないエクスペリエンスを持っているか、場合によっては、一部の機能がオフラインで動作しないようにするバグがあります。デバイスの使用方法を「スパイ」する以外の影響はありません。
Phil1970 2016

1

私は通常、カスタム[ Serializable]設定クラスを定義し、それをディスクにシリアル化することによって、この種のことを行います。あなたの場合、SQLiteデータベースに文字列blobと同じくらい簡単に保存できます。


0
  1. 私が働いたすべての場所で、アプリケーションのサポートのためにデータベースは必須でした。Adamが言ったように、ユーザーが自分のデスクにいないか、マシンがオフになっている可能性があります。または、誰かの構成をすばやく変更したり、新しい参加者にデフォルト(またはチームメンバー)の構成を割り当てたりすることもできます。

  2. アプリケーションの新しいバージョンがリリースされるにつれて設定が大きくなる可能性がある場合は、データをblobとして保存し、アプリケーションで逆シリアル化できるようにすることをお勧めします。これは、モジュールがどの設定を返すかわからないため、モジュールを検出するPrismのようなものを使用する場合に特に便利です。BLOBは、ユーザー名/マシン複合キーでキー設定できます。そうすれば、マシンごとに異なる設定を行うことができます。

  3. 組み込みのSettingsクラスはあまり使用していないので、コメントは控えます。:)


0

VB.netデスクトップWPFアプリケーションのクラスに基づくxml制御ファイルを使用したかったのです。これをすべて1つで行う上記のコードは優れており、正しい方向に私を設定します。誰かが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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.