Sven Bardos
Sven Bardos

Reputation: 872

Caliburn Micro self-replacing View/ViewModel

I have a listbox to select an item for edit. I have an edit button as well. Call this the MainView[Model].

If I press the edit button the MainView[Model] shall be replaced by EditView[Model]. The EditView shall not be displayed in a area below or beside the MainView. It should be completely replaced or at least completely hide the MainView.

If edit is finished (OK, cancel) the MainView shall be displayed again.

I have tried to overlay a ContentControl but with no success. Now, I'm thinking about a kind of NavigatorViewModel which has multiple ViewModels exposed by a property. But I'm not sure if this is the right direction to go.

Can anybody help?

Thx.

Upvotes: 0

Views: 667

Answers (1)

Frank
Frank

Reputation: 4481

You would preferably use the Conductor pattern that Caliburn.Micro provides. A conductor manages one or more Screens and controls their lifetime. See Screens, Conductors and Composition for further information.

  1. At first, we need a shell. This is your "NavigatorViewModel". It is derived from Conductor<Screen>.Collection.OneActive, what means that it holds a list of Screens of which a single one can be active at a time:

    public interface IShell
    {
        void ActivateItem(Screen screen);
    }
    
    public class ShellViewModel : Conductor<Screen>.Collection.OneActive, IShell
    {
        public ShellViewModel()
        {
            this.ActivateItem(new MainViewModel());
        }
    }
    
  2. A conductor has an ActiveItem property, and we want to bind a ContentControl to it, so we see the corresponding view:

    <!-- ShellView.xaml -->
    <Window x:Class="WpfApplication1.ShellView"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
        <ContentControl Name="ActiveItem" />
    
    </Window>
    
  3. Our MainViewModel can navigate to the EditViewModel using its parent, the shell:

    public class MainViewModel : Screen
    {
        public void Edit()
        {
            ((IShell)this.Parent).ActivateItem(new EditViewModel());
        }
    }
    
  4. We bind a button to the Edit method:

    <!-- MainView.xaml -->
    <UserControl x:Class="WpfApplication1.MainView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
        <Button Name="Edit" Content="Edit" />
    
    </UserControl> 
    
  5. EditViewModel also derives from Screen and just contains your edit logic:

    public class EditViewModel : Screen
    {
    }
    
  6. Finally, we bind a button to the TryClose method, so the view model closes itself and is removed from the shell's items. The last activated item (MainViewModel) will be reactivated:

    <!-- EditView.xaml -->
    <UserControl x:Class="WpfApplication1.EditView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
        <Button Name="TryClose" Content="Back" />
    
    </UserControl>
    

That's about it.

Upvotes: 2

Related Questions