Sometimes when I bind to commands to a ViewModel, my CanExecute code does not always get called and hence my buttons are not disabled when they should be.
Any ideas?
Thanks
When canExecute is not called the first time, that's a binding problem.
If it's not 'automatically' called second,[n]th time, that's the normal behavior.
Imagine, how the UI should know that it should requery your predicate? When you have a command parameter it will call your predicate every time the parameter changes. Generally, some UI 'events' also requery it (focus, updatelayout etc), but not always (that's good, it would be pointless to reevaluate every command binding all time). So you cannot rely on it. You make the business logic, so you know when it needs an update, not the UI. The UI 'cannot see inside your predicate delegate' and watch what happens. You have to notify the UI about it, same as you notify when a property changed.
ICommand has an event, so you must implement it, it's the CanExecuteChanged.
You should implement a public method to fire it (or it is already implemented if you use a framework, like MVVMLight or Prism).
A simple implementation.
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
So you can call it on your command whenever your canExecute state changes in your business logic and it is going to notify all the subscribers, which is your button in this case.
You mention the state of the button not being disabled. This seems more like a binding issue than binding to commands. Is the state of the property you are binding to changing? When is it changing, etc.?
Related
I've been using Josh Smith's implementation of RelayCommand in a couple of large projects for some years now. However today I've come across a scenario where the CanExecute on one of my commands isn't refreshing. I'm at a loss as to what's causing it - the view-model isn't doing anything that I haven't done dozens of times already.
The VM's constructor creates a couple of commands. The first one is a "start" command:-
StartCommand = new RelayCommand(o => StartAsync(), o => true);
The StartAsync() method looks like this:-
private async void StartAsync()
{
IsRunning = true;
await Task.Run(() => SomeLongRunningProcess(); }
IsRunning = false;
}
There is also a "save" command:-
SaveCommand = new RelayCommand(o => Save(), o => !IsRunning);
('IsRunning' is a bog-standard property, implementing INotifyPropertyChanged. As well as being used for the "CanExecute" delegate, it's also bound to the IsEnabled property of a few controls in the view to enable/disable them).
When I click my "Start" button (bound to 'StartCommand'), the "Save" button is correctly disabled. The b/g process runs to completion, then IsRunning is set to false, but this doesn't trigger the "Save" button to become enabled. It only enables if I click somewhere on my view.
(The controls whose IsEnabled property is bound to the VM IsRunning property do enable and disable correctly, by the way).
I've come across a few SO articles about this, but nothing really explains why this happens. My workaround was to bind the button's IsEnabled property to 'IsRunning', but it's frustrating that this particular view refused to play ball. Any thoughts on what might be causing this? Common sense says it's something specific to this view/VM, but I'm stumped (and I'm not going to post the code here - there's too much of it).
Yes, because the version of RelayCommand you're using is depending on CommandManager.RequerySuggested event and it's not accurate.
Its documentation states that
Occurs when the CommandManager detects conditions that might change
the ability of a command to execute.
Basically it guesses all the possible events where your data could be changed. It can never know when your ViewModel/Model is changed. It isn't listening for the property change notifications.
If you want to react immediately without waiting for CommandManager to guess, you need to fire the ICommand.CanExecuteChanged manually yourself when model is updated.
You saw that the event not fired unless you click the window or something but do note that it could fire several times too
My Model implements INotifyPropertyChanged, and I have a WPF window bound to it (two way bindings).
I need to know when the model is being changed through the bound UI, so I could call an Update method from another module (which then copies My model to it's internal structures).
Model can also be changed by another module.
How to tell (in my PropertyChanged event handler) if the change originated in my UI, and not in that other module?
I do not want to call Update method if it was the other module that triggered the PropertyChanged event.
I'm fairly new to WPF myself, but the only immediately obvious way I can think of to do this would be to add extra set methods to the model, that modify the backing store without directly changing the property and thus firing the PropertyChanged event. To remove duplication, the property setter should probably call those methods as well and there should be a boolean argument fireChangedEvent. Something like this:
public string SomeThing
{
get { return _someThing; }
set { SetSomeThing(value, true); }
}
public void SetSomeThing(string value, bool fireChangedEvent)
{
_someThing = value;
if(fireChangedEvent)
{
NotifyPropertyChanged("SomeThing");
}
}
Then, in the other module, it would be
public void DoStuff
{
// ...
model.SetSomeThing("foo",false);
// ...
}
It's not an elegant method I know, and I hope someone else can think of something smarter, but I can't think of a good way of finding out from inside a property setter what exactly is setting that property.
Hopefully this is at least a workaround suggestion.
There is another way: using Binding.SourceUpdated
Every binding on the window would have to be set NotifyOnSourceUpdated=true, and the common handler for SourceUpdated event would do the rest (raise the Window.ModelEdited event that would trigger Update on another module).
We've using Caliburn.Micro on a new Silverlight project and everythings working great. The inbuilt conventions bind buttons click events to the viewModel, but I'm not sure what the best way to handle the selectionChanged event on datagrids and comboboxes is.
At the moment, I'm binding to the selected item and calling custom logic, but I feel like this is a bit of a code smell and that I should seperate the setting of the property and the selectedChange event. But if I seperate these, how do I bind the selection changed event to my viewModel, by commands? or an EventTrigger? Or is the code below acceptable? Its a small change but I do this logic everywhere.
private Foo _selectedFoo;
public Foo SelectedFoo
{
get
{
return _Foo;
}
set
{
if (_Foo != null && _Foo.Equals(value)) return;
_Foo = value;
NotifyOfPropertyChange("SelectedFoo");
NotifyOfPropertyChange("CanRemove");
LoadRelatedBars();
}
}
I use this technique regularly and I feel very comfortable with it.
I find perfectly fine that the VM reacts to its own state change, without the need for the external actor (which incidentally is the View, but could be another component, too) to set the new state, THEN signal the VM that the state is changed.
If you really want to, however, you can use the Message.Attach attached property to hook an event in the View to an action in the VM:
cal:Message.Attach="[Event SelectionChanged] = [OnSelectionChangedAction]"
(see also https://caliburnmicro.com/documentation/actions)
Here is a sample for MVVM and Caliburn.Micro using. Some actions like SelectionChanged should get an explicit a event arguments, so you should set it in caliburn event action part. Freqently first argument is passing $this (The actual ui element to which the action is attached.) and you gets in handler a datacontext for the row but to get to the Grid you should pass $source, as the first argument ($source - is the actual FrameworkElement that triggered the ActionMessage to be sent). According to the manual Caliburn manual.
XAML
cal:Message.Attach="[Event SelectionChanged]=[Action DataGrid_JobTypesSelectionChanged($source,$eventArgs)];"
Code:
public void DataGrid_JobTypesSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var grid = sender as DataGrid;
JobTypesSelectedCollection = grid.SelectedItems.Cast<JobComplexModel>().ToList();
}
Let's assume I'm implementing a Winforms UI where all commands adhere to the following pattern:
interface ICommand
{
bool CanExecute { get; }
void Execute();
}
Buttons or menu items that trigger such a command should have the following set-up:
property Enabled is bound to the command's CanExecute
event Click is linked to the command's Execute (through an intermediate event handler due to the differing method signatures)
The trouble with CanExecute is, implementing INotifyPropertyChanged for it won't work here, as this property cannot be directly modified, but depends on other factors in the program which needn't be related to the command. And one shouldn't have to trigger the command's PropertyChanged event in completely unrelated parts of the program.
How do you let the data binding manager know when CanExecute has changed?
Here's a (purly fictional) example of my problem:
bool CanExecute
{
get
{
return User.LoggedInForAtLeastNMinutes(5);
// how would you trigger data-binding updates for CanExecute?
}
}
Ideally, I'd like to have the UI constantly checking CanExecute (as if it were a volatile field), but AFAIK this is not how Winforms data binding works. Does anyone have a solution for this problem?
Note: I am aware of WPF, btw. The background of my question is that I'm going to gradually improve an existing Winforms application in the general direction of WPF. But actually using WPF and thus getting rid of the problem I've asked about is not feasible right now.
I would implement INotifyPropertyChanged regardless (or add a CanExecuteChanged event, which has the same effect). I would try hard for objects to know when to raise the property changed event at the right time, rather than polling.
For instance, in your fictional example, you could have a UserLoggedIn event. In response to that, you could set a 5-minute timer; when that timer elapses, you raise the property changed event.
If you go for the polling approach then you face two dangers:
Polling too often, where your application consumes CPU checking for events that can't possibly happen yet (for instance, polling every 10 seconds to see if 5 minutes are up)
Not polling often enough, where controls bound to CanExecute properties lag the rest of the UI (for instance, a delay between making a text selection and the CopyTextCommand.CanExecute property updating)
A hybrid approach is that taken by Microsoft Foundation Classes in C++, which was to make this check any time the application's message loop was idle. This is a reasonable approach when you know that only user interface interaction that can affect your CanExecute property.
Use a Timer to constantly poll the CanExecute property. Raise the PropertyChanged event when the property changes.
I' do the polling on the Application.Idle event, as long as your can execute logic is simple, there shouldn't be any problem.
here is an extract of my current 'CommandManager' implementation;
public CommandManager()
{
Commands = new List<ICommand>();
Binders = new List<ICommandBinder>
{
new ControlBinder(),
new MenuItemCommandBinder()
};
Application.Idle += UpdateCommandState;
}
private void UpdateCommandState(object sender, EventArgs e)
{
Commands.Do(c => c.Enabled);
}
(Do() is just an extension method that does a foreach, like linq Select() but taking Action instead of Func)
I've blogged about this some time ago, feel free to check: http://codewithpassion.blogspot.com/2010/11/icommand-and-commandmanager-for-windows.html
hope it helps
This question already has answers here:
How can I Have a WPF EventTrigger on a View trigger when the underlying Viewmodel dictates it should?
(4 answers)
Closed 8 years ago.
I have quite simple (I hope :)) problem:
In MVVM, View usually listens on changes of ViewModel's properties. However, I would sometimes like to listen on event, so that, for example, View could start animation, or close window, when VM signals.
Doing it via bool property with NotifyPropertyChanged (and starting animation only when it changes from false to true) is possible, but it feels like a hack, I'd much prefer to expose event, as it is semantically correct.
Also, I'd like to do it without code in codebehind, as doing viewModel.myEvent += handler there would mean that I'd have manually unregister the event in order to allow View to be GC'd - WPF Views are already able to listen on properties 'weakly', and I'd much prefer to program only declaratively in View.
The standard strong event subscription is also bad, because I need to switch multiple ViewModels for one View (because creating View every time takes too much CPU time).
Thank you for ideas (if there is a standard solution, a link to msdn will suffice)!
Some comments:
You can use the weak event pattern to ensure that the view can be GC'd even if it is still attached to the view model's event
If you're already switching multiple VMs in for the one view, wouldn't that be the ideal place to attach/detach the handler?
Depending on your exact scenario, you could just have the VM expose a state property which the view uses as a trigger for animations, transitions, and other visual changes. Visual state manager is great for this kind of thing.
This is something that I wrestled with as well...
Similar to what others are saying, but here is an example with some code snippets... This example shows how to use pub/sub to have a View subscribe to an event fired by the VM - in this case I do a GridView. Rebind to ensure the gv is in sync with the VM...
View (Sub):
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Presentation.Events;
private SubscriptionToken getRequiresRebindToken = null;
private void SubscribeToRequiresRebindEvents()
{
this.getRequiresRebindToken =
EventBus.Current.GetEvent<RequiresRebindEvent>()
.Subscribe(this.OnRequiresRebindEventReceived,
ThreadOption.PublisherThread, false,
MemoryLeakHelper.DummyPredicate);
}
public void OnRequiresRebindEventReceived(RequiresRebindEventPayload payload)
{
if (payload != null)
{
if (payload.RequiresRebind)
{
using (this.gridView.DeferRefresh())
{
this.gridView.Rebind();
}
}
}
}
private void UnsubscribeFromRequiresRebindEvents()
{
if (this.getRequiresRebindToken != null)
{
EventBus.Current.GetEvent<RequiresRebindEvent>()
.Unsubscribe(this.getRequiresRebindToken);
this.getRequiresRebindToken = null;
}
}
Call unsub from the close method to prevent memory leaks.
ViewModel (Pub):
private void PublishRequiresRebindEvent()
{
var payload = new RequiresRebindEventPayload();
payload.SetRequiresRebind();
EventBus.Current.GetEvent<RequiresRebindEvent>().Publish(payload);
}
Payload class
using System;
using Microsoft.Practices.Composite.Presentation.Events;
public class RequiresRebindEvent
: CompositePresentationEvent<RequiresRebindEventPayload>
{
}
public class RequiresRebindEventPayload
{
public RequiresRebindEventPayload()
{
this.RequiresRebind = false;
}
public bool RequiresRebind { get; private set; }
public void SetRequiresRebind()
{
this.RequiresRebind = true;
}
}
Note that you can also set the constructor up to pass in a Guid, or some identified in, which can be set on Pub and checked on sub to be sure pub/sub is in sync.
imho yYand separated
state - to be able to move data back/forth between view <-> vm
actions - to be able to call onto view model functions/commands
notifications - to be able to signal to the view that something has happened and you want it to take a viewy action like make an element glow, switch styles, change layout, focus another element etc.
while is true that you can do this with a property binding, its more of a hack as tomas mentioned; always has felt like this to me.
my solution to be able to listen for 'events' from a view model aka notifications is to simple listen for data-context changes and when it does change i verify the type is the vm i'm looking for and connect the events. crude but simple.
what i would really like is a simple way to define some 'view model event' triggers and then provide some kind of handler for it that would react on the view side of things all in the xaml and only drop to code behind for stuff thats not do-able in xaml
Like adrianm said, when you trigger your animation off a bool property you are actually responding to an event. Specifically the event PropertyChanged which the WPF subsystem. Which is designed to attach/detach correctly to/from so that you don't leak memory (you may forget to do this when wiring an event yourself and cause a memory leak by having a reference active to an object which otherwise should be GCed).
This allows you to expose your ViewModel as the DataContext for the control and respond correctly to the changing of properties on the datacontext through databinding.
MVVM is a pattern that works particularly well with WPF because of all these things that WPF gives you, and triggering off a property change is actually an elegant way to use the entire WPF subsystem to accomplish your goals :)
A more general question to ask is: "Why am I trying to deal with this event in my ViewModel?"
If the answer has anything to do with view-only things like animations, I'd argue the ViewModel needs not know about it: code behind (when appropriate), Data/Event/PropertyTriggers, and the newer VisualStateManager constructs will serve you much better, and maintain the clean separation between View and ViewModel.
If something needs to "happen" as a result of the event, then what you really want to use is a Command pattern - either by using the CommandManger, handling the event in code behind and invoking the command on the view model, or by using attached behaviors in the System.Interactivity libs.
Either way, you want to keep your ViewModel as "pure" as you can - if you see anything View-specific in there, you're probably doing it wrong. :)