Search code examples
.netwpfmvvmdata-bindingcaliburn.micro

Calling another ViewModel using Caliburn.micro in .NET WPF


I have a ShellView and ShellViewModel. Within that ShellView I have a page call FirstPage which I have embedded as a frame and opening that on Startup is no problem. So far this is what I've done.

ShellView.xaml.cs

public partial class ShellView : Window
    {
        public ShellView()
        {
            InitializeComponent();
            FirstPage.Content = new FirstPage();
        }
    }

ShellView.xaml

<Window x:Class="CaliburnMicroDemo.Views.ShellView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CaliburnMicroDemo.Views"
        mc:Ignorable="d"
        Title="ShellWindow" Height="450" Width="800">
    <Grid>            
        <Frame x:Name="FirstPage" Source="FirstPage.xaml" NavigationUIVisibility="Hidden"/>
    </Grid>
</Window>

FirstPage.xaml.cs

public partial class FirstPage : Page
    {
        public FirstPage()
        {
            InitializeComponent();           
            this.DataContext = new FirstPageViewModel();
        }        
    }

FirstPage.xaml

<Page x:Class="CaliburnMicroDemo.Views.FirstPage"
      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:CaliburnMicroDemo.Views"
      mc:Ignorable="d" 
      d:DesignHeight="450" d:DesignWidth="800"
      Title="FirstPage">

    <Grid>            
        <Button x:Name="ChangeMessage" Content="Press Me" VerticalAlignment="Top" />
        <TextBlock x:Name="Message" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding Path=Message, Mode=OneWay}"/>
    </Grid>
</Page>

ShellViewModel.cs

 public class ShellViewModel : PropertyChangedBase
 {
 }

FirstPageViewModel.cs

public class FirstPageViewModel: PropertyChangedBase
{       
    private string message;

    public string Message
    {
        get
        {
            return message;
        }
        set
        {
            message = value;
            NotifyOfPropertyChange(() => Message);
        }
    }

    private int _pressCount;        

    public FirstPageViewModel()
    {
        Message = "Yolo";
        _pressCount = 0;
    }

    public void ChangeMessage()
    {
        _pressCount++;
        Message = "Presses = " + _pressCount;
    }        
}

Now the contents here is displayed without any issue. But when I click on the button eventhough I'm mapping them through NotifyOfPropertyChange at the setters in FirstPageViewModel.cs it's not working. For the one which uses two different windows the answer is here. But for the one which uses page not sure how to do it. Once again my question here is that how to map the ViewModel and View and bind the data and change the property. Here in this situation to change it on button click. The above logic works fine if I have only ShellView and ShellViewModel. And I'm starting this through the Bootstrapper class, the standard way of doing it using Caliburn.micro. Would appreciate if someone could help me out with this. Required anymore details please leave a comment would update accordingly.


Solution

  • The main idea is you should let Caliburn create instances of views. Please look at ActiveItem and ActivateItemAsync usages. Also you don't need FirstPage.xaml.cs and ShellView.xaml.cs files.

    If you want to use built in IoC and want to add messaging between view models you can check out my sample project.

    FirstPage.xaml:

    <Page x:Class="CaliburnMicroDemo.Views.FirstPage"
      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" 
      mc:Ignorable="d" 
      d:DesignHeight="450" d:DesignWidth="800"
      Title="FirstPage">
    <Grid>
        <Button x:Name="ChangeMessage" Content="Press Me" VerticalAlignment="Top" />
        <TextBlock x:Name="Message" VerticalAlignment="Center" HorizontalAlignment="Center"/>
    </Grid>
    

    ShellView.xaml:

    <Window x:Class="CaliburnMicroDemo.Views.ShellView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="ShellWindow" Height="450" Width="800">
    <Grid>
        <Frame x:Name="ActiveItem" NavigationUIVisibility="Hidden"/>
    </Grid>
    

    FirstPageViewModel:

    using Caliburn.Micro;
    
    namespace CaliburnMicroDemo.ViewModels;
    
    public class FirstPageViewModel : PropertyChangedBase
    {
        private string _message = "Yolo";
    
        public string Message
        {
            get => _message;
            set
            {
                _message = value;
                NotifyOfPropertyChange(() => Message);
            }
        }
    
        private int _pressCount = 0;
    
        public void ChangeMessage()
        {
            _pressCount++;
            Message = $"Presses = {_pressCount}";
        }
    }
    

    ShellViewModel:

    using Caliburn.Micro;
    
    namespace CaliburnMicroDemo.ViewModels;
    
    public class ShellViewModel : Conductor<object>
    {
        public ShellViewModel()
        {
            ShowFirstPage();
        }
    
        public void ShowFirstPage()
        {
            _ = ActivateItemAsync(new FirstPageViewModel());
        }
    }
    

    App.xaml:

    <Application x:Class="CaliburnMicroDemo.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:CaliburnMicroDemo">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary>
                    <local:Bootstrapper x:Key="bootstrapper" />
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
    

    Bootstrapper.cs:

    using Caliburn.Micro;
    using CaliburnMicroDemo.ViewModels;
    using System.Windows;
    
    namespace CaliburnMicroDemo;
    
    public class Bootstrapper : BootstrapperBase
    {
        public Bootstrapper()
        {
            Initialize();
        }
    
        protected override void OnStartup(object sender, StartupEventArgs e)
        {
            _ = DisplayRootViewForAsync<ShellViewModel>();
        }
    }