web-dev-qa-db-ja.com

c#-WPFアプリケーションでユーザー設定を保存する方法?

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

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

Q2-If Database:データベーステーブルの設計持つ可能性のあるさまざまなデータ型の列を持つ1つのテーブル(たとえば、stringlongDateTimeなど)OR値をシリアル化および非シリアル化する必要がある値の文字列?最初の方が簡単だと思いますし、設定が多くなければオーバーヘッドはそれほど多くありませんか?

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

66
Greg

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

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

WPFのユーザー設定

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

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

67
akjoshi

Settings.DefaultにXMLのStringsとして設定情報を保存できます。構成データを保存するクラスをいくつか作成し、それらが[Serializable]であることを確認します。次に、次のヘルパーを使用して、これらのオブジェクトのインスタンス、またはそれらのList<T>(または配列T[]など)をStringにシリアル化できます。これらのさまざまな文字列のそれぞれを、WPFアプリケーションのSettingsのそれぞれのSettings.Defaultスロットに格納します。

次回アプリを起動したときにオブジェクトを復元するには、目的の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);
}
12
Glenn Slayden

更新:最近は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);
    }
}

多分誰かがこのアプローチに触発されます。これは私が今何年もやっている方法であり、私はそれで非常に満足しています。

10
Mat

この質問に対する最も一般的なアプローチは、分離ストレージです。

コントロール状態を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);
        }
6
Jeff

データベースとは別に、ユーザー関連の設定を保存する次のオプションもあります

  1. HKEY_CURRENT_USERの下のレジストリ

  2. AppDataフォルダー内のファイル内

  3. wPFでSettingsファイルを使用し、そのスコープをUserとして設定する

4
ajay_whiz

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

3
Adam

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

1
Doobi

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

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

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

0
Will_Piuvans