2010-09-24 8 views
54

Jakie podejście zaleca się do utrzymywania ustawień użytkownika w aplikacji Windows (desktop) WPF? Należy pamiętać, że użytkownik może zmieniać swoje ustawienia w czasie wykonywania, a następnie może zamknąć aplikację, a następnie, po uruchomieniu aplikacji, aplikacja będzie korzystała z bieżących ustawień. Skutecznie wtedy pojawi się tak, jakby ustawienia aplikacji się nie zmieniły.C# - podejście do zapisywania ustawień użytkownika w aplikacji WPF?

Q1 - Baza danych lub inne podejście? Mam bazę danych sqlite, której będę używał, więc używanie tabeli w bazie danych byłoby tak dobre, jak każde podejście?

Q2 - Jeśli baza danych: Jaki projekt tabeli bazy danych? Jedna tabela z kolumnami dla różnych typów danych, które można mieć (np. string, long, DateTime itd.) LUB tylko tabelą z ciągiem znaków dla wartości, na której musisz serializować i rozdzielić wartości? Myślę, że pierwsze byłoby łatwiejsze, a jeśli nie ma wielu ustawień, obciążenie nie jest dużo?

Q3 - Czy można użyć ustawień aplikacji? Jeśli tak, to czy są jakieś specjalne zadania, które są wymagane, aby włączyć tutaj trwałość? Co się stanie w takim przypadku z używaniem wartości "domyślnej" w projektowaniu ustawień aplikacji? Czy ustawienie domyślne zastąpiłoby wszelkie ustawienia, które zostały zapisane między uruchomieniem aplikacji? (Albo trzeba by NIE używać wartości domyślnej)

Odpowiedz

56

Do tego celu można użyć Application Settings, ponieważ korzystanie z bazy danych nie jest najlepszą opcją, biorąc pod uwagę czas potrzebny na odczyt i zapis ustawień (szczególnie w przypadku korzystania z usług internetowych).

Oto kilka linków, które wyjaśnia, jak to osiągnąć i wykorzystać je w WPF -

User Settings in WPF

Quick WPF Tip: How to bind to WPF application resources and settings?

A Configurable Window for WPF

+2

Oto kolejny dobry link. http: // stackoverflow.com/a/396243/878612 – lko

4

Oprócz bazy danych, można również następujące opcje, aby zapisać ustawienia związane użytkowników

  1. rejestru pod HKEY_CURRENT_USER

  2. w pliku AppData w folderze

  3. wykorzystaniem Settings pliku w WPF i ustawiając jego zakres jako użytkownika

+2

Propozycja 1 jest powodem, dla którego Aplikacje spowalniają system Windows, lepiej nie wypełniać kluczy rejestru lepiej wykonanym w pliku IMO. – Console

+1

@ Konsola, zapisywanie pliku na dysku spowalnia (zużycie) SSD, zapisywanie danych w bazie danych spowalnia bazę danych. Jaka jest więc twoja opcja? Rejestr systemu Windows * jest przeznaczony * do użycia jako jeden z miejsc zapisywania * ustawień *. – Sinatr

+1

Masz rację, myślę, że ważne jest, aby wspomnieć, że rejestr ma pewne wady, jeśli każda aplikacja zapisuje w niej preferencje użytkownika. – Console

6

Długa biegu najbardziej typowe podejście na to pytanie brzmi: Pojedyncze przechowywanie.

Serializuj stan kontroli do formatu XML lub innego formatu (szczególnie łatwo, jeśli zapisujesz właściwości zależności z WPF), a następnie zapisz plik w odizolowanym miejscu użytkownika.

Jeśli chcesz iść trasę ustawień aplikacji, próbowałem coś podobnego w pewnym momencie sam ... choć poniżej podejście może być łatwo dostosowany do korzystania 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(); 
    } 
} 

.... .i....

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

ja zazwyczaj robić tego typu rzeczy poprzez zdefiniowanie niestandardowego [Serializable] Klasa ustawienia i po prostu szeregowania go na dysku. W twoim przypadku równie dobrze można go zapisać jako blob smyczkowy w bazie danych SQLite.

3

Z mojego doświadczenia wynika, że ​​przechowywanie wszystkich ustawień w tabeli bazy danych jest najlepszym rozwiązaniem. Nawet nie martw się o wydajność. Dzisiejsze bazy danych są szybkie i mogą łatwo przechowywać tysiące kolumn w tabeli. Nauczyłem się tego na własnej skórze - zanim przejrzałem/deserializując - koszmar. Przechowywanie w lokalnym pliku lub rejestrze ma jeden duży problem - jeśli musisz obsługiwać swoją aplikację, a komputer jest wyłączony - użytkownik nie znajduje się przed nią - nic nie możesz zrobić ... jeśli ustawienia są w DB - możesz zmieniono je i altówkę, nie wspominając o tym, że można porównać ustawienia ...

