简体   繁体   中英

Reusing views for multiple view models

I'd like to reuse a view for 2 different viewmodels, in my example MyEntityEditViewModel and MyEntityCreateViewModel . The view is basically just a form with a Save button, so pretty common layout.

I created both view models along with a parent view / view model ( MyEntitySummaryViewModel ) and now I'd like to define the form view using a ContentControl .

Summary view :

<ContentControl x:Name="ActiveItem" cal:View.Model="{Binding ActiveItem}" cal:View.Context="MyEntityDetailView" />

MyEntitySummaryViewModel :

public MyEntity SelectedEntity {
    get { return _selectedEntity; }
    set {
        _selectedEntity = value;
        NotifyOfPropertyChange();

        ActivateItem(new MyEntityEditViewModel(_selectedEntitity));
    }
}

public void Create() {
    ActivateItem(new MyEntityCreateViewModel(new MyEntity()));
}

My problem is now that Caliburn tries to locate a ' MyEntityEditView ' due to it's view locating conventions, even if I strictly defined the context of the ContentControl as a custom view. Is there a way around this? Or am I doing something completely wrong here?

If my understanding is right, You want 2 type of ViewModel to point on the same view. If so juste create a base classe for your Entity (EntityBaseViewModel) and Create a View (EntityBaseView).

To Bind a ContentControl set his x:Name so the name match a Property of your ViewModel.

Example:

View (ShellView):

<ContentControl x:Name="SelectedEntity"/>

ViewModel (ShellViewModel):

public EntityBaseViewModel  SelectedEntity 
{
    get 
    { 
        return this._selectedEntity; 
    }
    set 
    {
        this._selectedEntity = value;
        this.NotifyOfPropertyChange(() => SelectedEntity);
    }
}

And Caliburn will find the View for the ViewModel and bind the DataContext if you did create your ViewModel / View along the naming convention like you said.

A little late to the party, but perhaps this will help someone. This video helped a lot for me - (Tim Corey, WPF and Caliburn with MVVM)

Setting up the ShellView with a control that points to ActiveItem as you mentioned, allows that control to display whatever view you tell it from the ShellViewModel code. I was also using Fody with this project, so that took care of the change notifications so you won't see those listed in code.

ShellView -

<Button x:Name="LoadMainPage" />    
<Button x:Name="LoadSecondPage" />
<ContentControl x:Name="ActiveItem"/>

The ShellViewModel -

public class ShellViewModel : Conductor<object>.Collection.OneActive
{       
        public MainPageViewModel MainPageVM = new MainPageViewModel();
        public SecondPageViewModel SecondPageVM = new SecondPageViewModel();

        public ShellViewModel()
        {           
            LoadMainPage(); // auto load main page on startup
        }

        public void LoadMainPage()
        {
            ActivateItem(MainPageVM);
        }

        public void LoadSecondPage()
        {
            ActivateItem(SecondPageVM);
        }
}

Instead of creating a new instance of a ViewModel when using ActivateItem, you're just re-using the initial ones created. Or, if you DO prefer to create another instance each time that particular view is launched, then simply use the ActivateItem as you already have.

In your SecondPageViewModel for the view, which will occupy the space in the ContentControl for ActiveItem -

public class SecondPageViewModel : Screen

SecondPageView.xaml added as a User Control (and any other sub/child views you want to create) -

<UserControl x:Class="MyNamespace.Views.SecondPageView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:MyNamespace.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>

    </Grid>
</UserControl>

This will allow you to flip back and forth between whatever views you want from a base view/viewmodel, and display the contents of the child views (however many you want) that you choose within the ContentControl box.

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