Nucs / JsonSettings

This library simplifies creating configuration for your C# app/service by utilizing the serialization capabilities of Json.NET to serialize nested (custom) objects, dictionaries and lists as simply as by creating a POCO and inheriting JsonSettings class.
MIT License
76 stars 18 forks source link

[Feature Request] Support AutoSave for INotifyPropertyChanged, INotifyCollectionChanged #18

Closed ghost1372 closed 3 years ago

ghost1372 commented 3 years ago

I want to save the value of the datagrid column width in the setting file I used the following:

public class Configs : JsonSettings
    {
        public override string FileName { get; set; } = "config.json";

        public virtual double[] ColumnWidth { get; set; } = new double[5];
        public virtual string[] ColumnWidth3 { get; set; } = new string[5];
        public virtual DataGridLength[] ColumnWidth2 { get; set; } = new DataGridLength[5];
    }

public Configs Settings = JsonSettings.Load<Configs>().EnableAutosave();

private void dataGrid_LayoutUpdated(object sender, EventArgs e)
        {
            for (int index = 0; index < dataGrid.Columns.Count; index++)
            {
                Settings.ColumnWidth2[index] = new DataGridLength(dataGrid.Columns[index].ActualWidth);
                Settings.ColumnWidth[index] = dataGrid.Columns[index].ActualWidth;
                Settings.ColumnWidth3[index] = dataGrid.Columns[index].ActualWidth.ToString();
            }
        }

If I use the message box, I can see that all the columns have the correct value

MessageBox.Show(string.Join("; ", dataGrid.Columns.Select(column => column.ActualWidth)));

image

But the setting file shows the wrong information Untitled

Nucs commented 3 years ago

Did it happen due to lack of file versioning protection as described in #19 (did it fail to load becuase json was the older format?)

ghost1372 commented 3 years ago

@Nucs No I created a example project please take a look at it By clicking on the button, the size of the columns is displayed correctly But the same sizes are stored differently in the settings file WpfApp3.zip

Nucs commented 3 years ago

The idea you were trying to achieve is using a settings file as a view model or partially as a view model. So I've decided to implement support for INotifyPropertyChanged and INotifyCollectionChanged.

Please have a look at this self explanatory example: https://github.com/Nucs/JsonSettings/blob/master/examples/INotifyPropertyChangedExample.cs Also added IgnoreAutosaveAttribute

So now your settings file should look:

public class Configs : NotifiyingJsonSettings {
    public override string FileName { get; set; } = "config.json";

    private ObservableCollection<double> _columnWidth = new ObservableCollection<double>();
    private ObservableCollection<DataGridLength> _columnWidth2 = new ObservableCollection<DataGridLength>();
    private ObservableCollection<string> _columnWidth3 = new ObservableCollection<string>();

    public virtual ObservableCollection<double> ColumnWidth {
        get => _columnWidth;
        set {
            if (Equals(value, _columnWidth)) return;
            _columnWidth = value;
            OnPropertyChanged();
        }
    }

    public virtual ObservableCollection<string> ColumnWidth3 {
        get => _columnWidth3;
        set {
            if (Equals(value, _columnWidth3)) return;
            _columnWidth3 = value;
            OnPropertyChanged();
        }
    }

    public virtual ObservableCollection<DataGridLength> ColumnWidth2 {
        get => _columnWidth2;
        set {
            if (Equals(value, _columnWidth2)) return;
            _columnWidth2 = value;
            OnPropertyChanged();
        }
    }
}

This will trigger a save whenever the property (INotifyPropertyChanged) by calling OnPropertyChanged() from setter or one of the items inside the property (INotifyCollectionChanged) are changed.

Also only properties in the settings class are monitored, nested objects with INotifyPropertyChanged/INotifyCollectionChanged won't trigger save.

ghost1372 commented 3 years ago

thank you i will test it when nuget package is ready

ghost1372 commented 3 years ago

@Nucs i tested your sample code and column width is stored correctly but I see the 0s again. Is this normal? 3

ghost1372 commented 3 years ago

I also noticed something strange If we run the example project I sent earlier with a change

private void Button_Click(object sender, RoutedEventArgs e)
        {
            Settings.MyProperty = "test";
        }

All information is stored correctly in the database 🤔 3

Nucs commented 3 years ago

but I see the 0s again. Is this normal?

I think Json.NET might be serializing the ObservableCollection's internal array instead of determining how many items to serialize using Count property.

I also noticed something strange If we run the example project I sent earlier with a change

I think the binding for the column's widths is not working on the XAML level. So it triggers save only when change Settings.MyProperty. Could you drop .zip you ran? thanks.

ghost1372 commented 3 years ago

Run the program Open the settings file in the text editor (vscode) Click on the button You can see the changes WpfApp3.zip

Nucs commented 3 years ago

I initially assumed you would bind the column widths by templating the columns. Anyways, on the basic level - this is how it should be done without advanced binding. WpfApp3.zip

Key note, Collections have to be initialized empty, otherwise every load you'll end up with 5 more items (count: 5 in constructor). So you have to dynamically do it like in the example.

ghost1372 commented 3 years ago

Arigato My problem was completely solved, I think this issue can be closed