Command class in Caliburn Micro - mvvm

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.

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.

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

Related

Xamarin + Prism: bubble property change events from hierarchical model / view model or ...?

I've been trying to find the correct approach for this problem I got, using Prism with Xamarin Forms:
I've a model class, Customer, that contains another class, Address as a property. In my view, I show fields from both objects. I would like to have a "save" button, that only gets enabled after you've made some changes to those models.
Now, the button is bound to a Command, with the corresponding CanSave() function, as is normal with DelegateCommands. I'm trying to find an approach where I can end up with a single IsDirty property on my view model, that gets to "true" after any changed to the underlying models.
The MVVM approach
First thing I thought was the "purist" mvvm approach. A "flat" view model, with properties for each visual element, implemented as a Prism BindableObject, where each getter/setter gets/sets values from/to the underlying model classes.
That failed though, since SetProperty<> has a ref parameter, where I can't use properties from my models.
The over-engineered approach [?]
Second thing I thought was that, if my inner models were observables themselves, I could listen for changes from all of them, throughout the tree. Which opens up a whole new world of issues. Do I register property change listeners in my View model ? Do I make inner models observables, and have the parents listen for change events on their children and propagate that ?
Won't that observable models approach quickly become event handler hell ?
The simplest thing
And last, the simplest thing possible. I have a flat observable ViewModel, that only reads/writes values to/from the actual inner hierarchical model upon read & save
What do you guys think ?
Maybe I didn't understand your question right, but I'm wondering why you limit yourself to such a small helper function like SetProperty. It has 4 Lines of code. All it does is checking for equality, setting a value and raising an event.
You could easily create another helper function like this.
MyBindableBase
protected virtual bool SetProperty<T>(Func<T> get, Action<T> set, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(get(), value)) return false;
set(value);
OnPropertyChanged(propertyName);
return true;
}
Model
class Model
{
public string Property { get; set; }
}
ViewModel
class ViewModel : BindableBase
{
private Model Model { get; set; }
public string Property
{
get { return Model.Property; }
set { SetProperty(() => Model.Property, x => Model.Property = x, value); }
}
}
I think you can shorten the usage, if you introduce some naming rules for the mapping and/or use reflections.
Well, in the end I went for option 3, the simplest thing I could do.
I was leaning towards returning properties from my model, which would be easy, and using the nullable [?.] syntax it would be null-safe too, but I found that at times I'll have to wrap the actual model properties with something that is more UI-friendly, exposing more/different properties than my actual DB model classes.
So, I went for that, until some other complexity forces me to change my mind again :)
Thanks a lot #Sven-Michael Stübe and #adminSoftDK for the help

Trying to understand list selection in MvvM pattern with ICommands (no specific framework)

