StorageHandler

Topics: Framework Services
Jun 8, 2011 at 10:09 PM
Edited Jun 8, 2011 at 10:48 PM

what would cause the storagehandler not store an updated instance of the viewmodel its attached to or the viewmodel itself not be a current one? At what point does the storagehandler actually commit the instance?

if I leave a screen that is setup for InAppSettings (SettingsView) and go back after having made changes upon return after having left the changes made aren't retained or reflected on the interface. What is it that I am not doing correctly?

at this rate [survivetombstone] sounds easier...

Coordinator
Jun 9, 2011 at 3:34 AM

The instances are committed when either a. the app is deactivated or b. the app is closed. At that time it only persists instances that are alive. It holds weak references internally. What is the lifetime of the SettingsViewModel?

Jun 9, 2011 at 3:13 PM
Edited Jun 9, 2011 at 3:13 PM

lifetime of the SettingsViewModel right now inherits from Screen, maybe I am not inheriting from the correct object.

Coordinator
Jun 9, 2011 at 3:23 PM

How is it registered with the container?

Jun 9, 2011 at 3:23 PM

I think Rob means how the SettingsViewModel is registered e.g. Singleton or PerRequest?

Jun 9, 2011 at 3:25 PM
Edited Jun 9, 2011 at 3:31 PM

its PerRequest at present

Coordinator
Jun 9, 2011 at 3:45 PM

Make it a singleton :)

Jun 9, 2011 at 4:06 PM
Edited Jun 9, 2011 at 4:12 PM

thanks... so now that its working as expect, what is the method for retreival of the tombstoned values from the storagehandler, in another viewmodel?

Coordinator
Jun 9, 2011 at 6:41 PM
Edited Jun 9, 2011 at 6:42 PM

What do you mean by "in another viewmodel"? You can gain access to the SettingsViewModel by having it injected into the ctor of any other view model. Is that what you mean?

Jun 10, 2011 at 3:40 AM

No i mean the saved settings that were made by using the StorageHandler in the SettingsViewModelStorage.cs, do I just use the injection of the SettingsViewModel as you indicated...

what I meant by in another viewmodel was the rest of the application since these are settings for the entire app.

Coordinator
Jun 10, 2011 at 1:37 PM

Yes. Just have the SettingsViewModel injected into the ctor of whatever ViewModel needs to use them.

Jun 10, 2011 at 2:16 PM

just way to convienent....

Thanks for the work you put into 1.1...

Jun 11, 2011 at 3:26 PM
Edited Jun 11, 2011 at 3:30 PM

so as previously discussed to retrieve settings from the settingsviewmodel was thru injection.  so injecting works but doesn't produce the previously saved data till you hit the settings screen to call a restore off the storagemech.. At which point the data stored is correctly rehydrated into the SettingsViewModel, this isn't an ideal situation.  note: this is on the emulator at present, and the settings were set so it wasn't a reset emulator.

Coordinator
Jun 11, 2011 at 8:06 PM

I'm confused about what you are doing. The VM should be restored as soon as it is created by the container. The whole storage mechanism hooks into the container so that data is restored when things are instantiated *unless* you specify a different *timing* in your configuration. Perhaps that is what is going wrong? For example, if you have this:

Property(x => x.MyProperty)
    .InAppSettings()
    .RestoreAfterViewLoad();

Then the MyProperty data will be stored in app settings, but it will not be restored until after the view associated with your settings vm is loaded. If you want it restored immediately, on creation, you do it like this:

Property(x => x.MyProperty)
    .InAppSettings();

Jun 11, 2011 at 9:35 PM

yep its a configuration brain fart...  Helps if I would pay attention more.

 

Coordinator
Jun 11, 2011 at 11:56 PM

It may not have been clear in the documentation. I added a special note to try and help future developers.

Jul 7, 2011 at 12:08 AM

