I'm following this tutorial on Screen
and ScreenConductors
in Caliburn.Micro
Framework.
I'm using WPF, not Silverlight, and I have made the corresponding changes in App.xaml (using MergedDictionary for the Bootstrapper).
The original Simple Navigation example has a shell with two buttons, and a content area where two possible screens are displayed, conducted by the ShellViewModel
.
Then I tried to move each button to its counterpart View, so that PageOne would have a button to take to PageTwo, and vice-versa. I did it because I don't want the home shell continuously "showing its entrails" across the application.
The fact is, if I just move a button to a Screen
View, it no longer binds to the command, which is in the ShellViewModel
, not in the Screen
ViewModel itself. I know these bindings happen by convention, but I don't know if the convention covers this case, or I'd need to configure.
The symptom I am facing is: When I run the app, PageOneView shows, with the "Go to Page Two" button in it, but when I click the button nothing happens.
The question I ask is: "How is the proper way to "bubble" a button in a ScreenView.xaml to an action in the ScreenConductorViewModel.cs?
My current code is below:
PageOneView.xaml
<UserControl x:Class="ScreenConductor.PageOneView"
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="300" d:DesignWidth="300">
<Grid Background="LightGreen">
<Button x:Name="ShowPageTwo" Content="Show Page Two" HorizontalAlignment="Center" VerticalAlignment="Top" />
</Grid>
</UserControl>
PageOneViewModel
using Caliburn.Micro;
namespace ScreenConductor {
public class PageOneViewModel : Screen {
protected override void OnActivate() {
base.OnActivate();
}
}
}
ShellView.xaml
<Window x:Class="ScreenConductor.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<ContentControl x:Name="ActiveItem" />
</Window>
ShellViewModel.cs
using Caliburn.Micro;
namespace ScreenConductor
{
public class ShellViewModel : Conductor<object>
{
public ShellViewModel()
{
ShowPageOne();
}
public void ShowPageOne()
{
ActivateItem(new PageOneViewModel());
}
public void ShowPageTwo()
{
ActivateItem(new PageTwoViewModel());
}
}
}
As long as you bind the command using the explicit action binding syntax the bubbling should work fine. This is because to bind by name CM examines controls on the view bound to the current vm. If there is no matching named control a binding does not get created.
You can use this:
This will attempt to bubble the message up the control hierarchy until a suitable handler is found. Note that this will throw an exception if no handler could be found. I recall there being an option to turn this off on the binding but you might want to check the docs (I'm contributing from my mobile at the moment :-)
Edit:
Ok if you want more info for behind the scenes, the best place to check really is the source. The author Rob Eisenberg mentions that the source is quite small (something like 2700 lines of code I think) so it's easy to inspect and easy to hold in your head
It's worth reading through all the documentation on the CodePlex site (there's a fair few pages but they explain everything in enough detail for you to piece together the rest).
Not sure how much you know about the
Message
class that contains theAttach
attached property but this is what kicks off the action binding when the standard controlName
conventions aren't used (I assume you know about attached properties in WPF/SL). You can see the standard named conventions in theViewModelBinder
classThe
Message
class looks like:http://caliburnmicro.codeplex.com/SourceControl/changeset/view/35582bb2a8dfdd3fcd71a07fa82581ddb93a786f#src/Caliburn.Micro.Silverlight/Message.cs
(yes it's the Silverlight source but this is the base for all other versions and it's just a few compiler directives and some additional classes that appear in other version such as WPF/WinRT)
If you look at the source you can see that when the attached property is set, the
Parser
class kicks off to parse the string. The parser actually parses several different formats so you can attach to different events and methods, and also pass properties e.g.or
Above you can see that the
$eventargs
special value was used. There are several out-of-box special values, and you can also write your own (check out this SO question Using MessageBinder.SpecialValues in Windows 8 app not working? where a user was usingSpecialValues
to pass the horizontal mouse position from controls for use in a synthesizer app)You can also pass the CM default property of other controls e.g.
Where the
Text
value ofTextBox1
will be passed to theMouseClicked
method on the VM. This is specified in the default convention bindings forTextBox
(look atConventionManager.AddElementConvention
and the docs on that)The bubbling works by inspecting the visual tree and attempting to bind to each level (happens in
SetMethodBinding
inActionMessage
class)It's pretty simple but effective (it just uses
VisualTreeHelper
to walk up the visual tree until a suitable handler is found)Not sure what else you might need info on :P
If you have not yet referenced
System.Windows.Interactivity
(goes along with the Blend SDK) I'd really recommend it. I suppose a Caliburn.Micro project runs without it though I have never tried it. It's referenced per default as you install Caliburn.Micro via NuGet.Your problem can be solved properly with using
System.Windows.Interactivity.Interaction
along withCaliburn.Micro.ActionMessage
like this:You can find more about actions here, the official documentation at CodePlex.
Action messages are going to bubble to the conductor and if no suitable method can be found an
Exception
is thrown.