WPF 응용 프로그램에서 사용자 설정을 저장하는 방법?


84

WPF Windows (데스크톱) 애플리케이션에서 사용자 설정을 유지하기 위해 어떤 접근 방식을 권장합니까? 아이디어는 사용자가 런타임에 설정을 변경 한 다음 응용 프로그램을 닫을 수 있으며 나중에 응용 프로그램을 시작할 때 응용 프로그램이 현재 설정을 사용할 수 있다는 것입니다. 실제로 응용 프로그램 설정이 변경되지 않는 것처럼 나타납니다.

Q1-데이터베이스 또는 기타 접근 방식? 어쨌든 사용할 sqlite 데이터베이스가 있으므로 데이터베이스에서 테이블을 사용하는 것이 모든 접근 방식만큼 좋을까요?

Q2-If Database : 어떤 데이터베이스 테이블 디자인입니까? 하나가있을 수 있음을 다른 데이터 형식의 열이 하나 개의 테이블 (예를 들면 string, long, DateTime등) 또는 당신이 직렬화 및 역 직렬화 값으로이있는시 값에 대한 문자열을 그냥 테이블? 첫 번째가 더 쉬울 것이라고 생각하고 설정이 많지 않으면 오버 헤드가 많지 않습니까?

Q3-이를 위해 응용 프로그램 설정을 사용할 수 있습니까? 그렇다면 여기에서 지속성을 활성화하는 데 필요한 특별한 작업이 있습니까? 또한이 경우 응용 프로그램 설정 디자이너의 "기본값"값 사용과 관련하여 어떤 일이 발생합니까? 기본값이 응용 프로그램 실행 사이에 저장된 모든 설정을 무시합니까? (또는 기본값을 사용하지 않아도됩니다)


@All new users 질문을 하나로 결합하는 대신 별도의 질문을 게시 할 수있는 것이 좋습니다. 이렇게하면 사람들이 귀하의 질문에 답변하고 다른 사람들이 귀하의 질문 중 하나 이상을 찾는 데 도움이됩니다. 감사!
Hille

답변:


80

이를 위해 응용 프로그램 설정 을 사용할 수 있습니다. 데이터베이스를 사용하는 것은 설정을 읽고 쓰는 데 걸리는 시간을 고려할 때 최선의 선택이 아닙니다 (특히 웹 서비스를 사용하는 경우).

다음은이를 달성하고 WPF에서 사용하는 방법을 설명하는 몇 가지 링크입니다.

WPF의 사용자 설정

빠른 WPF 팁 : WPF 애플리케이션 리소스 및 설정에 바인딩하는 방법은 무엇입니까?

WPF를위한 구성 가능한 창


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 2017-04-24

새로운 질문이있는 경우 질문하기 버튼 을 클릭하여 질문하십시오 .
Mat

12

설정 정보 Strings를 XML 형식으로 Settings.Default. 구성 데이터를 저장할 몇 가지 클래스를 만들고 [Serializable]. 그런 다음 다음 도우미를 사용하여 이러한 개체의 인스턴스 List<T>(또는 배열 T[]등)를 String. 이러한 다양한 문자열 각각을 Settings.DefaultWPF 애플리케이션의 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 레지스트리는 설정 을 저장하는 장소 중 하나로 사용 하기위한 것 입니다.
Sinatr 2014

1
당신이 옳습니다. 모든 응용 프로그램이 사용자 환경 설정을 저장하면 레지스트리에 몇 가지 단점이 있음을 언급하는 것이 중요하다고 생각합니다.
Console

@Sinatr 원래 레지스트리는 그 목적을위한 것이었지만 대량의 데이터를 처리하도록 설계되지 않았으므로 역사의 어느 시점에서 Microsoft는 사용을 중단 할 것을 권장했습니다. 내가 아는 한, Windows는 로그온시 전체 레지스트리를로드하고 로밍을 위해 복사본을 만들거나 주요 충돌 후 마지막으로 알려진 양호한 구성을로드 할 수 있습니다. 따라서 레지스트리를 사용하면 응용 프로그램을 사용하지 않더라도 시스템에 영향을줍니다.
Phil1970

또한 레지스트리에는 크기 제한이 있으며 응용 프로그램에서 여전히 사용하는 경우 대부분의 컴퓨터에서 제한을 초과 할 수 있습니다. 레지스트리는 현재 컴퓨터의 메모리 (GB)보다 시스템의 메모리 (MB)가 적을 때 설계되었습니다. 레지스트리는 그렇게 크게 설계되지 않았으므로 제한이 증가 했음에도 불구하고 현재 요구 사항에 최적화되어 있지 않습니다.
Phil1970

3

내 경험상 모든 설정을 데이터베이스 테이블에 저장하는 것이 최상의 솔루션입니다. 성능에 대해 걱정하지 마십시오. 오늘날의 데이터베이스는 빠르며 테이블에 수천 개의 열을 쉽게 저장할 수 있습니다. 나는 이것을 어려운 방법으로 배웠다-내가 serilizing / deserializing하기 전에-악몽. 로컬 파일이나 레지스트리에 저장하는 것은 한 가지 큰 문제가 있습니다. 앱을 지원해야하고 컴퓨터가 꺼져있는 경우-사용자가 앞에 있지 않은 경우-할 수있는 일이 없습니다 .... 설정이 DB에 있으면 가능합니다. 설정을 비교할 수 있다는 것은 말할 것도없고 비올라와 변경했습니다 ....


원격으로 저장하는 것도 연결이 불가능할 때 큰 문제가됩니다. 온라인으로 작동하도록 작성된 많은 응용 프로그램은 오프라인으로 작업 할 때 이상적인 경험에 미치지 못하거나 때로는 일부 기능이 오프라인에서 작동하지 않도록 만드는 버그가 있습니다. 장치 사용 방식을 "스파이"하는 것 외에는 영향을 미치지 않습니다.
Phil1970

1

저는 일반적으로 사용자 정의 [ Serializable] 설정 클래스를 정의 하고 단순히 디스크에 직렬화하여 이러한 종류의 작업을 수행합니다 . 귀하의 경우에는 SQLite 데이터베이스에 문자열 blob으로 쉽게 저장할 수 있습니다.


0
  1. 내가 일한 모든 곳에서 애플리케이션 지원으로 인해 데이터베이스가 필수였습니다. Adam이 말했듯이 사용자가 자신의 책상에 있지 않거나 컴퓨터가 꺼져 있거나 다른 사람의 구성을 빠르게 변경하거나 새 참여자에게 기본 (또는 팀 구성원의) 구성을 할당 할 수 있습니다.

  2. 애플리케이션의 새 버전이 출시됨에 따라 설정이 커질 가능성이있는 경우 데이터를 Blob으로 저장 한 다음 애플리케이션에서 역 직렬화 할 수 있습니다. 모듈이 어떤 설정을 반환할지 알 수 없기 때문에 모듈을 검색하는 Prism과 같은 것을 사용하는 경우 특히 유용합니다. blob은 사용자 이름 / 머신 복합 키로 입력 할 수 있습니다. 이렇게하면 모든 컴퓨터에 대해 다른 설정을 가질 수 있습니다.

  3. 내장 설정 클래스를 많이 사용하지 않았으므로 주석을 사용하지 않겠습니다. :)


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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.