I have what I hope is a simple question.  I'm a newbie to CM, and in my app I have a VM (let's call it VM1) that inherits from Conductor<IScreen>.Collection.OneActive.   The associated view has a Pivot control bound to the VM's Items property.  I've added instances of another VM (let's call it VM2) into Items, and its views are PivotItems.  The user can add new instances of VM2 on the fly.

So far, so good... the UI works, and now I'm trying to get tombstoning working.  I need to persist the user-created instances of VM2 (the contents of the pivot pages).  I have classes that implement IStorageHandler<VM1> and IStorageHandler<VM2>.  The first specifies persistence for VM1.Items, and the second one specifies persistence for a handful of the simple properties of VM2, properties (strings and ints).

I was kind of hoping that a) CM would attempt to persist VM1's Items property, and that it would look at the actual type of the objects in Items to get an appropriate storage handler; but it doesn't appear to be getting VM2's storage handler.  Rather, it looks like it's trying to do a generic serialization on it and include all the other properties, and it's blowing up with an InvalidDataContractException.  I tried adding a type-specific wrapper around VM1.Items defined as List<VM2>, but that didn't work either.

Am I doing something wrong?  I can post code snippets if the above is hard to follow... basically I'm unclear on what I need to do to get CM to persist collections in a VM.

Thanks!

Mike

Jul 7, 2011 at 12:43 AM

Sorry, just re-read my post and realized I could have been clearer... My storage classes inherit from StorageHandler<T>.  Here's the top-level one:

public class CarsStorage : StorageHandler<CarsViewModel> {
    public override void Configure() {
        Property(x => x.Items).InAppSettings();
    }
}

and here's the one for the contents of Items (Items contains instances of CarViewModel):

public class CarStorage : StorageHandler<CarViewModel> {
    public override void Configure() {
        Id(x => x.Id);
        Property(x => x.Year).InAppSettings();
        Property(x => x.MakeId).InAppSettings();
        Property(x => x.DriveTrainId).InAppSettings();
    }
}
CarViewModel inherits directly from Screen.  I have another VM that also inherits from Screen, for which persistence is working well.
Mike
Jul 7, 2011 at 12:55 AM

in your boot strapper how do you have both viewmodel's registered with the container?

Jul 7, 2011 at 12:58 AM

Like so:

public class AppBootstrapper : PhoneBootstrapper {
        PhoneContainer _container;

        protected override void Configure() {
            _container = new PhoneContainer(RootFrame);

            _container.RegisterPhoneServices();
            // ...
            _container.PerRequest<CarsViewModel>();
            _container.PerRequest<CarViewModel>();

            AddCustomConventions();
        }
		
        // ...
}
Coordinator
Jul 7, 2011 at 11:45 AM

The scenario you describe is not explicitly supported out of the box. I could look at supporting this in v1.2. To help me with that, would you create a simple solution that demonstrates the scenario? Along with the storage handlers you currently have. Create a new issues with the description of what you are trying to do and attach the ticket. 

FWI, what you want to do isn't ooutb, but the mechanism can be extended to do it, You would need to create a custom storage instruction.

Coordinator
Jul 7, 2011 at 11:53 AM

You could also write a custom IStorageHandler that does manual serialization.

Jul 7, 2011 at 2:29 PM

Thanks for the reply - that's helpful to know. In my app I'll probably go with the custom IStorageHandler for now, as I'm used to doing my own binary serialization in phone apps. But I'll create a demo solution today or tomorrow and attach it to a ticket as you described.

 

Feb 5, 2013 at 6:33 AM
First, after seeing this and learning I need to make these app settings a Singleton and not using RestoreAfterActivation, thank you. This is amazing.

Second, do you have any advice when we need to store a "selected item" for a list (an object) and then use it when we load it from storage later? I'm binding to a list (not stored) and on restore, I'm getting an exception basically saying it can't find the restored instance in the list. To solve this, I overrode the Equals and GetHashCode method on the object to compare it's "unique key".

Have you done this before? Bind to a list, don't store it, and then try to store it's selected item and restore later? I suppose it might be safer to store the "key" itself rather than the entire object.