Programmeinstellungen vor dem Entladen speichern

  • WPF MVVM
  • .NET (FX) 4.5–4.8

Es gibt 41 Antworten in diesem Thema. Der letzte Beitrag () ist von kafffee.

    kafffee schrieb:

    Verzeih mir wenn ich mich täusche aber die Property ist doch Public?
    Nur die Variable zum Zwischenspeichern ist Private...


    Tatsache! Wer lesen kann, ist natürlich klar im Vorteil. Wegen der Einrückung im Post musste ich gerade aber auch noch ein zweites Mal schauen.
    Aber es stimmt auch, dass es eine Indexed-Property ist. VB ist mir nicht ganz geläufig und die Syntax ist (für mich) auch weniger verständlich als C#. Aber das wird schwer, das zu serialisieren.
    Am sinnigsten wäre es denke ich, wenn du deine _EqualiserLeft Property nochmal in eine eigene Eigenschaft lädst und mit deinem Indexer separat drauf zugreifst, oder deine Property direkt zu einem Array umänderst und dann direkt darauf zugreifst.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    Okay, also auf der Website von Newtonsoft heisst es:

    IEnumerable, Lists, and Arrays.NET lists (types that inherit from IEnumerable) and .NET arrays are converted to JSON arrays. Because JSON arrays only support a range of values and not properties, any additional properties and fields declared on .NET collections are not serialized. In situations where a type implements IEnumerable but a JSON array is not wanted, then the JsonObjectAttribute can be placed on the type to force it to be serialized as a JSON object instead.

    So wie ich das verstehe ist es wohl eingeschränkt möglich, aber ich kann mich auch täuschen:
    "any additional properties and fields declared on .NET collections are not serialized"

    Versteht jemand den englischen Text richtig? Was meinen die mit "additional"?

    siycah schrieb:

    Am sinnigsten wäre es denke ich, wenn du deine _EqualiserLeft Property nochmal in eine eigene Eigenschaft lädst und mit deinem Indexer separat drauf zugreifst, oder deine Property direkt zu einem Array umänderst und dann direkt darauf zugreifst.


    Kannst du mir vielleicht an einem kleinen Beispiel zeigen, wie du das meinst, ich verstehe es nicht ganz...
    Die meinen glaub eine Auflistung, die noch mehr tut als nur auflisten.
    Kommt nur sehr selten vor.
    ZB IGrouping(Of T) ist eine Auflistung, die zusätzlich noch ein Key-Feld transportiert.
    ich verstehe den Satz so, dass der Serializer diesen Key nicht serialisiert.
    in anderen Worten: Für deinen Anwendungsfall nicht relevant.
    Habe auch noch Folgendes gefunden:

    Public and non-public fields
    Newtonsoft.Json can serialize and deserialize fields as well as properties.

    In System.Text.Json, use the JsonSerializerOptions.IncludeFields global setting or the [JsonInclude] attribute to include public fields when serializing or deserializing. For an example, see Include fields.

    Das war hier:
    learn.microsoft.com/en-us/dotn…lic-and-non-public-fields

    Was meinen die mit "Fields"?

    So wie @ErfinderDesRades meint?

    kafffee schrieb:


    IEnumerable, Lists, and Arrays.NET lists (types that inherit from IEnumerable) and .NET arrays are converted to JSON arrays. Because JSON arrays only support a range of values and not properties, any additional properties and fields declared on .NET collections are not serialized. In situations where a type implements IEnumerable but a JSON array is not wanted, then the JsonObjectAttribute can be placed on the type to force it to be serialized as a JSON object instead.

    So wie ich das verstehe ist es wohl eingeschränkt möglich, aber ich kann mich auch täuschen:
    "any additional properties and fields declared on .NET collections are not serialized"


    Muttersprachler eilt zur Hilfe!

    Da steht, dass Objekte, basierend auf IEnumerable<T>, List<T> und Arrays zu JSON Arrays serialisiert werden; was man schließlich auch erwartet.

    C#-Quellcode

    1. void SerialiseObjects() {
    2. var ex1 = new List<string> { "Element 1", "Element 2", "Element 3" };
    3. var ex2 = ex1.Select(x => $"${x}.ienumerable");
    4. var ex3 = new string[] { "Element Arr1", "Element Arr2", "Element Arr3" };
    5. var json1 = JsonConvert.SerializeObject(ex1);
    6. var json2 = JsonConvert.SerializeObject(ex2);
    7. var json3 = JsonConvert.SerializeObject(ex3);
    8. }


    Der Output wäre:

    Quellcode

    1. json1: [ "Element 1", "Element 2", "Element 3" ]
    2. json2: [ "Element 1.ienumerable", "Element 2.ienumerable", "Element 3.ienumerable" ]
    3. json3: [ "Element Arr1", "Element Arr2", "Element Arr3" ]


    Klar, ein Beispiel zu meinem Vorschlag kann ich dir auch geben. Aber in C# :D

    C#-Quellcode

    1. class MySettings {
    2. public short[] EqualizerLeft { get; set; } = { 0, 1, 2, 3 };
    3. public short this[int idx] {
    4. get => EqualizerLeft[idx];
    5. set {
    6. if (idx > short.MaxValue) { throw new IndexOutOfRangeException("zu groß!"); }
    7. else if (idx < short.MinValue) { throw new IndexOutOfRangeException("zu klein!"); }
    8. EqualizerLeft[idx] = (short)value;
    9. }
    10. }
    11. internal override string ToString() => JsonConvert.SerializeObject(this);
    12. }
    13. // Verwendung
    14. class User {
    15. void Foo() {
    16. var settings = new Settings();
    17. short myValue = settings[2];
    18. // myValue hat nun den Wert 2
    19. }
    20. }


    Das JSON dazu wäre:

    Quellcode

    1. {
    2. "EqualizerLeft": [ 0, 1, 2, 3 ]
    3. }
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    Eines solltest Du in jedem Fall beachten, wenn Du beim Beenden des Programmes Daten oder Einstellungen speicherst.
    Die heutigen PC-Systeme mit SSD sind mitunter so schnell, dass Windows beim Herunterfahren Deine Anwendung beendet, Deine Anwendung nun versucht noch letzte Daten zu schreiben, aber der PC schon ausgeschaltet wird, während Dein Programm noch schreibt.

    Dies passiert häufig, wenn man die my.settings verwendet.
    Dann ist das XML-File (das im Appdata-Ordner liegt) meist nicht komplett geschrieben und die Anwendung startet nicht mehr.

    Daher würde ich versuchen die Daten und Einstellungen während der Programmlaufzeit zu speichern, dann, wenn sich die Einstellungen oder Daten ändern.
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    Daher würde ich versuchen die Daten und Einstellungen während der Programmlaufzeit zu speichern, dann, wenn sich die Einstellungen oder Daten ändern.


    Genau. Hierzu bin ich letztens bei folgendem Tool fündig geworden, beim Versuch den MS-Ranz (Settings) loszuwerden:
    github.com/aloneguid/config
    Es funktioniert auch mit DI Containern zusammen, was meine Hauptanforderung war. Wollte es nur mal fix hier einwerfen.
    An manchen Tagen gibt es zu allem Überfluss auch noch Ärger!

    siycah schrieb:

    Muttersprachler eilt zur Hilfe!


    Haha :P , irgendwie hab ich mir schon gedacht, dass du englischsprachig bist. Daher ein kleiner Tipp:

    siycah schrieb:

    Leider bietet den Faden bisher wenig Quellcode

    In diesem Zusammenhang kannst du "thread" ruhig mit "Thread" übersetzen. "Faden" ist nur die wörtliche Übersetzung, wenn du das Utensil meinst, mit dem man Socken flickt... Ansonsten ist dein Deutsch perfekt.


    siycah schrieb:

    Am sinnigsten wäre es denke ich, wenn du deine _EqualiserLeft Property nochmal in eine eigene Eigenschaft lädst und mit deinem Indexer separat drauf zugreifst, oder deine Property direkt zu einem Array umänderst und dann direkt darauf zugreifst.

    Okay jetzt hab ichs verstanden. Habs so ausprobiert und es funktioniert :)

    dive26 schrieb:

    Dies passiert häufig, wenn man die my.settings verwendet.

    my.settings, was ist das? Ich benutze es nicht, zumindest nicht wissentlich

    dive26 schrieb:

    Daher würde ich versuchen die Daten und Einstellungen während der Programmlaufzeit zu speichern, dann, wenn sich die Einstellungen oder Daten ändern.

    Ich mache das so:
    Ich hab mir ein Interface im ViewModel erstellt:

    VB.NET-Quellcode

    1. Interface IClosing
    2. Function OnClosing() As Boolean
    3. End Interface


    Dann hab ich dieses in meinem ViewModel implementiert:

    VB.NET-Quellcode

    1. Public Function OnClosing() As Boolean
    2. Dim close As Boolean = True
    3. 'hier kann ich Aktionen einfügen, die vor dem Speichern ausgeführt werden sollen
    4. Return close 'und bei Bedarf kann ich hier mit "Return False" das Beenden abbrechen.
    5. End Function


    Dann noch das hier:

    VB.NET-Quellcode

    1. Private Sub Window_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs)
    2. Dim context As IClosing = TryCast(DataContext, IClosing)
    3. If context IsNot Nothing Then
    4. e.Cancel = Not context.OnClosing()
    5. End If
    6. End Sub


    Die Werte meiner Settings-Datenklasse schreibe ich jeweils in den Settern der an die Controls gebundenen Eigenschaften im ViewModel.

    Hatte zuerst vor, ReadOnly Properties zu machen, die sich erst beim Beenden des Programms die Daten in den Gettern holen, aber da hatte ich Probleme mit den richtigen Instanzen...

    Ich denke damit bin ich auf dem richtigen Pfad und die Settings werden rechtzeitg gespeichert bzw. das Programm wird ja erst beeendet, wenn OnClosing True zurückgibt... Ich bitte um Rückmeldung, wenn dem nicht so ist ?(

    Edit:
    @dive26
    Ich werd noch wahnsinnig, da denkt man man speichert mal kurz paar Einstellungen:
    Während ich manche Settings, z. B. den Text einer Textbox problemlos gespeichert bekomme, hab ich mit einer ObservableCollection(Of String), die an einer ComboBox im selben ViewModel hängt, schon wieder Probleme. Es wird ein Nullwert in das JSON geschrieben. Kann das an dem von dir geschilderten Problem liegen oder fällt das raus, weil ich my.settings nicht benutze?

    Während es bei manchen Einstellungen möglich wäre, direkt bei der Änderung in das JSON File zu schreiben, gibt es andere, wo das wenig sinnvoll wäre. Z. B. die eines Lautstärkereglers, was bei mir z. B. ein Single wäre, bzw. dann müsste / sollte ich ja zumindest noch prüfen, ob der Mausbutton denn dann wieder losgelassen wurde beim Verschieben des Sliders....

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „kafffee“ ()

    kafffee schrieb:

    In diesem Zusammenhang kannst du "thread" ruhig mit "Thread" übersetzen. "Faden" ist nur die wörtliche Übersetzung, wenn du das Utensil meinst, mit dem man Socken flickt... Ansonsten ist dein Deutsch perfekt.


    Ich weiß ;)

    "Faden" nutze ich meist auf deutschen Foren, weil es mich immer wieder zum Schmunzeln bringt. Ich sage aber auch "Du Röhre" zu YouTube ;)

    Aber danke fürs Kompliment! Jahrelange Arbeit!

    kafffee schrieb:

    Okay jetzt hab ichs verstanden. Habs so ausprobiert und es funktioniert


    Das freut mich zu hören. Ich hoffe die Umstellung von C# zu VB war jetzt kein Riesenaufwand. Mit VB als Sprache konnte ich mich der Syntaxhalber nicht anfreunden. Irrwitzigerweise allerdings mit Lua und Bash...

    kafffee schrieb:

    Es wird ein Nullwert in das JSON geschrieben.


    Wenn in deinem JSON null steht, dann wurde der Parameter nicht korrekt serialisiert. Wenn du dein Programm debuggst, kannst du mit Debug.WriteLine() mal deinen JSON ausgeben lassen und dann alle Werte, die serialisiert werden auch. Oftmals sind die Eigenschaften dann leer und es ist kein Fehler von der JSON-Bibliothek.
    Wenn es das Problem wäre, was @dive26 beschrieben hat, dann wäre deine Datei korrupt und (wahrscheinlich!) nicht parsbar. Dann würde eine Exception geworfen.

    Ich habe über den Aufbau deiner Anwendung natürlich keine Ahnung, aber ich nehme gerade einfach mal an, dass du ein Fenster hast, in dem deine Einstellungen verwaltet werden. In dem Fall kannst du deine Einstellungen beim Schließen des Fensters wegspeichern, oder (wenn du wirklich paranoid sein willst) natürlich auch bei jeder Änderung. Aber je mehr Einstellungen du änderst und speichern willst, desto länger dauert der Prozess natürlich. Das spiegelt sich in der Performance deiner Applikation wieder.

    Was evtl. noch zu beachten wäre, ist wenn du Threads (schau, kein Faden! :D ) verwendest. Das kann wirklich immer solche merkwürdigen Effekte haben, dass Objekte plötzlich leer oder null sind. Meist sind das sog. Race-Conditions, wo zwei Teile deiner Applikation versuchen, auf den selben Speicher zuzugreifen.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)

    siycah schrieb:

    Das freut mich zu hören. Ich hoffe die Umstellung von C# zu VB war jetzt kein Riesenaufwand. Mit VB als Sprache konnte ich mich der Syntaxhalber nicht anfreunden

    Ne so kleine Fragmente krieg ich grad noch so hin :) Es gibt ja auch Übersetzer im Netz... Ich habe mich gerade wegen der Syntax für VB entschieden... Da hab dann zumindest ein End If nd kein } und weiss genau, wozu das dann gehört...

    Meine Einstellungen sind im Programm leider nicht alle in einem Fenster gesammelt, sondern "all over the place", wie es so schön auf Englisch heisst :-). Also auf verschiedenen Registern.

    Jetzt wo du es sagst mit den Threads: Das kann natürlich gut sein, mit Threads hatte ich schon so einige Probleme bei dieser Anwendung. Das mit den Race-Conditions leuchtet mir am ehesten ein...

    kafffee schrieb:

    Ich habe mich gerade wegen der Syntax für VB entschieden...


    VB hat mMn viele Eigenheiten, die die Syntax für mich unverständlicher machen. Es gibt IMO auch etwas wie "zu verbos".
    Dass man:

    VB.NET-Quellcode

    1. public function Foo(ByVal x As String) As Boolean
    2. end function ' für eine Methode mit Rückgabewert
    3. ' und
    4. public sub Bar(ByVal y As Integer)
    5. end sub ' für eine Methode ohne Rückgabewert


    hat leuchtet mir nicht ein. Es hat natürlich auch einen gewissen Charm in vielen Sachen, z.B. das eingebaute Handling für Events.

    Aber ich finde die Klammern für mich übersichtlicher, vor allem wenn man Klammer-Highlighting hat.
    VB hat aber durchaus auch seine Daseinsberechtigung und ich finde es super, dass es darum eine aktive Community geht - auch wenn sie manchmal (wie es eigentlich auch überall ist) ziemlich toxisch werden kann.

    kafffee schrieb:

    Meine Einstellungen sind im Programm leider nicht alle in einem Fenster gesammelt


    Ah okay, ja gut. Dann macht es natürlich Sinn, die in einem Model zu sammeln. Dein Vorgehen, die Einstellungen beim Beenden des Programms zu speichern leuchtet mir dann noch mehr ein.
    Müssen wir nur noch dem Sache auf den Grund gehen, dass die Einstellungen nicht richtig gespeichert werden...

    kafffee schrieb:

    Das mit den Race-Conditions leuchtet mir am ehesten ein...


    Ich würde dir empfehlen, einen Profiler (z.B. den mitgelieferten) zu nutzen, um den Speicher mal genauer zu untersuchen.
    Früher gab's mal ein Tool, das hieß JINX. Damit konnte man u.a. Deadlocks und Race Conditions im Code finden. Zwar nicht statisch, aber zur Laufzeit. Ich weiß nicht ob es sowas auch für modernes .net gibt, wenn ich ehrlich bin!
    Für mich war es bisher relativ einfach auf dem Mac oder Linux .net direkt zu AARCH64 oder x86_64 zu compilen und dann Valgrind laufen zu lassen - aber ich entwickle heutzutage eher selten für Windows/mit einer UI. Und wenn, dann bevorzuge ich Technologien wie MAUI.

    Ansonsten kann ich dir ein paar Ratschläge geben:
    Achte drauf, wo deine Mutexe gerade sind und wer die gerade locked. (In .net ist das ja mit dem lock-Schlüsselwort).

    In C++ finde ich sowas ganz nützlich:

    C-Quellcode

    1. #include <mutex>
    2. #include <string>
    3. using std::lock_guard;
    4. using std::mutex;
    5. using std::string;
    6. class Foo {
    7. public:
    8. void foo1() {
    9. // hier wird nicht zwingend gelocked. Ich definiere, dass diese Methode einfach weniger Prio hat
    10. lock_guard myLock(m_mutex, std::defer_lock);
    11. if (condition) {
    12. if (!myLock.try_lock()) {
    13. // try again later
    14. logger.log("Mutex locked by {0:s}!", m_mutexLockedBy);
    15. return;
    16. }
    17. // Mutex wurde gelocked
    18. m_mutexLockedBy = __FUNCTION__;
    19. // nach allen Berechnungen würde der Mutex wieder freigegeben
    20. }
    21. }
    22. void foo2() {
    23. lock_guard myLock(m_mutex); // hier wird der Mutex gelocked!
    24. m_mutexLockedBy = __FUNCTION__;
    25. // hier am Ende nach allen Berechnungen/Mutationen wird der Lock freigegeben
    26. }
    27. private:
    28. mutex m_dataMutex;
    29. string m_mutexLockedBy;
    30. };


    Alternativ gibt es in C++ auch timed_mutexe, wo man noch ein Timeout mitgeben kann. Ganz praktisch wenn man mit Hochleistungsrechnern arbeitet.

    In C# würde ich das so oder so ähnlich schreiben:

    C#-Quellcode

    1. using System;
    2. using System.Threading;
    3. class MyClass {
    4. internal static object _myLock;
    5. internal static string _lockedBy;
    6. static MyClass() {
    7. _myLock = new object();
    8. }
    9. public void NotImportant() {
    10. if (Monitor.IsEntered(_myLock) {
    11. Console.Error.WriteLine("Object is locked by {0}", _lockedBy);
    12. return;
    13. }
    14. if (condition) {
    15. lock (_myLock) {
    16. _lockedBy = nameof(NotImportant);
    17. // do stuff
    18. }
    19. }
    20. }
    21. // In der wichtigen Methode dann einfach stumpf locken
    22. }


    Ob die C# Syntax jetzt genau richtig ist, weiß ich nicht. Habe ich gerade hier im Forum so geschrieben. Aber ich denke (hoffe) dass du das Beispiel verstehst.
    Du wirst damit vielleicht nicht im ersten Anlauf eine 100%ige Abdeckung bekommen, aber mit solchen Locks (auch statisch, also instanzübergreifend) wirst du einige solcher Race-Conditions vermeiden können. Es geht aber wie gesagt nichts über einen Memory Profiler und ein Tool. was Deadlocks finden kann!
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    @siycah

    Ich werde jetzt nochmal genau im Setter überprüfen, wann genau der Wert meiner ObersvableCollection auf Nothing gesetzt wird. Ansonsten probier ich es mal mit deinem C# Code. Ich versteh ihn aber noch nicht so ganz:

    siycah schrieb:

    C#-Quellcode

    1. if (condition) {
    2. lock (_myLock) {
    3. _lockedBy = nameof(NotImportant);
    4. // do stuff
    5. }
    6. }
    7. }
    8. // In der wichtigen Methode dann einfach stumpf locken
    9. }


    Was kommt unter condition rein? Was meinst du mit "in der wichtigen Methode dann einfach stumpf locken"?
    Und welcher Code sollte bei do stuff reinkommen?
    Das Beispiel war an mein C++ Beispiel angelehnt.

    Ich glaube für das Speichern von Einstellungen brauchst du nicht zwingend eine Bedingung beim Locken von Ressourcen.
    Stattdessen kannst du relativ einfach mit sowas hier arbeiten:

    Spoiler anzeigen

    C#-Quellcode

    1. using System;
    2. namespace MySettings {
    3. using Newtonsoft.Json;
    4. using System.ComponentModel;
    5. using System.IO;
    6. public sealed class AppSettings: INotifyPropertyChanged {
    7. static object _lock;
    8. static AppSettings() {
    9. _lock = new object();
    10. }
    11. static readonly string DefaultConfigLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), ".myapp", "settings.json");
    12. #region "INotifyPropertyChanged"
    13. public event PropertyChangedEventHandler PropertyChanged;
    14. private void OnPropertyChanged(string propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    15. #endregion
    16. #region "Singleton"
    17. /*
    18. Hier erstellen wir einen Singleton.
    19. Das bedeutet, dass du innerhalb deiner Applikation zur Laufzeit
    20. immer nur eine Instanz dieser Klasse hast.
    21. */
    22. private static AppSettings? _settingsInstance;
    23. public static AppSettings Instance {
    24. get => _settingsInstance ?? (_settingsInstance = new AppSettings());
    25. }
    26. private AppSettings() {
    27. // alle Werte default initialisieren
    28. m_myArray = new short[100];
    29. m_myText1 = string.Empty;
    30. m_myObject = new { Placeholder = "" };
    31. }
    32. #endregion
    33. #region "Private members"
    34. private string m_myText1;
    35. private short[] m_myArray;
    36. private object m_myObject;
    37. #endregion
    38. public string MyText1 {
    39. get => m_myText1;
    40. set {
    41. if (object.Equals(m_myText1, value)) { return; }
    42. lock (_lock) { m_myText1 = value; }
    43. OnPropertyChanged(nameof(MyText1)); // Ggf. andere Bauteile deiner Applikation auf eine Änderung hinweisen
    44. }
    45. }
    46. public short[] MyArray {
    47. get => m_myArray;
    48. set {
    49. if (object.Equals(m_myArray, value)) { return; }
    50. lock (_lock) { m_myArray = value; }
    51. OnPropertyChanged(nameof(MyArray));
    52. }
    53. }
    54. public object MyObject {
    55. get => m_myObject;
    56. set {
    57. if (object.Equals(m_myText1, value)) { return; }
    58. lock (_lock) { m_myObject = value; }
    59. OnPropertyChanged(nameof(MyObject));
    60. }
    61. }
    62. public async Task LoadSettingsAsync() {
    63. if (!File.Exists(DefaultConfigLocation)) {
    64. await SaveSettingsAsync(); // nicht vergessen: wir haben oben Default-Werte eingegeben!
    65. return; // Default-Einstellungen wurden geladen
    66. }
    67. try {
    68. // ein kleiner Hack, den man auch durchaus anders lösen kann
    69. // ich mache das nun um den Code etwas kleiner zu halten!
    70. // Nach "LoadSettingsAsync()" musst du nur einmal noch AppSettings.Instance verwenden und dann in eine Variable auslagern
    71. using (var fStream = File.OpenText(DefaultConfigLocation)) {
    72. _settingsInstance = JsonConvert.DeserializeObject<AppSettings>(await fStream.ReadToEndAsync());
    73. }
    74. } catch (Exception ex) {
    75. // Fehler behandeln, ggf. wegloggen
    76. throw; // kannst du auch weglassen, aber dann musst du zwingend ein try/catch um den Aufruf machen!
    77. }
    78. }
    79. public async Task SaveSettingsAsync() {
    80. using (var fStream = File.OpenWrite(DefaultConfigLocation))
    81. using (var sWriter = new StreamWriter(fStream)) {
    82. var myConf = JsonConvert.SerializeObject(this, Formatting.Indented);
    83. await sWriter.WriteAsync(myConf);
    84. await sWriter.WriteLineAsync(); // Dateien immer mit einer Leerzeile beenden
    85. await sWriter.FlushAsync(); // Alles auf die Platte schieben
    86. }
    87. }
    88. }
    89. }


    Verwende den Code folgendermaßen:

    C#-Quellcode

    1. public async Task GetAppSettings() {
    2. await AppSettings.Instance.LoadSettingsAsync(); // ggf. Fehler behandeln
    3. m_mySettings = AppSettings.Instance; // du kannst stattdessen auch überall einfach AppSettings.Instance[.Setting] verwenden!
    4. }


    Als Bonus habe ich noch INotifyPropertyChanged mit eingebaut:

    C#-Quellcode

    1. public void Bar() {
    2. // nach dem Laden!
    3. AppSettings.Instance.PropertyChanged += (s, e) => {
    4. if (e.PropertyName == nameof(AppSettings.Instance.MyArray) {
    5. // auf Änderungen der Config reagieren :)
    6. }
    7. };
    8. }
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „siycah“ () aus folgendem Grund: natürlich habe ich vergessen, die Locks mit einzubauen :)

    @siycah

    Wow das ist erstmal ein Riesenhaufen-Code für mich, den ich erstmal verstehen muss... =O

    Mal für dich zum Verständnis:

    Ich habe in meinem ViewModel ne ObservableCollection(Of String), die per ItemsSource an ne ComboBox gebunden ist:

    VB.NET-Quellcode

    1. Private _Lautsprecher As ObservableCollection(Of String)
    2. Public Property Lautsprecher As ObservableCollection(Of String)
    3. Get
    4. Return _Lautsprecher
    5. End Get
    6. Set(value As ObservableCollection(Of String))
    7. If Object.Equals(_Lautsprecher, value) Then
    8. Return
    9. End If
    10. SyncLock _lock
    11. _Lautsprecher = value
    12. End SyncLock
    13. MainModule.MeineEinstellungen.LautsprecherListe = value
    14. RaisePropertyChanged()
    15. End Set
    16. End Property


    Da wird dann im Setter der value in die entsprechende Eigenschaft meiner Settingsdatenklasse "kopiert".

    Diese sieht dann so aus:

    VB.NET-Quellcode

    1. Private _LautpsrecherListe As ObservableCollection(Of String)
    2. Public Property LautsprecherListe As ObservableCollection(Of String)
    3. Get
    4. Return _LautpsrecherListe
    5. End Get
    6. Set(value As ObservableCollection(Of String))
    7. If Object.Equals(_LautpsrecherListe, value) Then
    8. Return
    9. End If
    10. SyncLock _lock
    11. _LautpsrecherListe = value
    12. End SyncLock
    13. End Set
    14. End Property


    Und die Settingsdatenklasse serialisiere ich dann...

    Ist das erstmal so in deinem Sinne? Weil funktionieren tuts jedenfalls nicht.

    Wie gesagt ich verstehe sehr wenig von deinem Code, das war jetzt eher geraten als gedacht...

    Noch ne andere Frage:

    Ich befülle die OC folgendermassen:

    VB.NET-Quellcode

    1. Public Sub ErmittleWiedergabeGeraete()
    2. Dim WiedergabeGeraeteInfo As New BASS_DEVICEINFO
    3. For i = 0 To Bass.BASS_GetDeviceCount - 1
    4. Bass.BASS_GetDeviceInfo(i, WiedergabeGeraeteInfo)
    5. Lautsprecher.Add(WiedergabeGeraeteInfo.name & ErmittleGeraeteTyp(WiedergabeGeraeteInfo))
    6. If IsInDesignMode = False Then
    7. Bass.BASS_Init(i, 44100, BASSInit.BASS_DEVICE_DEFAULT, Nothing)
    8. Bass.BASS_PluginLoad("bass_aac.dll")
    9. Bass.BASS_PluginLoad("bassalac.dll")
    10. Bass.BASS_PluginLoad("bassflac.dll")
    11. Bass.BASS_PluginLoad("bassopus.dll")
    12. Bass.BASS_PluginLoad("basswma.dll")
    13. Bass.BASS_PluginLoad("basswv.dll")
    14. End If
    15. Next
    16. Kopfhoerer = Lautsprecher
    17. If Lautsprecher.Count > 0 Then
    18. LautsprecherIndex = 1
    19. KopfhoererIndex = 1
    20. End If
    21. End Sub


    Wird da überhaupt der Setter von Lautsprecher aufgerufen wenn man .Add macht oder passiert das nur, wenn man z.B.

    Lautsprecher = LautpsrecherListe

    machen würde?

    kafffee schrieb:

    Wow das ist erstmal ein Riesenhaufen-Code für mich, den ich erstmal verstehen muss...


    Wer fraget, der bekommet. ;)

    kafffee schrieb:

    Wird da überhaupt der Setter von Lautsprecher aufgerufen


    Nein, da wird kein Setter aufgerufen, weil du nicht auf den Property-Setter zugreifst.

    kafffee schrieb:

    der passiert das nur, wenn man z.B.

    Lautsprecher = LautpsrecherListe

    machen würde?


    S.o.

    Wenn du nur auf den Wert (die Referenz) deiner Property zugreifst, dann kann niemand in deinem Programm auf Änderungen reagieren.
    Dafür bietet aber ​ObservableCollection Events, die du abonnieren kannst: learn.microsoft.com/en-us/dotn…collection-1?view=net-7.0

    In dem Fall musst du dann auf das ​CollectionChanged-Event horchen und ggf. dabei deine Einstellungen noch ändern:

    C#-Quellcode

    1. public MyViewModel() {
    2. InitializeComponent(); // Standard-Aufruf
    3. _lautsprecher = new ObservableCollection<T>(); // einmal initialisieren
    4. _lautsprecher.CollectionChanged += _lautsprecher_HandleCollectionChanged;
    5. m_meinEinstellungen = new MeineEinstellungen();
    6. }
    7. private void _lautsprecher_HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
    8. m_meineEinstellungen.LautsprecherListe = sender as ObservableCollection<string>; // hier passt du dann (ebenfalls) deine Einstellungen an
    9. }


    So funktioniert übrigens auch das DataBinding im Hintergrund, weshalb du keine eigenen Events dafür definieren musst; die handlet .Net im Hintergrund, weshalb es bei dir in der Oberfläche nicht zu Fehlern kam.


    kafffee schrieb:

    ch habe in meinem ViewModel ne ObservableCollection(Of String), die per ItemsSource an ne ComboBox gebunden ist:


    kafffee schrieb:

    Und die Settingsdatenklasse serialisiere ich dann...

    Ist das erstmal so in deinem Sinne? Weil funktionieren tuts jedenfalls nicht.


    Bis auf die o.g. Änderungen sieht das erstmal richtig aus, ja :)

    Fürs nächste Mal ist es immer praktisch, wenn man ein vollständiges Beispiel mitliefert. So hätten wir viel schneller an eine Lösung kommen können.
    Ich weiß man will - gerade am Anfang - nie seinen Quellcode preisgeben, aber letzten Endes kann man nur so jemandem wirklich gut und qualifiziert helfen.

    Zugegeben, ich hätte in meinem Beispiel aber auch eine ​ObservableCollection einbauen können :D

    Was die Lesbarkeit von deinem Code angeht, würde ich dir auch gerne einen Rat geben. Ob du dem befolgst oder nicht ist natürlich dir selbst überlassen, aber das hat so die Erfahrung der letzten 10+ Jahre gezeigt.

    Es gibt grundsätzlich fünf Arten von Variablen:
    1. globale Variablen
    2. Member-Variablen
    3. Statische Member-Variablen
    4. Funktions (Methoden) Parameter
    5. Variablen in Methoden/Funktionen
    In deinem Beispiel verwendest du z.B. für deine privaten Member ein Unterstrich, was schon mal absolut super ist!
    Um diese weiter differenzieren zu können, würde ich den Unterstrich aber nur bei statischen Membern (in VB müsste das ​shared sein?) verwenden.
    Für Member (Instanzvariablen) würde ich ein m_-Präfix verwenden.

    Variablen in Methoden/Funktionen und Parameter würde ich nicht so präfixen, außer es sind out-Parameter. Bei allem anderen kann man sich beruhigt auf die IDE verlassen, also das ist definitiv keine Empfehlung für ungarische Notation! :D

    Das wird ganz oft praktiziert und es hilft wirklich ungemein Verstehen von Quellcode, vor allem in Forum-Posts wo man kein Zugriff auf die gesamte Quellcode-Basis hat.
    Wie gesagt: musst du natürlich nicht befolgen und es hält keiner deswegen mehr oder weniger von dir, aber es ist ein Erfahrungswert, den hier auch sicherlich einige teilen :)
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    @ErfinderDesRades

    Problem ist momentan:

    Ich hab in meinem VM eine Property Lautsprecher (s.Post 34). In dessen Setter möchte ich den eigenen Wert an meine Property in der Klasse, die ich dann serialisiere übertragen ((LautsrpecherListe), (bzw. übertragen will, das klappt nicht).

    Serialisieren selber klappt vorzüglich.

    Nun habe ich es mal so versucht, wie @siycah vorgeschlagen hat, und habe das Event abonniert:

    VB.NET-Quellcode

    1. AddHandler Lautsprecher.CollectionChanged, AddressOf UpdateLautsprecher
    2. Private Sub UpdateLautsprecher(sender As Object, e As NotifyCollectionChangedEventArgs)
    3. MainModule.MeineEinstellungen.LautsprecherListe = Lautsprecher
    4. End Sub


    Aber dieses wird nicht gefeuert. Muss ich das vielleicht noch manuell machen?

    Des Weiteren habe ich noch Folgendes zufällig entdeckt:

    Ich habe noch ein anderes Property-Paar:
    Kopfhoerer und KopfhoererListe

    Mit dem mache ich exakt dasselbe, der einzige Unterschied ist wirklich der Name. Und bei denen funktioniert das übergeben der Werte...

    Und jetzt wirds noch kurioser:

    Also hab ich mich gefreut, denn mein Plan ist es, beim Laden des Programms zu checken, ob Audiogeräte dazukommen oder wegfallen. Deshalb die ganze Mühe...
    Beim Laden des ViewModels mache ich also Folgendes:

    VB.NET-Quellcode

    1. If LayerViewModel.SettingsViewModel.Lautsprecher IsNot MainModule.MeineEinstellungen.KopfhoererListe Then
    2. Debug.WriteLine("Audiosetup hat sich geändert!")


    Hab einen Haltepunkt gesetzt und Lautsprecher und KopfhoererListe enthalten genau dasselbe, also wie es sein soll, trotzdem bekomme ich da die Ausgabe "Audiosetup hat sich geändert"...

    Hää?
    Kannst du hier vielleicht mal ein Beispiel einfügen, was dieses Verhalten reproduziert?

    Der von mir gelieferte Quellcode funktioniert.

    Irgendwo ist da noch ein Bock drin.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    Ich glaub dir voll und ganz dass der Code bei dir funktioniert. Ich muss dazu sagen ich hab wirklich nur die Codestellen bei mir implementiert, die ich oben gepostet habe, den Rest hab ich noch nicht so ganz durchschaut, deswegen...

    Mir würde es ja voll und ganz reichen, wenn das hier funktioniert:

    VB.NET-Quellcode

    1. If LayerViewModel.SettingsViewModel.Lautsprecher IsNot MainModule.MeineEinstellungen.KopfhoererListe Then
    2. Debug.WriteLine("Audiosetup hat sich geändert!")


    Ist da irgendwas an der Syntax falsch und ich hab da irgendeine falsche Idee davon? Das sind beides OvservableCollection(Of String)

    Ansonsten kann ich evtl schon mal probieren ein kleines Demopragramm zu machen aber ich bezweifle dass ich das rekonstruieren kann, weisst schon, der Vorführeffekt

    kafffee schrieb:

    VB.NET-Quellcode

    1. If LayerViewModel.SettingsViewModel.Lautsprecher IsNot MainModule.MeineEinstellungen.KopfhoererListe Then
    Ist da irgendwas an der Syntax falsch und ich hab da irgendeine falsche Idee davon?
    kann man wohl sagen.
    Speziell deine Idee vom IsNot-Operator bedarf der Überarbeitung. ;)

    probierma

    VB.NET-Quellcode

    1. If not LayerViewModel.SettingsViewModel.Lautsprecher.SequenceEquals(MainModule.MeineEinstellungen.KopfhoererListe) Then