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:
- Using action messages - you attach an action message to your view to handle events, and this will call methods on the attached viewmodel
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)
- Using the event aggregator - you subscribe to the aggregator and listen for messages of a certain type by implementing
IHandle<T>
whereT
is the message type you are interested in
I 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