CanExecute of ApplicationCommand does not update - c#

I would like to call an ApplicationCommand with a MenuItem-Click:
<MenuItem Header="{StaticResource MenuItemSave}" Command="ApplicationCommands.Save"/>
In my ViewModel-Constructor I inizialize my bindings with:
CommandBinding saveBinding = new CommandBinding(ApplicationCommands.Save, SaveCommand_Execute, SaveCommand_CanExecute);
CommandManager.RegisterClassCommandBinding(typeof(ViewModel_Main), saveBinding);
RegisterCommandBindings.Add(saveBinding);
Now I would like to handle the command, but it simply is not executeable. Even if it should be ALWAYS true.
private void SaveCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void SaveCommand_Execute(object sender, ExecutedRoutedEventArgs e)
{
//Stuff
}
I have also tried to update all bindings after my init function:
CommandManager.InvalidateRequerySuggested();
But my MenuItem stays disabled.
Thank you!

I would suggest using this implementation of ICommand
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
namespace Common
{
public class Command<TArgs> : ICommand
{
public Command(Action<TArgs> exDelegate)
{
_exDelegate = exDelegate;
}
public Command(Action<TArgs> exDelegate, Func<TArgs, bool> canDelegate)
{
_exDelegate = exDelegate;
_canDelegate = canDelegate;
}
protected Action<TArgs> _exDelegate;
protected Func<TArgs, bool> _canDelegate;
#region ICommand Members
public bool CanExecute(TArgs parameter)
{
if (_canDelegate == null)
return true;
return _canDelegate(parameter);
}
public void Execute(TArgs parameter)
{
if (_exDelegate != null)
{
_exDelegate(parameter);
}
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
bool ICommand.CanExecute(object parameter)
{
if (parameter != null)
{
var parameterType = parameter.GetType();
if (parameterType.FullName.Equals("MS.Internal.NamedObject"))
return false;
}
return CanExecute((TArgs)parameter);
}
void ICommand.Execute(object parameter)
{
Execute((TArgs)parameter);
}
#endregion
}
}

Related

Change data binding value in ViewModel xamarin forms android

I have a binding set up:
using System;
using System.Collections.Generic;
using System.Text;
using LoanApp2.Model;
using System.Windows.Input;
using System.Threading.Tasks;
using System.ComponentModel;
using LoanApp2.Views;
using Newtonsoft.Json;
using System.Net.Http;
namespace LoanApp2.ViewModel
{
public class LoginViewModel : INotifyPropertyChanged
{
// For data binding of activity indicator
string actIndVal = "False";
public string ActIndVal {
get => actIndVal;
set {
if(actIndVal == value)
{
return;
} else
{
actIndVal = value;
OnPropertyChanged(nameof(ActIndVal));
}
}
}
public static List<LoginBasicData> listLoginBasicData = new List<LoginBasicData>();
public LoginViewModel()
{
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string value)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(value));
}
public static async void VerifyClientID(string clientID)
{
// Start of HTTP Requests
using (HttpClient client = new HttpClient())
{
...
}
}
}
The default value would be false, in in the class VerifyClientID(), I want to call the ActIndVal and change it to true so that the Activity Indicator will be visible. And call it again in the bottom part of the VerifyClientID() class so that I can change the value to false again.
Change the property in the ViewModel won't work, you should change the value of ActIndVal of the ViewModel which is your BindContext.
For example:
public partial class MainPage : ContentPage
{
LoginViewModel myViewModel;
public MainPage()
{
InitializeComponent();
myViewModel = new LoginViewModel();
this.BindingContext = myViewModel;
}
private async void Button_Clicked(object sender, EventArgs e)
{
myViewModel.ActIndVal = true;
await myViewModel.VerifyClientID("clientID");
myViewModel.ActIndVal = false;
}
}
public class LoginViewModel : INotifyPropertyChanged
{
// For data binding of activity indicator
bool actIndVal = false;
public bool ActIndVal
{
get => actIndVal;
set
{
if (actIndVal == value)
{
return;
}
else
{
actIndVal = value;
OnPropertyChanged(nameof(ActIndVal));
}
}
}
public LoginViewModel()
{
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string value)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(value));
}
public async Task VerifyClientID(string clientID)
{
// Start of HTTP Requests
await Task.Delay(3000);
}
}
myViewModel.ActIndVal is the value you need to change because Frame.IsVisible is binding to this value. Changing the value of ActIndVal in your viewModel makes no sense.
I uploaded my sample project here and feel free to ask me any question.
first of all this should be a boolean to avoid casting issues
private bool actIndVal;
public bool ActIndVal {
get => actIndVal;
set {
if(actIndVal == value)
{
return;
} else
{
actIndVal = value;
OnPropertyChanged(nameof(ActIndVal));
}
}
}
Then in your method change the values:
public async Task VerifyClientID(string clientID)
{
ActIndVal= true;
//API CALL CODE
ActIndVal= false;
}
Then just bind this to your Indicator
<ActivityIndicator IsRunning="{Binding ActIndVal}" IsVisible="{Binding ActIndVal}" ....

Why does an ICommand object have to be public to work properly in WPF?

I am following a course on Pluralsight called "Practical MVVM". In the 4th module of the course, the author was explaining how to use commands and that's when I had this strange issue. I had written almost the same code as it was in the video except for one difference. My local ICommand variable was declared as private instead of public.
Here is what happens when I click the button that has a Binding set to the method EditCoffee() that belongs to EditCommand command:
When ICommand EditCommand is declared as public or internal the method EditCoffee() is executed following the execution of Execute() and CanExecute() in CustomCommand class.
When ICommand EditCommand is declared as private or protected the method EditCoffee() is never executed and does not follow execution of Execute() and CanExecute() in CustomCommand class.
It's worth mentioning that LoadCommands() is triggered on in both scenarios.
CoffeeOverviewViewModel:
using JoeCoffeeStore.StockManagement.App.Services;
using JoeCoffeeStore.StockManagement.App.Utility;
using JoeCoffeeStore.StockManagement.Model;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace JoeCoffeeStore.StockManagement.App.ViewModel
{
public class CoffeeOverviewViewModel : INotifyPropertyChanged
{
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// Commands
private ICommand EditCommand { get; set; }
// Data Service
private CoffeeDataService _coffeeDataService;
// Properties
private ObservableCollection<Coffee> _coffees;
public ObservableCollection<Coffee> Coffees
{
get{ return _coffees; }
set
{
_coffees = value;
OnPropertyChanged();
}
}
private Coffee _selectedCoffee;
public Coffee SelectedCoffee
{
get { return _selectedCoffee; }
set
{
_selectedCoffee = value;
OnPropertyChanged();
}
}
// Constructor
public CoffeeOverviewViewModel()
{
_coffeeDataService = new CoffeeDataService();
LoadData();
LoadCommands();
}
private void LoadCommands()
{
EditCommand = new CustomCommand(EditCoffee, CanEditCoffee);
}
private void EditCoffee(object obj)
{
//TODO
}
private bool CanEditCoffee(object obj)
{
if (SelectedCoffee != null)
return true;
return false;
}
private void LoadData()
{
Coffees = new ObservableCollection<Coffee>(_coffeeDataService.GetAllCoffees());
}
}
}
CustomCommand.cs:
using System;
using System.Windows.Input;
namespace JoeCoffeeStore.StockManagement.App.Utility
{
public class CustomCommand : ICommand
{
private Action<object> _execute;
private Predicate<object> _canExecute;
public CustomCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public bool CanExecute(object parameter)
{
bool b = _canExecute == null ? true : _canExecute(parameter);
return b;
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
}
CoffeeOverviewView.xaml:
<StackPanel Grid.Row="7">
<Button Command="{Binding EditCommand}" Content="Edit coffee"/>
</StackPanel>
Solution structure:
Binding isn't magic. It's code that takes your XAML binding declaration:
<Button Command="{Binding EditCommand}"
and looks for a property of that name, EditCommand, in the DataContext of your class.
Your XAML view is a separate class than your ViewModel, so it makes sense that it can only access public properties on the ViewModel. Even internal properties are out of bounds, because it's the binding code that tries to access it, in the WPF libraries, not your own view code.

Relaycommand ICommand.CanExecute not firing

i have the following problem:
I have a relaycommand with a execute an a canexecute method, but everytime i call raisecanexecutechanged(); it calls raisecanexecutechanged in relaycommand, sets a new delegate for it and then returns back to the view model.
The same setup works in another viewmodel. I checked like 1000 times what's different but i don't find anything.
I would really appreciate if you could help me.
public RelayCommand UpdateAMSCommand { get; private set; }
public AMSSettingsViewModel(IEventAggregator eventAggregator)
{
UpdateAMSCommand = new RelayCommand(OnUpdateAMS, CanUpdateAms);
CustomAMSOffices.ListChanged += listChanged;
CustomAMSContacts.ListChanged += listChanged;
}
private void listChanged(object sender, ListChangedEventArgs e)
{
if (sender != null)
{
if (sender is BindingList<CustomAMSOffice>)
{
BindingList<CustomAMSOffice> temp = (BindingList<CustomAMSOffice>)sender;
if (temp.Count > _amsOfficesItemsCounter)
{
_amsOfficesItemsCounter = temp.Count;
for (int i = 0; i < temp.Count; i++)
{
temp[i].ErrorsChanged += RaiseCanExecuteChanged;
}
}
}
else if (sender is BindingList<CustomAMSContact>)
{
BindingList<CustomAMSContact> temp = (BindingList<CustomAMSContact>)sender;
if (temp.Count > _amsContactsItemsCounter)
{
_amsContactsItemsCounter = temp.Count;
for (int i = 0; i < temp.Count; i++)
{
temp[i].ErrorsChanged += RaiseCanExecuteChanged;
}
}
}
}
UpdateAMSCommand.RaiseCanExecuteChanged();
}
private void RaiseCanExecuteChanged(object sender, DataErrorsChangedEventArgs e)
{
UpdateAMSCommand.RaiseCanExecuteChanged();
}
private bool CanUpdateAms()
{
foreach (var cao in CustomAMSOffices)
{
if (!cao.Check() || cao.HasErrors)
{
return false;
}
}
foreach (var cac in CustomAMSContacts)
{
if (!cac.Check() || cac.HasErrors)
{
return false;
}
}
return true;
}
Edit:
the relaycommand i use: https://github.com/briannoyes/WPFMVVM-StarterCode/blob/master/ZzaDashboard/ZzaDashboard/RelayCommand.cs
Ok, I'm just going to copy paste some code that I have in use, so that you should be able to pop these into your project and use.
First off, the RelayCommand() class. I lifted this code from this msdn page:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion
#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
#region ICommand Members
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
}
Now our ModelView.cs class needs to inherit from INotifyPropertyChangedand will have our RaisePropertyChanged(). Now I usually make just make this a it's own file and have all my ModelViews inherit from it so the code is a little cleaner, but you can do as you please.
Here's how I have it setup though:
BaseViewModel.cs:
public class BaseViewModel : INotifyPropertyChanged
{
internal void RaisePropertyChanged(string prop)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
public event PropertyChangedEventHandler PropertyChanged;
// Any other code we want all model views to have
}
Now for our MainViewModel.cs we will just inherit from BaseViewModel, add our event handlers in, and run it!
Example: ServerViewModel.cs
public class ServerViewModel : BaseViewModel
{
public RelayCommand BroadcastMessageCommand { get; set; }
private string _broadcastmessage;
public string broadcastmessage
{
get { return _broadcastmessage; }
set { _broadcastmessage = value; RaisePropertyChanged("broadcastmessage"); }
}
Server server;
public ServerViewModel()
{
server = new Server();
server.run();
BroadcastMessageCommand = new RelayCommand(BroadcastMessage, CanBroadcast);
}
private bool CanBroadcast(object param)
{
if (string.IsNullOrWhiteSpace(broadcastmessage))
return false;
if (!server.running)
return false;
return true;
}
public void BroadcastMessage(object param)
{
server.BroadcastMessage(broadcastmessage);
broadcastmessage = "";
RaisePropertyChanged("broadcastmessage");
}
}
Now anything in our MainView.xaml that is bound with Command="{Binding broadcastmessage}" will update appropriately. In my case I have this bound to a button and the button will be disabled if there message is empty, or if we are not connected to the server.
Hopefully that's enough code example to get you headed in the right direction! Let me know if you have any questions on it.
Let's try simplifying the code as much as we can until we get this working properly, and then we will slowly add code back until we find the code(s) that are causing trouble.
So let's reduce this to it's barebones and see if we have any success. Try this code:
public RelayCommand UpdateAMSCommand { get; private set; }
public AMSSettingsViewModel(IEventAggregator eventAggregator)
{
UpdateAMSCommand = new RelayCommand(OnUpdateAMS, CanUpdateAms);
CustomAMSOffices.ListChanged += listChanged;
CustomAMSContacts.ListChanged += listChanged;
}
private void listChanged(object sender, ListChangedEventArgs e)
{
UpdateAMSCommand.RaiseCanExecuteChanged();
}
private void RaiseCanExecuteChanged(object sender, DataErrorsChangedEventArgs e)
{
UpdateAMSCommand.RaiseCanExecuteChanged();
}
// This will simply flip from true to false every time it is called.
private bool _canupdate = false;
private bool CanUpdateAms()
{
_canupdate = !_canupdate;
return _canupdate;
}
Edit: I don't know why it doesn't work.

MvvmCross Custom Event Binding Event Args

I have created a custom binding for the FocusChange event on an EditText using MvvmCross. I can get the event bound and firing, but I can't figure out how to pass the event args. My Custom Binding is this
using Android.Views;
using Android.Widget;
using Cirrious.MvvmCross.Binding;
using Cirrious.MvvmCross.Binding.Droid.Target;
using Cirrious.MvvmCross.Binding.Droid.Views;
using Cirrious.MvvmCross.ViewModels;
using System;
namespace MPS_Mobile_Driver.Droid.Bindings
{
public class MvxEditTextFocusChangeBinding
: MvxAndroidTargetBinding
{
private readonly EditText _editText;
private IMvxCommand _command;
public MvxEditTextFocusChangeBinding(EditText editText) : base(editText)
{
_editText = editText;
_editText.FocusChange += editTextOnFocusChange;
}
private void editTextOnFocusChange(object sender, EditText.FocusChangeEventArgs eventArgs)
{
if (_command != null)
{
_command.Execute( eventArgs );
}
}
public override void SetValue(object value)
{
_command = (IMvxCommand)value;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_editText.FocusChange -= editTextOnFocusChange;
}
base.Dispose(isDisposing);
}
public override Type TargetType
{
get { return typeof(IMvxCommand); }
}
protected override void SetValueImpl(object target, object value)
{
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.OneWay; }
}
}
}
I wire it up in my ViewModel as this:
public IMvxCommand FocusChange
{
get
{
return new MvxCommand(() =>
OnFocusChange()
);
}
}
private void OnFocusChange()
{
//Do Something
}
Is there a way to do something like
public IMvxCommand FocusChange
{
get
{
return new MvxCommand((e) =>
OnFocusChange(e)
);
}
}
private void OnFocusChange(EditText.FocusChangeEventArgs e)
{
//Do Something
}
What I tried to do there doesn't work, but I was hoping that there was something similar that might work. I am able to pass the eventargs when the command fires in the custom binding with this line
_command.Execute( eventArgs );
I just can't figure a way to catch them in the ViewModel. Can anyone help me with this?
Jim
After trying many different arrangements, I found that the correct syntax to wire up your MvxCommand is
public IMvxCommand FocusChange
{
get
{
return new MvxCommand<EditText.FocusChangeEventArgs>(e => OnFocusChange(e));
}
}
private void OnFocusChange(EditText.FocusChangeEventArgs e)
{
if (!e.HasFocus)
{
//Do Something
}
}
Hope this helps!

