Viewmodel is closed at startup and build again

Topics: Issues / bugs
May 28, 2014 at 7:18 AM
Edited May 28, 2014 at 7:38 AM
Next problem with the newest beta :-(

The viewmodel of the catel:usercontrol is build, closed and build again.
It'ts not clear why.

Code in view:
-> called twice. Not clear why? Before and after close of viewmodel
protected override IViewModel GetViewModelInstance(object dataContext)
{
       if (dataContext is PlaceInstance)
       {
           var model = dataContext as PlaceInstance;
           var manager = ServiceLocator.Default.ResolveType<IViewModelManager>();

           var vm = manager.GetViewModelsOfModel(model).SingleOrDefault();

            return vm;
         }

    return null;
}
Code in viewmodel.
-> called at startup
protected override void Close()
{
        base.Close();

        Debug.WriteLine("Close");
}
CallStack: (opened wit .Net Reflector)

1: public UserControl(IViewModel viewModel)

2: handler12 = (sender, e) => EventHandlerExtensions.SafeInvoke(this._viewDataContextChanged, this);

3: public override void OnTargetViewDataContextChanged(object sender, EventArgs e)

4: base.ViewModel = base.ConstructViewModelUsingArgumentOrDefaultConstructor(newDataContext);

5: CloseAndDisposeViewModel
May 28, 2014 at 9:16 AM
Jun 18, 2014 at 11:16 AM
Again a question to this problem.
I dont know really where to post this comment. You have moved the discussion to the issue with the tabcontrol.
But I have do some tests and it looks like it has nothing todo with the tabcontrol. So I thought it will be better to ask here.

My viewmodels are constructed and initialized. But then they are closed again at startup. No I get errors. Because the Validation are still called in the viewmodels and the propertys of the viewmodel are all null.

The Viewmodels are closed because of the event OnInheritedDataContextChanged in DataContextChangedHelper. But I don't know who is changing the datacontext.

I registered my viewmodel with 2 views
viewModelLocator.Register(typeof(PlaceGeneralView), typeof(PlaceViewModel));
viewModelLocator.Register(typeof(PlaceDetailView), typeof(PlaceViewModel));
I added a CustomViewModelFactory
public class CustomViewModelFactory : ViewModelFactory
    {
        public CustomViewModelFactory(ITypeFactory typeFactory)
            : base(typeFactory)
        {
        }

        public override IViewModel CreateViewModel(Type viewModelType, object dataContext)
        {
            var manager = ServiceLocator.Default.ResolveType<IViewModelManager>();
            IViewModel vm = null;

            if (viewModelType == typeof(PlaceViewModel))
            {
                var model = dataContext as PlaceInstance;
                vm = manager.GetViewModelsOfModel(model).SingleOrDefault();
            }

            if (vm != null)
                return vm;

            // Fall back to default behavior
            return base.CreateViewModel(viewModelType, dataContext);
        }
    }
And used the views within a itemscontrol

View 1
<ItemsControl ItemsSource="{Binding Locations}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <views:PlaceDetailView />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
 </ItemsControl>
View 2
<ItemsControl ItemsSource="{Binding Locations}">
    <ItemsControl.ItemTemplate>
          <DataTemplate>
               <views:PlaceGeneralView />
           </DataTemplate>
     </ItemsControl.ItemTemplate>
</ItemsControl>
Works perfect in catel 3.9.

But who close the viewmodels in Catel 4.0. Or rather why is the datacontext changed.
Jun 18, 2014 at 1:28 PM
Hm.

It depends on the count of instances of viewmodels.

I tested with 36 instances. Everythink works ok. Then i created 40 instances. And CloseviewModel is called. ???
Jun 18, 2014 at 1:31 PM
What is e.OldValue and e.NewValue on the datacontext? In Catel 4.0, we have changed the behavior to be aware of inherited datacontext as well as direct datacontext. In the previous versions, you must set DataContext directly on the UserControl, but that is not in line with default xaml behavior.

It's maybe that the view model is being injected but the logic is not aware about it. Somehow the DC changes, and I need to know both values before I can give more info.
Jun 18, 2014 at 1:44 PM
e.OldValue is null e.NewValue is PlaceInstance

"In the previous versions, you must set DataContext directly on the UserControl, but that is not in line with default xaml behavior."

I have only set the designcontext direct in the usercontrol. Also in previous versions:
<catel:UserControl x:Class="ConfiguratorControl.Views.PlaceDetailView"
                           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                           xmlns:catel="http://catel.codeplex.com"
                          d:DataContext="{d:DesignInstance viewModels:PlaceViewModel}">
...
or what do you mean?
Jun 18, 2014 at 1:45 PM
No, the view using the control had to set the DC directly to allow dependency injection of the model.
Jun 18, 2014 at 1:52 PM
Sorry but I don't undestand.

You said I have to set the DataContext directly in view which use the control in pervious version:

Like in this way
<views:PlaceGeneralView DataContext={Binding ...}/>
But I do this also not before like you see in my code above.
Jun 18, 2014 at 1:54 PM
You didn't set the DataContext directly in previous versions?

So you were using this:
<!-- this is now possible -->
<views:PlaceGeneralView />
instead of
<!-- this was required -->
<views:PlaceGeneralView DataContext={Binding ...}/>
Jun 18, 2014 at 1:57 PM
Yes for sure. It works perfect.

But maybe it was a little bit different because it was defined in a <DataTemplate> ?
Jun 18, 2014 at 1:58 PM
Yes, that might be the cause. I changed it because it didn't work on inherited datacontext.
Jun 18, 2014 at 1:59 PM
But know it did not works any more on my side :-(
Jun 18, 2014 at 2:04 PM
Can you please create a simple repro with 4 vm's with models (use for example the furniture example) so we can reproduce?
Jun 23, 2014 at 1:52 PM