简体   繁体   中英

Using single View for multiple ViewModels with Caliburn.Micro on Windows Phone 8.1

I have a Windows Phone 8.1 app using Caliburn.Micro. In the app I have a few ViewModels that fetch data in different way and with different logic but show them in the same way. So I want to make all those ViewModel use the same one View.

I found out that ViewLocator.LocateTypeForModelType is a method that gets executed for mapping ViewModels to Views. So I override it to use my custom attribute when present:

        var original = ViewLocator.LocateTypeForModelType;

        ViewLocator.LocateTypeForModelType = (modelType, displayLocation, context) =>
        {
            var useViewAttributes = modelType.GetTypeInfo().GetCustomAttributes<UseViewAttribute>(true);

            if (useViewAttributes.Count() == 1)
            {
                var viewTypeName = string.Concat(modelType.Namespace.Replace("Model", string.Empty), ".", useViewAttributes.First().ViewName);
                var type = AssemblySource.FindTypeByNames(new List<string>() { viewTypeName });
                return type;
            }

            return original(modelType, displayLocation, context);
        };

Stepping through the it seems to work fine. If I navigate to a ViewModel and that ViewModel has a UseView, my method returs the correct View.

The app navigates to the correct View but the ViewModel is never created. Kind of like Caliburn.Micro forgot about the ViewModel, or was looking for one using a different convention, or something.

I found out that ViewModelLocator.LocateTypeForViewType is called after navigation to a View to resolve the ViewModel. The ViewModel type from the previous step seems to be forgotten completely.

In ViewModelLocator.LocateTypeForViewType I only have access to the View type and I do not know, how to make it resolve the correct ViewModel from the previous step. I could scan all the ViewModel and find the ones with the correct attribute, but I would not know which one to choose.

Any ideas on how to approach this?

Here is a minimal project showing my setup: https://dl.dropboxusercontent.com/u/73642/CMVMTest.zip

This sort of solution would work everywhere else except for the top level navigation. The reason for this is there is sort of a "double dispatch: going on when you navigate.

As you know the Frame or PhoneNavigationFrame control (depending on WinRT or Silverlight) is view based in it's navigation. So the steps look a little like this.

  1. Your code tells the navigation servie=ce to navigate to ProductViewModel .
  2. It uses ViewLocator (where you've injected your code) to locate ProductView and tells the Frame to navigate to that.
  3. The navigation service then responds to the navigating event to ProductView and locates the correct view model using ViewModelLocator .
  4. It then instantiates and binds this view model.

This sort of view model to view to view model step in navigation service causes the hiccup in your code.

You should be able to create dummy views that simply inherit the base view and add nothing. So if you have MySharedView.xaml then declaring what's below should be enough.

public class SecondView : MySharedView { }

It's not ideal I know, but does get you the reuse you're after. Having the navigation service remember the view model between the navigating and navigated events becomes complicated with all the external factors that can cause navigation as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM