WPF View and ModelView creating - c#

I'am distracted because of view and modelview objects instantiating. As an example:
I've got view V with a listview LV and a button. To the button is bound command that takes as a parametr listview LV. Commands CanExecute method checks whether listView LV has elements. But when i open view V the view model object creates before the view V does. So when CanExecture method checks a listView, it is null and my button becomes unavaliable for ever.
How solve that problem?
EDIT:
Command implementation:
public class DelegateCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public DelegateCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
if (execute == null)
{
throw new ArgumentNullException(nameof(execute));
}
this._execute = execute;
this._canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}

For that kind of command implementation, you have to call RaiseCanExecuteChanged() manually (in your case after the list view got items).
You can also change the implementation of the CanExecuteChanged event to:
[...]
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
[...]
That implementation enbales the command manager to evaluate CanExecute automatically.

Related

How to disable command for specific button in row of my Grid

I have a grid where every element have a button with command. When i click a button i set CanExecute to false and all buttons are disabled. How can i disable only one button that i click?
My command:
public RelayCommand SignDocumentsCommand
{
get
{
return signDocumentsCommand ??
(signDocumentsCommand = new RelayCommand(obj => MyMethod(), () => !IsEnabled));
}
}
My RelayCommand:
public class RelayCommand : ICommand
{
private readonly Action<object> execute;
private readonly Func<bool> canExecute;
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public RelayCommand(Action<object> execute, Func<bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute == null || canExecute();
}
public void Execute(object parameter)
{
execute(parameter);
}
}
You have 2 options.
1) move the command to the objects that are in the grid, so that each of them has seperate copy of the command and its execution depends on the object properties
2) add "CanXXXX" properties to your objects and use Style.DataTrigger to disable the button in each row
The second one will consume much less memory, but is less MVVMy

The event 'Command.CanExecuteChanged' can only appear on the left hand side of += or -=

Am I missing something in my Command-ViewModel?
public class Command : ICommand
{
public Command(Action execute, Func<bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute ?? new Func<bool>(() => true);
}
private Action execute;
private Func<bool> canExecute;
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return canExecute.Invoke();
}
public void Execute(object parameter)
{
execute?.Invoke();
}
}
Everytime I want to use CanExecuteChanged in my MainViewModel with this line of code ((Command)MyCommand).CanExecuteChanged(); it gives me this error The event 'Command.CanExecuteChanged' can only appear on the left hand side of += or -=
CanExecuteChanged is an event. You can only use it like this:
YourCommand.CanExecuteChanged += (sender, args) =>
{
// Do your magic here
};
public event EventHandler CanExecuteChanged;
is syntactic sugar. What the compiler actually generates when you put this in is something like*
private EventHandler _CanExecuteChanged;
public event EventHandler CanExecuteChanged
{
add { _CanExecuteChanged += value; }
remove { _CanExecuteChanged -= value; }
}
So the CanExecuteChange that's publicly exposed isn't the actual field but only something you can add and remove handlers with.
Related note: The backing field being private is also the reason for the normal pattern of having a protected OnXXXX() method in the base class.
public event EventHandler CanExecuteChanged;
protected void OnCanExecuteChanged(EventArgs args)
{
CanExecuteChanged?.Invoke(this, args);
}
*Note the "like" part; there's some null checking that's needed for proper add and remove as well.
To answer your question yes you are missing something from your code. I can't tell if you are using the Command class supplied by Xamarin.Forms but if you aren't then you really should be!
Ultimately you cannot interact with an event outside of the class that it belongs to apart from subscribing for the event notification which is what the 'The event 'Command.CanExecuteChanged' can only appear on the left hand side of += or -=' is telling you. To subscribe you implement something like the following:
MyCommand.CanExecuteChanged += (sender, e) =>
{
// Your code to react to the event goes here.
};
What you can do though and this is where you need to be using the Xamarin.Forms Command class (or you can implement something similar yourself in your Command class). Is call ChangeCanExecute e.g.
((Command)MyCommand).ChangeCanExecute();
This will then trigger the event to be fired and thus updating any UI controls that are bound to that command.
I deleted my Command-Class and now I am using the Xamarin.Forms Command Class which is making this a lot easier because now I can just use this delicious short line of code: ((Command)MyCommand).ChangeCanExecute(); to fire the event.
Thanks to all of you guys.
Just a another approach!
public sealed class Command : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public RelayCommand(Predicate<object> CanExecute, Action<object> Execute)
{
_canExecute = CanExecute;
_execute = Execute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
Then just let the Command Manager handle updating your .CanExecuteChanged problem :)
If you need to force the Command Manager to "look again" call CommandManager.InvalidateRequerySuggested();
Here is a little more info on the Command Manager
How does CommandManager.RequerySuggested work?

redundant ICommand class for wpf mvvm

I'm still learning about wpf, however I'm familiar withe how to setup mvvm in wpf c#. However when it comes to the ICommand/RelayCommand stuff, its a bit of a confusing area for me. Over the past few months I've compiled a few implementations of the ICommand classes in order to create my tools. However I'm at the point now where I've read a few articles and I've looked at the code long enough, I'm looking for someone to help me out and put into simple terms what is going on here and if so, how can I combine/clean up these classes. At the moment the code seems redundant and I'm not sure how to go about optimizing it. Hope this isn't asking for to much. Thanks.
The two important things I want to maintain in this, is the ability to pass arguments to the commands as seen in this first usage example of RelayCommand. Secondly the ability to enable/disable a command as seen in the second command.
So in my tool i have this helper class.
1. I don't get the differences of use between the two classes inside this RelayCommand.cs. There is a public and an internal class.
2. Is there a need for both or can they be combine?
RelayCommand.cs
using System;
using System.Windows.Input;
namespace WpfApplication1.Helper
{
public class RelayCommand<T> : ICommand
{
private readonly Action<T> execute;
private readonly Predicate<T> canExecute;
public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
{
if (execute == null)
throw new ArgumentNullException("execute");
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (parameter == null)
{
return true;
}
else
{
return canExecute == null || canExecute((T)parameter);
}
}
public void Execute(object parameter)
{
execute((T)parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
// added
internal class RelayCommand : ICommand
{
private readonly Predicate<object> canExecute;
private readonly Action<object> execute;
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
{
if (execute == null)
throw new ArgumentNullException("execute");
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute == null || canExecute(parameter);
}
public void Execute(object parameter)
{
execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}
An example of me using the RelayCommand.cs in my class object called Customer.cs
private ICommand addNewLicense_Command;
public ICommand AddNewLicense_Command
{
get
{
return addNewLicense_Command ?? (addNewLicense_Command = new RelayCommand<Customer>(n =>
{
AddNewLicense_Execute(n);
}));
}
}
So then in my MainViewModel.cs i have another ICommand Class in the same project my Helper class mentioned above is part of. Is this class necessary? It seems so similar to the RelayCommand class.
public class CommandHandler : ICommand
{
private Action _action;
private bool _canExecute;
public CommandHandler(Action action, bool canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
}
An example of me using CommandHandler in my MainViewModel.cs
private ICommand addNewUser_Command;
public ICommand AddNewUser_Command
{
get
{
return addNewUser_Command ?? (addNewUser_Command = new CommandHandler(() => AddNewUser_Execute(), true));
}
}
If you use a library like MVVM Lite then it will provide the RelayCommand implementations for you. Either way use the non-generic when you don't need a parameter passed in e.g. an "Ok" button:
public ICommand OkCommand { get { return new RelayCommand(Ok); } }
protected virtual void Ok()
{
// ... do something ...
}
The associated XAML is something like:
<Button Content="Ok" Command="{Binding OkCommand}" IsDefault="True" />
Use the generic when you want to pass a parameter:
public ICommand OpenClientCommand { get { return new RelayCommand<Client>(OnOpenClient); } }
private void OnOpenClient(Client client)
{
// ... do something with client ...
}
In this case you need to pass in a Client object via the command parameter:
<Button Content="Open" Command="{Binding OpenClientCommand}" CommandParameter="{Binding SelectedClient}"/>
Passing parameters is also handy when used with event triggers, e.g. you can add something like this to intercept your MainWindow's Closing event:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<cmd:EventToCommand Command="{Binding ClosingCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
This trigger passes the message arguments into your handler which gives you the opportunity to cancel it in the event that the user hasn't saved their changes:
public ICommand ClosingCommand { get { return new RelayCommand<CancelEventArgs>(OnClosing); } }
private void OnClosing(CancelEventArgs args)
{
if (!PromptUserForClose())
args.Cancel = true;
}
The RelayCommand you have is all you need. If you want to disable the command you can pass a method in the constructor do do so:
return addNewLicense_Command ?? (addNewLicense_Command = new RelayCommand<Customer>(n =>
{
AddNewLicense_Execute(n);
},AllowAddNeLicense));
...
bool AllowAddNewLicense()
{
return _allowAddEnabled;
}
The second class called CommandHandler is just another implementation of ICommand. The difference is that you can pass the "enabled" boolean inside the constructor, which means it will remain the same unless you create a new instance of it. While in the RelayCommand you can pass a function that gets executed everytime* so you can influence the outcome.

MVVM - Commands on special events

I try to use commands with the MVVM - Pattern and I don't know how to "bind" a command to a special event, e.g. MouseUp or MouseEnter. How to do this?
First you should define ICommnad property in your ViewModel.
public ICommand MouseUpCommand
{
get
{
if (this.mouseUpCommand == null)
{
this.mouseUpCommand = new RelayCommand(this.OnMouseUp);
}
return this.mouseUpCommand;
}
}
private void OnMouseUp()
{
// Handle MouseUp event.
}
You can find lot of ICommand implementations. One of them:
public class RelayCommand : ICommand
{
public RelayCommand(Action<object> execute)
{
this._execute = execute;
...
}
...
public void Execute(object parameter)
{
_execute(parameter);
}
}
Then add event trigger within which invoke your Command:
<i:EventTrigger EventName="MouseUp">
<i:InvokeCommandAction Command="{Binding MouseUpCommand}"/>
</i:EventTrigger>
Read the EventToCommand at the following page, please
Look at WPF Binding UI events to commands in ViewModel.
For this you need System.Windows.Interactivity.dll which you can get from Nuget
Completing #AnatoliiG post here's an implementation and a sample usage of the RelayCommand class.
Code:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
Usage:
// To use this class within your viewmodel class:
RelayCommand _myCommand;
public ICommand MyCommand
{
get
{
if (_myCommand == null)
{
_myCommand = new RelayCommand(p => this.DoMyCommand(p),
p => this.CanDoMyCommand(p) );
}
return _myCommand;
}
}

How to bind to property with only get accessor

I have some custom editable listbox on my wpf window.
I also have a viewmodel class with Property Changed which looks like that:
public bool HasChanges
{
get
{
return customers.Any(customer => customer.Changed);
}
}
So, I would like to bind my Save button to this property:
<Button IsEnabled="{Binding HasChanges, Mode=OneWay}"...
My question is how to update Save button if one of the listbox rows is changed?
The proper way to deal with buttons is to implement ICommand interface. Here is an example from my solution:
public class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute) : this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
#endregion
}
You can then databind to button like this:
<Button Command="{Binding MyCommand}" .../>
Whats left is to declare an ICommand property on your viewmodel:
public ICommand MyCommand { get; private set; }
//in constructor:
MyCommand = new RelayCommand(_ => SomeActionOnButtonClick(), _ => HasChanges);
The state of the button will then automatically update on most changes. If it doesnt for some reason - you can force the update by calling CommandManager.InvalidateRequerySuggested
In order for WPF to react to changes in properties, the class must implement INotifyPropertyChanged interface. You need to send notifications every time a customer is changed, like this:
class CustomerList : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private List<Customer> customers = ...
public bool HasChanges {
get {
return customers.Any(customer => customer.Changed);
}
}
// Callers who change customers inside your list must call this method
public void ChangeCustomer(Customer c) {
// Do whatever you need to do, ...
...
// then send out the notification to WPF
OnPropertyChanged("HasChanges");
}
protected void OnPropertyChanged(string name) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Your ViewModel should implement INotifyPropertyChanged and should raise the PropertyChanged Event for HasChanges when ever you change a Customer in customers
Update:
If Customers implement INotifyPropertyChanged and customers it self is an observable collection. You could subscribe, and depending on the action desubscribe, to all the customers in the CollectionChangedEvent of your customers collection.
If your ViewModel implements INotifyPropertyChanged, you just need to call the OnPropertyChanged() method on HasChanges. With Prism, the equivalent method is RaisePropertyChanged.
However, with MVVM, you might want to put that test in the CanExecute method of your command which is bound to your button Command property. This will handle the IsEnabled automatically.
The button somehow has to receive notifications. In your case you probably implement the INotifyPropertyChanged interface in your viewmodel. When your "listbox row is changed" you should raise the PropertyChanged event for the "HasChanges" property. Changes should be noticed however in you viewmodel and there the event should be raised.
As a different solution since you have a viewmodel you could use a command on your button and the CanExecute would have the logic returning true or false, which also has to be marked by you when changes have happened.

Categories

Resources