INotifyPropertyChanged and Threading

I have a base class implementing INotifyPropertyChanged:
protected void OnNotifyChanged(string pName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(pName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
I have a derived class with a property Latitude like so:
private double latitude;
public double Latitude
{
get { return latitude; }
set { latitude = value; OnNotifyChanged("Latitude"); }
}
My derived class also has a method Fly that manipulates Latitude.
I also have a Form with a TextBox bound to Latitude of my derived class:
txtLat.DataBindings.Clear();
txtLat.DataBindings.Add("Text", bindSrc, "Latitude");
A thread is used to kick off Fly like so:
Thread tFly = new Thread(f.Fly);
tFly.IsBackground = true;
tFly.Start();
When Latitude changes, an exception is thrown:
DataBinding cannot find a row in the list that is suitable for all bindings.
This seems to be an odd issue with thread affinity. Ultimately, the code is trying to do the update from a non-UI thread - I'm unclear why it isn't just displaying the cross-thread exception, though - I wonder whether this is actually a catch-all exception handler. If I remove the BindingSource (and bind directly to the object, which is valid) you do get a cross-thread exception (which I expected).
Personally, I would be inclined to handle this manually, i.e. subscribe to the event with a method that does an Invoke to the UI thread and updates the Text manually. However, I'm just checking if some previous cross-threaded binding code might help...
Here's an example using Invoke:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
class FlightUav : INotifyPropertyChanged
{
protected void OnNotifyChanged(string pName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(pName));
}
public event PropertyChangedEventHandler PropertyChanged;
private double _latitude;
public double Latitude
{
get { return _latitude; }
set { _latitude = value; OnNotifyChanged("Latitude"); }
}
public void Fly()
{
for (int i = 0; i < 100; i++)
{
Latitude++;
Thread.Sleep(10);
}
}
[STAThread]
static void Main()
{
using (Form form = new Form())
{
FlightUav currentlyControlledFlightUav = new FlightUav();
currentlyControlledFlightUav.PropertyChanged += delegate
{ // this should be in a *regular* method so that you can -= it when changing bindings...
form.Invoke((MethodInvoker)delegate
{
form.Text = currentlyControlledFlightUav.Latitude.ToString();
});
};
using (Button btn = new Button())
{
btn.Text = "Fly";
btn.Click += delegate
{
Thread tFly = new Thread(currentlyControlledFlightUav.Fly);
tFly.IsBackground = true;
tFly.Start();
};
form.Controls.Add(btn);
Application.Run(form);
}
}
}
}
Here's an example using a (modified) version of some old threading code of mine:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
class FlightUav : INotifyPropertyChanged
{
protected void OnNotifyChanged(string pName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(pName));
}
public event PropertyChangedEventHandler PropertyChanged;
private double _latitude;
public double Latitude
{
get { return _latitude; }
set { _latitude = value; OnNotifyChanged("Latitude"); }
}
public void Fly()
{
for (int i = 0; i < 100; i++)
{
Latitude++;
Thread.Sleep(10);
}
}
[STAThread]
static void Main()
{
using (Form form = new Form())
{
FlightUav currentlyControlledFlightUav = new FlightUav();
BindingSource bindSrc = new BindingSource();
var list = new ThreadedBindingList<FlightUav>();
list.Add(currentlyControlledFlightUav);
bindSrc.DataSource = list;
form.DataBindings.Clear();
form.DataBindings.Add("Text", list, "Latitude");
using (Button btn = new Button())
{
btn.Text = "Fly";
btn.Click += delegate
{
Thread tFly = new Thread(currentlyControlledFlightUav.Fly);
tFly.IsBackground = true;
tFly.Start();
};
form.Controls.Add(btn);
Application.Run(form);
}
}
}
}
public class ThreadedBindingList<T> : BindingList<T>
{
private readonly SynchronizationContext ctx;
public ThreadedBindingList()
{
ctx = SynchronizationContext.Current;
}
protected override void OnAddingNew(AddingNewEventArgs e)
{
SynchronizationContext ctx = SynchronizationContext.Current;
if (ctx == null)
{
BaseAddingNew(e);
}
else
{
ctx.Send(delegate
{
BaseAddingNew(e);
}, null);
}
}
void BaseAddingNew(AddingNewEventArgs e)
{
base.OnAddingNew(e);
}
protected override void OnListChanged(ListChangedEventArgs e)
{
if (ctx == null)
{
BaseListChanged(e);
}
else
{
ctx.Send(delegate
{
BaseListChanged(e);
}, null);
}
}
void BaseListChanged(ListChangedEventArgs e)
{
base.OnListChanged(e);
}
}

Categories