10

I'm currently working on a C# WPF project using some of the Controls provided by Telerik and I'm respecting the MVVM pattern with:

  • a Model containing the data

  • a ViewModel presenting the data to the View

  • a View displaying the data

Of course, some of the Models can be reused and displayed in several Views (in my case, the data can be the content of shapes displayed on different diagrams).

Now I'm starting to design how to save the data. My goal is very simple: when the user leaves the application and comes back, all Views must be identical in terms of content, color, fonts, size, position in space...

Saving a Model covers only the content part. How would you save the display properties like color, fonts, position in space, especially when several Views will rely on the same Model ? Should I use Bindings and move all properties from the View to the Model ? With the risk that the Model complexity will greatly increase ?

Do you have any solution where you keep the UI properties separated from the Model ?

Also is there any best practice on how to save and load data "the MVVM way" ?

Thanks in advance.

David MVVM
  • 109
  • 1
  • 4
  • Yes you are correct you should move all those properties to your Model. – Novitchi S Aug 01 '14 at 15:26
  • View-related stuff is view-related stuff. You might create some service interface or methods which works on the same or a different persisting layer as your models, and use this service (or method) in your view-related code-behind to save/load UI state(s). But i would not mix and mingle models and view models with stuff which is entirely and purely related to the view(s) only. (I am speaking about stuff that should be of no concern to VMs, such as whether text is white-on-black instead of black-on-white, for example, or whether the user makes a resizable panel a little larger than another...) –  Aug 01 '14 at 15:33
  • @elgonzo : I like your solution better than mine because you keep the separation between the "Business" properties and the "Display" ones. But it makes me sad to add code-behind when I tried so hard to keep it to a minimum (like DataContext).Have you read any article or blog post describing your service interface or methods ? – David MVVM Aug 01 '14 at 16:44
  • With service/method i just meant some facility to load/save your settings (like you have something like a data service to load/save/persist your models). What i usually do is to have a type with one (singleton) instance accessible through a static property like `UISettings.Instance`. This instance could have properties with the UI settings or further 'configuration containers' specific to certain windows/UI elements. In XAML you could then access a UI setting like this `"{Binding SomeProperty, Source={x:Static local:UISettings.Instance}}"` (perhaps you will need to declare TwoWay mode as well) –  Aug 01 '14 at 17:05
  • And somewhere else in your code-behind, you could then do something like `UISettings.Instance.Load()` in the application startup sequence, and `UISettings.Instance.Save()` at some appropriate time (assuming you will implement those Load and Save method for your UISettings class) –  Aug 01 '14 at 17:07
  • @elgonzo Thanks for the clarification. What do you think of StephenM approach ? – David MVVM Aug 01 '14 at 17:29
  • 1
    On the other hand, instead of spinning your own UISettings class (as i do, but then again, our app is somewhat larger and has some shady history...) you could directly use the Settings facility as provided by default in your C# project (see Sheridan's answer). Binding to the Settings object would be similar as to what i said before: `"{Binding SomeProperty, Source={x:Static YourNamespace:Settings.Default}}"` (*YourNamespace* would be the namespace akin to the *YourProject.Properties* namespace) –  Aug 01 '14 at 17:35
  • StephenM is suggesting to put view-only stuff such as colors, fonts and size in the VM - not good in my opinion - VMs should abstract UI aspects with regard to business logic and interaction logic - they should not become a surrogate UI (but that is just my opinion. It is in no way meant to be the one and only definite statement...) Note, that his example with font is somewhat ambiguous: if for example font properties would be part of document data/definition, then font information would need to be represented by the model/viewmodel for a document (and then font setting would not be view-only) –  Aug 01 '14 at 17:39
  • @elgonzo I suggest this approach because (as I see it), a VM is an encapsulation of the state of the application. Using a VM as an abstraction of business/interaction logic suggests to me that the domain hasn't yet been sufficiently broken down into a valuable class structure. As with my suggestion, the ViewModel (which is only a *single* class) has one single responsibility: Provide the View with the data and context needed to render itsself. If part of the requirement for the program is making sure the color and font are persistent, that is the responsibility of nothing but the ViewModel. – astallaslions Aug 01 '14 at 18:43
  • It is the Views job to Display the data. It should not have to contain code to figure out what it has to do next. I think using code-behind to reload the last font and such is less than satisfactory. Also, with my ambiguous example: Font wouldn't need to known by the model. Anyways, it's not like a decision like this is going to make or break someones new start-up. – astallaslions Aug 01 '14 at 19:10
  • @StephenM, in MVVM the view is concerned about styling and layout, not the ViewModel. The desire/requirement to persist layout/styling information does not make it a concern of the VM in my opinion. Anyway, i think we can agree that we disagree... :) –  Aug 01 '14 at 19:13
  • Yeah I was going to say. :) Ultimately it's a design decision that wouldn't even take that long to completely undo and re-implement later either way. – astallaslions Aug 01 '14 at 19:54