First note that I am not referring to any specific framework or technology like XAML.
The question is how to implement the MvvM pattern using ICommand for selection of an item in a list (=clicking a row)?
I have a view model (pseudo code):
class ListViewModel
{
// Items in the list.
public ObservableCollection<T> Items {};
// Command for item selection.
public ICommand ItemSelectedCommand
{
...
}
// Select an item in the list.
public void SelectItem(int index)
{
...
}
// The current selected item.
public T SelectedItem
{
get { ... };
}
}
How would I now connect my UI to that view model "manually"? Say, for instance in an iOS application.
I would probably have a UITableViewController, get an instance of the view model and populate the UITableView contents from it. Then I would trigger the ICommand from the RowSelected() method.
And here comes the thing I don't understand: how does the view model now know which item index was selected? I don't want to call SelectItem() because then I would not need the loosely coupled ICommand at all.
And maybe here we have to look how it is solved in XAML to understand the trick?
Coming from XAML and WPF, there are two options to forward selection changes from the UI to the ViewModel (as I understand your question, you're not asking about the other way around - feedbacking changes in the ViewModel to the UI - here):
Command with payload
The ICommands Execute method has a payload parameter. Executing a command without a payload can be done passing null:
SomeCommand.Execute(null);
In your case, it would make sense to pass the selected item as the parameter in the event handler:
vm.ItemSelectedCommand.Execute(eventArgs.SelectedItem);
or
vm.ItemSelectedCommand.Execute(myList.SelectedItem);
In the command's execution method, you can handle the parameter. Note that your ViewModel property SelectedItem is not directly involved here. If you need the selected index explicitly (which is not the case, usually), I would check the selected item's index in the Items collection.
Binding selected item of list to a ViewModel property
Option B is to 'bind' the selected item of the list to a distinct property on the ViewModel, in your case the SelectedItem property in the event handler of the list:
vm.SelectedItem = myList.SelectedItem;
The command is kind of redundant then, although you could invoke it without a payload after setting SelectedItem on the ViewModel. I would rather handle the change of the selected item in the set accessor of the property on the ViewModel.
Note: XAML and WPF come with quite a lot of infrastructure code out of the box. MVVM doesn't make sense without a proper framework to actually take care of binding UI and ViewModels in a loosely coupled way. You quickly end up with a lot of extra work and little benefit, because you're still maintaining tight dependencies. Bottom line: I recommend getting or writing a proper MVVM framework, before actually implementing it.

MVVM access other view's element from viewModel

I started working with the MVVM pattern in a new project.
Everything is ok, but i came to the following problem.
The implementation looks like this:
I have a MainView, the main app window. In this window i have a telerik RadGroupPanel in wich I host the rest of the app views as tabs.
The rest of the viewModels does not know about this RadGroupPanel which is hosted in MainVIew.
How should i correctly add those views to the RadGroupPanel from the commands in the viewModels?
Thanks.
Have you considered injecting your view into the ViewModel using an interface to maintain separation? I know this breaks MVVM but I've successfully used this on a number of WPF projects. I call it MiVVM or Model Interface-to-View ViewModel.
The pattern is simple. Your Usercontrol should have an interface, call it IView. Then in the ViewModel you have a property with a setter of type IMyView, say
public IMyView InjectedView { set { _injectedView = value; } }
Then in the view you create a dependency property called This
public MyUserControl : IMyView
{
public static readonly DependencyProperty ThisProperty =
DependencyProperty.Register("This", typeof(IMyView), typeof(MyUserControl));
public MyUserControl()
{
SetValue(ThisProperty, this);
}
public IMyView This { get { return GetValue(ThisProperty); } set { /* do nothing */ } }
}
finally in Xaml you can inject the view directly into the ViewModel using binding
<MyUserControl This="{Binding InjectedView, Mode=OneWayToSource}"/>
Try it out! I've used this pattern many times and you get an interface to the view injected once on startup. This means you maintain separation (Viewmodel can be tested as IView can be mocked), yet you get around the lack of binding support in many third party controls. Plus, its fast. Did you know binding uses reflection?
There's a demo project showcasing this pattern on the blog link above. I'd advocate trying out the Attached Property implementation of MiVVM if you are using a third party control.
You can have the list of viewmodels that you need to add controls for in an ObservableCollection in your main window viewmodel. You can then bind the ItemsSource of the RadGroupPanel to that collection and use the ItemTemplateSelector and ContentTemplateSelector of the RadGroupPanel to select the right template to use based on the viewmodel that is bound.

How do you create a FubuMVC behavior that copies site configuration values to an output model?

I'm trying to figure out to create a behavior that will copy a boolean site configuration value to an output model.
This way I don't have to copy the bool in each action who's view requires it, but can simply add the behavior to the controller actions that need this value.
In some of the older versions of FubuMVC, I believe behaviors could modify the output model after it's left the controller. But I'm not sure how to do this in the more recent versions of FubuMVC (or I've forgotten).
Can anyone give me an example of or point me in the direction of the best practice for copying a site configuration value to an output model?
Let's say I had an output model called HomeViewModel that had a property called FooterText that I wanted loaded from settings object (let's say HomeSettings) that was retrieved from the container (i.e. StructureMap).
The Behavior
My behavior would look something like this:
public class HomeFooterBehavior : BasicBehavior
{
private readonly HomeSettings _settings;
private readonly IFubuRequest _request;
public HomeFooterBehavior(HomeSettings settings, IFubuRequest request)
: base(PartialBehavior.Executes)
{
_settings = settings;
_request = request;
}
protected override DoNext performInvoke()
{
SetupFooter();
return DoNext.Continue;
}
public void SetupFooter()
{
var viewModel = _request.Find<HomeViewModel>().First();
viewModel.HomeFooterText = _settings.FooterText;
}
}
This behavior takes in the HomeSettings object and the IFubuRequest object (both injected dependencies) and then gets the HomeViewModel (output model) from the request and then sets the HomeFooterText property on the output model based on the value from the settings object.
NOTE: I'm assuming that you've already got your HomeSettings object wired up in the container (for example, using the ISettingsProvider stuff built into FubuMVC). If you don't already have this, let me know and I can post some code on how to do that.
Wiring Up The Convention
To wire up the behavior, you'll need to define the convention through an IConfigurationAction, for example:
public class HomeFooterBehaviorConfiguration : IConfigurationAction
{
public void Configure(BehaviorGraph graph)
{
graph.Actions()
.Where(x => x.HasOutput &&
x.OutputType().Equals(typeof(HomeViewModel)))
.Each(x => x.AddAfter(Wrapper.For<HomeFooterBehavior>()));
}
}
This is a real dumb convention for demonstration purposes. In your project, you might make it a little more generic. For example, any output model that has an attribute on it, or implements a specific interface, etc. In fact, you might want to inspect all output models to see if they contain any properties that match a certain criteria (for example, all properties that end with "Settings" - like "FooterSettings" or something).
Don't be afraid to define wide sweeping conventions like this due to performance concerns since all this convention code runs at start-up time and not on every request.
Note the "AddAfter" call and the "Wrapper.For" call. That's the key in that it places your behavior after the controller action is executed, but before the view is rendered.
Now that you have your behavior and your convention defined, it's time to wire it up in your FubuRegistry.
Wiring Up Your Convention in your FubuRegistry
After the call to "Routes." in your FubuRegistry, add a line like this:
ApplyConvention<HomeFooterBehaviorConfiguration>();
Recompile and it should work.
Please let me know if you run into any problems.

Can I register a custom model binder somewhere other than Global.asax?

It would be handy to limit the scope of a custom model binder for just a specific controller action method or its entire controller. Hanselman wrote a sentence that implied alternative locations for custom model binder registration but never seemed to finish the thought:
You can either put this Custom Model Binder in charge of all your DateTimes by registering it in the Global.asax
Is it possible to make these registrations at a smaller scope of the controller system? If so, is there any reason to avoid doing so outside of the Global.asax MvcApplication (e.g., performance reasons)?
As I was closing the tabs I opened for this question that I hadn't reached before giving up, I found someone with an answer. You can assign a ModelBinderAttribute to your view models:
[ModelBinder(typeof(SomeEditorModelModelBinder))]
public class SomeEditorModel {
// display model goes here
}
public class SomeEditorModelModelBinder : DefaultModelBinder {
// custom model binder for said model goes here
}
While it wasn't quite what I was looking for, it is even more specific than registering it for a controller or controller method.
Update
Thanks to Levi's comment pointing out a much better solution. If you are consuming the object with a custom model binder in an MVC action method directly, you can simply decorate that method's parameter with the ModelBinder property.
public ActionResult SomeMethod([ModelBinder(typeof(SomeEditorModelBinder))]SomeEditorModel model) { ... }