In my CMContrib Project I use a MVVM approach for showing dialogs to the user. The model for the dialog has a dialog type (Question, Error,…), a subject (or title), a message and a list of possible responses the user can choose from. The default response in CMContrib is an Answer enum with values for Ok, Cancel, Yes and all the other standard answers but you can also use a complex type as a response.
Here’s an example of how to ask the user a question and cancel the coroutine when the user responses with No.
1 2 3 4 5 6 7 8 9 10 11 12
One View to show them all (again)
.AsResult() on a dialog wraps it in a
DialogViewModel which is then
passed to Caliburn.Micro’s
IWindowManager and shown as a modular dialog. The problem
with that approach is, that the same default view, which is
Caliburn.Micro.Contrib.Dialogs.DialogView unless you added a namespace alias, is resolved for all types of dialogs. Now, if you need a special view for, let’s say errors only, you are in trouble.
But fear not, because Caliburn already has a solution to that problem, namely view contexts, which are explained here. Since each dialog already has a dialog type we can use that as our view context. The change to show the context based view instead of the default view couldn’t be easier, it’s just one line in the
1 2 3 4 5 6 7 8 9 10 11
Now the default view for an Error is
Okay cool, but what happens if we want to show a Question? Well, we get an
error because there is looks for the view
Caliburn.Micro.Contrib.Dialogs.Question which doesn’t exist and which we don’t
want to create. Instead, we want to use the default
DialogView as a fallback view.
Changing the ViewLocator
Since we want to change the way how views are located, the
be a good class to look at. The function responsible for locating the view type for
a view model type is called
LocateTypeForModelType. In a nutshell, this function takes the
type of the view model and the view context, transforms those into a list of
possible view type names and searches for a type in the assemblies that matches
one of the names. If none is found,
null will be returned and Caliburn.Micro
shows the “Could not locate view for …” error view that you might have seen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Now, we basically have two options. Either we replace the function with one that
tries to locate the view without a context when the default function returns
null or we replace the
TransformName function to also return the type names
without a view context.
I opted for the second options because it is easier to implement and other
function that use
TransformName benefit from that change, too.
1 2 3 4 5 6 7 8 9 10 11 12
The implementation is pretty straight-forward. Since the
ViewLocator returns the first view type found, we simply append the name(s) of the fallback view(s) to the list of names given by: the default
Even more customization !
If you need a different view for Errors and Questions, you might also need
different views for different Questions! Let’s say we have views named
My.Namespace.BarQuestion which we want to use for different
kinds of Questions. We add a
ContextPrefix to the
DialogResult and create
the view context by concatenating the
DialogType. Adding a
fluent configuration for the prefix to the
DialogResult gives us this nice
syntax to for showing the
1 2 3 4 5 6 7 8 9 10
I: Note on Namespace aliases
In case you don’t want to put your custom dialog views in the
Caliburn.Micro.Contrib.Dialogs namespace, just add a namespace alias the
II: Online Silverlight Demo
Although I added an example for this feature to the Silverlight demo, it currently crashes each browser when embedded in a page. Out-of-browser works though. If you know why, tell me ! Will update the demo once it works.