3
votes

On my first project trying out Caliburn.Micro, I like a lot of the things :-)

One thing I miss (or havn't discovered yet) is how to separate the viewmodel and a command. CM doesn't support ICommand, as it's way of doing things is superior. I'm sure it's true, so I would love a small push in the right direction to achieve this or perhaps discover a better way.

As I understand you have to put the "Execute" method and "CanExecute" property directly in the viewmodel, named to match the control in the view, to get the magic to work.

I would like to put the "Execute" and "CanExecute" in a different object that is property on the viewmodel and then CM would automatically bind to that object, using the control name and property names as usually.

Repost from the forum on Caliburn Micro, I didn't get any answers so I'm trying my luck here.

2
Can you please ask more clearly, what is it that you are confused with or you need more info about ? I don't understand your question fully, especially the last part on putting "Execute" and "CanExecute" in different object ?Ibrahim Najjar
Sounds like you want to create a command handler type of object which will be used as the binding source for your commands. Is there any reason you want to do this over just using CM conventions and methods/props directly on the VM? Can you give some advantages of this?Charleh
My reason for wanting to do this is to avoid fat ViewModels.Karsten

2 Answers

2
votes

To avoid fat ViewModels you also need to avoid fat Views. Caliburn.Micro allows you to compose Views/ViewModels as described in Screens, Conductors and Composition.

The short version is, you can include a "DetailView" and "DetailViewModel" pair in a "MasterView"/"MasterViewModel" shell by defining a DetailViewModel-typed property in MasterViewModel and adding a ContentControl named after it in MasterView. Binding and actions work as usual, so you avoid both fat models/views and routing of commands.

Another option is to bind a MasterView element to a DetailViewModel property or action, by prepending the detail's property to the target's name. I can't find the specific URL yet, so the example is from memory.

Assuming you have the following classes:

public class MasterViewModel:Screen
{
    public property DetailViewModel MyDetails{get;set;}
}

and

public class DetailViewModel:Screen
{
    public property string SomeText{get;set;}

    public void DoTheBoogie(){}
}

You can add a control in you MasterView named 'MyDetails_SomeText' to bind to the DetailViewModel.SomeText. You can also bind to DoTheBoogie the same way.

I prefer to create a separate View though, named DetailView and add a ContentControl named "MyDetails" in MasterView. This results in a cleaner and more modular design

3
votes

You should try to avoid invalidating the Law of Demeter. Therefore, on your view model you can have an execute method, and a CanExecute property (usually calculated), and these can call into the containing model where appropriate, e.g:

public void Save
{
   // .. save logic
}

public bool CanSave
{
    get
    {
        return this.model.CanSave ... and other logic etc.
    }
}

You must remember to notify a change in the calculated property when the can save state changes, e.g:

public void CodeThatGetsRunWhenAPropertyOfTheModelChanges()
{
    this.NotifyOfPropertyChanged(() => this.CanSave);
} 

If you have e.g. a Button on your view with x:Name="Save", then Caliburn.Micro will automatically invoke your Save verb on the view model when the button is clicked, and will automatically enable and disable the button when the CanSave property value changes.