1 Answers1

4

I save user options like that in the Application Settings. If you're not familiar with them, you can find out the full story on the Using Settings in C# page on MSDN. In short, you can have Application and User Settings and it sounds like you'd want User Settings that are saved per user. UI properties have no place in any model as there is no benefit from storing that kind of information along with model data. You can do this kind of thing with them:

private void LoadSettings(MainWindow window)
{
    Settings.Default.Reload();
    window.WindowStartupLocation = WindowStartupLocation.Manual;
    window.Left = Settings.Default.ApplicationLocation.X;
    window.Top = Settings.Default.ApplicationLocation.Y;
    window.Width = Settings.Default.ApplicationSize.Width;
    window.Height = Settings.Default.ApplicationSize.Height;
    window.WindowState = Settings.Default.IsApplicationMaximised ? WindowState.Maximized : WindowState.Normal;
}

private void SaveSettings(MainWindow window)
{
    Settings.Default.ApplicationLocation = new Point(window.Left, window.Top);
    Settings.Default.ApplicationSize = new Size(window.Width, window.Height);
    Settings.Default.IsApplicationMaximised = window.WindowState == WindowState.Maximized;
    Settings.Default.Save();
}

It might be easier to add some of the properties to a base or main view model, so that you can data bind to them:

public void SaveSettings(string tabName)
{
    Settings.Default.ReleaseTrackSideFormat = StateManager.ReleaseTrackSideFormat;
    Settings.Default.ReleaseLabelCopyFormat = StateManager.ReleaseLabelCopyFormat;
    Settings.Default.ReleaseExportDestination = StateManager.ReleaseExportDestination;
    Settings.Default.ReleaseSearchOptions = new SerializableReleaseSearchOptions(ReleaseSearchOptions);
    ...
    Settings.Default.Save();
}

public void LoadSettings()
{
    Settings.Default.Reload();
    StateManager.ReleaseTrackSideFormat = Settings.Default.ReleaseTrackSideFormat;
    StateManager.ReleaseLabelCopyFormat = Settings.Default.ReleaseLabelCopyFormat;
    StateManager.ReleaseExportDestination = Settings.Default.ReleaseExportDestination;
    ReleaseSearchOptions = new ReleaseSearchOptions(Settings.Default.ReleaseSearchOptions);
    ReleaseExportSearchOptions = new ReleaseExportSearchOptions(Settings.Default.ReleaseExportSearchOptions);
    ...
}

UPDATE >>>

You're quite right... you wouldn't want to store your model data in this way. This is for UI related user preferences. If you're also asking how to save your model data, then the quick answer is that I'd store mine in a database, but that's up to you. You could just as easily store it in a file on your computer. It all depends on scale, convenience, speed, access to resources, etc., so it's not really a question that is suitable in scope for this website.

However, there are plenty of tutorials online showing different ways of saving data step by step. To answer that question, I recommend that you follow some of them.

What I can tell you is that it is customary to put your data access code into a class in a separate project (or folder for a small project). This class is then often referenced only in a parent, or base view model property and all child view models would access their data via this... perhaps something like this:

protected IModel Model
{
    get { return model; }
}

Then the child view models would use it like this:

SomeCollectionProperty = Model.GetSomeData();

Or:

Model.SaveSomeData(SomeCollectionProperty);

To clarify a little further, it makes no difference at this stage what implementation you have for this Model class. The view models don't care whether it is using a database, or a plain old text file as long as it implements the GetSomeData and SaveSomeData methods. Therefore, it is good to use an interface here, especially if you ever want to do any testing of the view models.

Finally, you might want to take a look at my answer to the Project structure for MVVM in WPF question to get a better idea about that too.

Community
  • 1
  • 1
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • I'm not sure we can consider that the "Display" properties of my project are User Settings. Imagine that the application will display a diagram with 20 shapes. Each shape is bound to a content saved in the Model. And you want to save the position of each shape in space. Would you do it with User Settings ? – David MVVM Aug 01 '14 at 17:05