Emin Hasanov
Emin Hasanov

Reputation: 1362

Save WPF ViewModel current state

In my WPF application's ViewModel has few properties, observableCollections etc. How can I save their current state as file when app is closing or save clicked Should I use [Serializable] implementation?

What is the best approach?

public class CustomerViewModel 
{
    private Customer obj = new Customer();

    public string TxtCustomerName
    {
        get { return obj.CustomerName; }
        set { obj.CustomerName = value; }
    }        

    public string TxtAmount
    {
        get { return Convert.ToString(obj.Amount) ; }
        set { obj.Amount = Convert.ToDouble(value); }
    }
}

Upvotes: 1

Views: 1993

Answers (2)

Mike Strobel
Mike Strobel

Reputation: 25623

There isn't a standard practice for persisting MVVM state. You just need to pick a format, just like you would for any other serialization problem. I personally am a fan of using Xaml, as it's human readable and can represent most of the types that you might be using in a WPF application.

Example code, included in a base class for your view models:

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private static string RootStoragePath =>
        Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            "YourApplication",
            "ViewState");

    protected string StateFilePath =>
       Path.Combine(RootStoragePath, this.GetType().Name + ".xaml");

    public void Save()
    {
        var fileName = StateFilePath;

        var directoryName = Path.GetDirectoryName(fileName);
        if (directoryName != null && !Directory.Exists(directoryName))
            Directory.CreateDirectory(directoryName);

        XamlServices.Save(fileName, this);
    }

    public void Load()
    {
        var fileName = StateFilePath;

        if (File.Exists(fileName))
        {
            using (var file = File.OpenRead(fileName))
            using (var reader = new XamlXmlReader(file))
            {
                var writer = new XamlObjectWriter(
                    reader.SchemaContext,
                    new XamlObjectWriterSettings { RootObjectInstance = this });

                using (writer)
                {
                    XamlServices.Transform(reader, writer);
                }
            }
        }
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

The saved state would look something like this:

<CustomerViewModel TxtAmount="3.5"
                   TxtCustomerName="Mike Strobel"
                   xmlns="clr-namespace:WpfTest;assembly=WpfTest" />

Note that this is a minimal example. Ideally, your error handling on the storage operations would be more robust. Be sure to change RootStoragePath to be unique to your application.

Upvotes: 1

mm8
mm8

Reputation: 169400

What the best approach is depends on your requirements. You could for example serialize your class using the built-in DataContractJsonSerializer class without modifying the class at all:

CustomerViewModel vm = new CustomerViewModel();
//...

using (MemoryStream ms = new MemoryStream())
{
    DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(CustomerViewModel));
    ser.WriteObject(ms, vm);
    File.WriteAllText("save.txt", Encoding.UTF8.GetString(ms.ToArray()));
}

You will need to add a reference to System.Runtime.Serialization.dll. Please refer to the following blog post for more information: https://www.bytefish.de/blog/enum_datacontractjsonserializer/.

A popular high-performance third-party JSON serializer is othwerwise Json.NET: https://www.newtonsoft.com/json. And there are plenty of other solutions that use various formats, whether they are text-based or binary. In complex cases you may have to write your own custom serializer.

Upvotes: 2

Related Questions