+0

Przechowywanie ich zdalnie również ma jeden duży problem, gdy połączenie nie jest dostępne ... Wiele aplikacji napisanych do pracy online ma mniej niż idealne warunki do pracy w trybie offline, a czasami nawet ma błędy, które sprawiają, że niektóre funkcje nie pracować w trybie offline, nawet jeśli nie powinno to mieć żadnego wpływu poza "szpiegowaniem" sposobu użytkowania urządzenia. – Phil1970

10

Możesz przechowywać informacje o ustawieniach jako Strings XML w Settings.Default. Utwórz kilka klas do przechowywania danych konfiguracyjnych i upewnij się, że są one [Serializable]. Następnie za pomocą poniższych helperów można szeregować instancje tych obiektów - lub List<T> (lub tablic T[] itp.) Z nich - na String. Przechowuj każdy z tych różnych ciągów w swoim odpowiednim gnieździe Settings.Default w aplikacji Settings aplikacji WPF.

Aby odzyskać obiektów następnym razem aplikacja uruchamia przeczytać ciąg Settings zainteresowania i Deserialize do oczekiwanego typu T (który tym razem musi być explcitly określonej jako typ argumentu 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); 
} 
0
  1. We wszystkich miejscach, z którymi pracowałem, baza danych jest obowiązkowe ze względu na poparcie wniosku. Jak powiedział Adam, użytkownik może nie być przy swoim biurku lub maszyna może być wyłączona, albo możesz szybko zmienić czyjąś konfigurację lub przypisać nowemu stolarskiemu domyślną konfigurację (lub członka zespołu).

  2. Jeśli ustawienia prawdopodobnie pojawią się wraz z wydaniem nowych wersji aplikacji, możesz zapisać dane jako obiekty typu blob, które następnie mogą zostać deserializowane przez aplikację. Jest to szczególnie przydatne, jeśli używasz czegoś takiego jak Prism, który odkrywa moduły, ponieważ nie wiesz, jakie ustawienia zwróci moduł. Bloby mogą być wpisywane przy użyciu nazwy użytkownika/klucza kompozytowego komputera. W ten sposób możesz mieć różne ustawienia dla każdej maszyny.

  3. Nie użyłem wbudowanej klasy ustawień, więc wstrzymuję się od komentowania. :)

8

Ja również wolę z serializacją do pliku. Pliki XML pasują do większości wymagań. Możesz użyć kompilacji ApplicationSettings, ale mają one pewne ograniczenia i określone, ale (dla mnie) bardzo dziwne zachowanie, w którym są przechowywane. Dużo ich używałem i działają. Ale jeśli chcesz mieć pełną kontrolę nad tym, jak i gdzie przechowujesz, używam innego podejścia.

  1. Zrób lekcję Gdzieś ze wszystkimi ustawieniami.Nazwałem to MySettings
  2. Wdrożenie zapis i odczyt dla wytrwałości
  3. Używaj ich w ciebie aplikacji kodu

Zalety:

  • Bardzo proste podejście.
  • Jedna klasa dla ustawień. Obciążenie. Zapisać.
  • Wszystkie ustawienia są bezpieczne.
  • można uprościć lub rozszerzenie logiki do swoich potrzeb (Versioning, wielu profili każdego użytkownika, etc.)
  • działa bardzo dobrze w każdym przypadku (baza danych, WinForms, WPF, usług, etc ...)
  • Możesz określić, gdzie przechowywać pliki XML.
  • Możesz je znaleźć i manipulować nimi za pomocą kodu lub instrukcji.
  • Działa dla każdej metody wdrażania, jaką mogę sobie wyobrazić.

Wady: - Zastanów się, gdzie przechowywać pliki ustawień. (Ale może po prostu użyć folder instalacyjny)

Oto prosty przykład (nie testowane) -

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

A oto jak go używać. Jest to możliwe, aby załadować domyślne wartości lub zastąpić je z ustawieniami użytkownika tylko o sprawdzenie, czy istnieją ustawienia użytkownika:

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

może ktoś get zainspirowany tym podejściem. Tak to robię teraz od wielu lat i jestem z tego całkiem zadowolony.

+0

Wadą jest to, że nie masz już projektanta ustawień, więc jest trochę mniej przyjazny dla użytkownika, gdy oba będą działać. – Phil1970

+0

Całkowicie zgadzam się na "bardzo dziwne zachowanie, w którym są przechowywane", właśnie z tego korzystam. +1. – Hannish

0

Chciałem użyć pliku kontrolnego xml opartego na klasie dla mojej aplikacji WPF na komputery stacjonarne VB.net. Powyższy kod, aby zrobić to wszystko w jednym jest doskonały i ustawić mnie we właściwym kierunku. W przypadku, gdy ktoś szuka rozwiązania VB.net, jest to klasa, którą zbudowałem:

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 
Powiązane problemy