I use Caliburn.Micro. Well, to tell the truth, here is the whole issue I have faced:
I have set up the binding at the design time. See the code below:
<Window x:Class="Microtech.TPM.Views.DestinationChoiceView"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
xmlns:vmns="clr-namespace:Microtech.TPM.ViewModels"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vmns:DestinationChoiceViewModel, IsDesignTimeCreatable=True}"
cal:Bind.AtDesignTime="True" Width="1280" Height="1024">
<Window.Resources>
<vmns:DestinationChoiceViewModel x:Key="ViewModelKey" />
</Window.Resources>
I need to subscribe for the events of the ViewModel. How to accomplish this if I have defined the reference to the ViewModel in Window.Resources and use it for the binding further in xaml? I quite don't understand how to use the same references. Besides, I quite don't understand how many ViewModel instances I will already have with this code. As I understand, I'll have at least 2 instances, am I right? So, how to avoid this? And this is the part of the question to.
If you are performing ViewModel-First binding you should have already used the Bootstrapper
class to create the root viewmodel for your application hierarchy.
In this case, you either need to bind to a property which contains a ViewModel
which is located on your root VM, or you need to make the VM a Conductor
and Activate
one or more items within it.
Example - binding other VMs as properties
Your VM:
public class RootViewModel : PropertyChangedBase
{
private SomeOtherViewModel _someOtherView;
public SomeOtherViewModel SomeOtherView
{
get { return _someOtherView; }
set
{
if(_someOtherView != value)
{
_someOtherView = value;
NotifyOfPropertyChange(() => SomeOtherView);
}
}
}
}
And the associated View:
<Window x:Class="SomeProject.SomeView">
<... some view related code - buttons etc ...>
<!-- Render the other ViewModel using CMs conventions -->
<ContentControl x:Name="SomeOtherView">
</Window>
Example - using a conductor
The VM:
public class RootViewModel : Conductor<IScreen>
{
public RootViewModel(SomeViewModel someViewModel)
{
ActivateItem(someViewModel);
}
}
And the associated View:
<Window x:Class="SomeProject.SomeView">
<... some view related code - buttons etc ...>
<!-- Render the other ViewModel using CMs conventions -->
<ContentControl x:Name="ActiveItem">
</Window>
In addition to the above, my advice is not to declare a viewmodel as a resource as you are creating a dependency on that VM within your view. You are also scattering the concern of reference locating and handling and spreading it amongst your views, which can lead to maintenance headaches and spaghetti code.
Remember that you want to keep class complexity down to a minimum so your classes should follow Single Responsibility Principle - i.e. they should be concerned with one area of functionality and should not be responsible for jobs outside of that single scope. This is why we have IoC containers, they are there to manage your references (as a component that's their single responsibility!)
The IoC container SimpleContainer
that comes with Caliburn Micro is fine for most projects; adding one of the popular IoC containers such as Castle Windsor/Ninject etc is easy and there are plenty of tutorials to get this running
This way you can specify your required dependencies in your VM but be agnostic to the resolution and control of these dependencies
In order to send events and messages between your VMs there are a couple of mechanisms CM has:
e.g.
<Window x:Class="SomeProject.SomeView">
<Button cal:Message.Attach="[Event Click] = [Action ButtonClicked()]">click me</Button>
</Window>`
(this will call ButtonClicked() method on your ViewModel)
IHandle<T>
where T
is the message type you are interested inI will point out that at no point does the view or VM reference each other - though it's possible, it should be avoided unless really necessary. The viewmodel should be blissfully unaware of the view, with no coupling between the objects. Caliburn Micro is there to glue the VM to the view, and it does a good job of doing so.
The more decoupled your objects are the easier it is to make (inevitable) changes, refactorings and additions.
If you are struggling with using CM I'd suggest running through all the tutorials on the CodePlex site