c# Missing TapHandler in RelayCommand - c#

I've got a little help from another Thread here, which I not fully understood.
It's a UserControl which differentiates between several types of texts and makes them look like as one single textbox. Some of the types (i.e. Hyperlinks) are clickable.
To click on them I've got this code.
public class RelayCommand<T> : ICommand
{
readonly Action<T> _execute;
readonly Func<T, bool> _canExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public void RefreshCommand()
{
var cec = CanExecuteChanged;
if (cec != null)
cec(this, EventArgs.Empty);
}
public bool CanExecute(object parameter)
{
if (_canExecute == null) return true;
return _canExecute((T)parameter);
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
}
The problem is now, that I want to stop the Tap/Click event after clicking on the links. Usually to prevent the event from bubbling up I would do this e.Handled = True. But in this case I've got no TappEvent on the links.
The code- behind of the user control look like this:
private void Init()
{
//ComplexTextPresenterElement.Input = "This is where the Content string has to be....";
ComplexTextPresenterElement.OnHyperlinkCommand = new RelayCommand<object>(Execute);
}
private async void Execute(object o)
{
List<object> passingParameters = new List<object>();
//put here the code that can open browser
if (o is HyperLinkPart)
{
var obj = (HyperLinkPart)o;
await Windows.System.Launcher.LaunchUriAsync(new Uri(obj.RealUrl, UriKind.Absolute));
} [...]
Before the webbrowser opens I have to stop the TapEvent which gets called from the UI Element surrounding this UserControl.
I hope this is enough information. Let me know otherwise.
Cheers,
Ulpin

Have a bool canTapBrowser = false;
Change this
ComplexTextPresenterElement.OnHyperlinkCommand = new RelayCommand<object>(Execute);
To
ComplexTextPresenterElement.OnHyperlinkCommand = new RelayCommand<object>(Execute,()=> {return canTapBrowser});
In the webbrower navigation completed event do the following:
canTapBrowser = true;
OnHyperlinkCommand.RefreshCommand();

Related

WPF Command binding with method returning bool

I have two methods that do almost the two things:
public static void ShowThing()
{
// code..
}
and
public static bool TryShowThing()
{
if(condition)
{
// same code above..
return true;
}
return false;
}
At the moment I'm binding a button's Command to the void method and it does what it should.
Problem is that now I'm cleaning up the code and to avoid coupling I wanted to bind the button to the bool method and that won't work.
Is Command={Binding BooleandReturningMedhod} even allowed in xaml?
Apparently nobody on the internet has ever had this problem before so I think I'm missing something here...
You cannot bind directly to a method.
What i think what you really wanna achieve is something like this
Code:
ShowThingCommand { get; } = new RelayCommand((o) => ShowThing(),(o) => condition)
RelayCommand:
public class RelayCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
XAML:
<Button Command={Binding ShowThingCommand } />
Important part is the CanExecute method, when it returns false your Button gets disabled

RelayCommand change canExecute automatic

The current step of learning MVVM is RelayCommand for me.
So i came up with this RelayCommand class:
Relay Command class
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public RelayCommand(Action<object> execute) : this(execute, null)
{
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute ?? (x => true);
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public void Refresh()
{
CommandManager.InvalidateRequerySuggested();
}
}
View Code-Behind
To test if CanExecute is true or false, I created a Click Event which is calling the Command if CanExecute == true or Show an Error Message when CanExecute == false.
if (sender is Button button)
{
if (_viewModel.MyCommand.CanExecute(button.Tag)) // Also testet to set this parameter `null`
_viewModel.MyCommand.Execute(button.Tag);
else
ErrorMessage.Error("CanExecute = false");
}
ViewModel
In my ViewModel I created the Command and added a Thread.Sleep() to have time that canExecute can show me the ErrorMessage from the Code-Behind.
public ICommand MyCommand { get; set; }
public ViewModel()
{
MyCommand = new RelayCommand(MyCommandMethod);
}
public async void MyCommandMethod(object obj)
{
await Task.Run(() =>
{
Thread.Sleep(5000);
ErrorMessage.Error(obj as string);
});
}
The Problem now is, that if I click the Button 5 times for example, that MyCommandMetod() is used 5 times. So CanExecute will never change.
But why isn't it changing?
I understand RelayCommand as this:
1st - Button is clicked
2nd - canExecute = false (wait till process is finished)
3rd - canExecute = true
4th - Button can be executed again.
So that u can't spam Button clicks and crash the application if for example someone use SpeedClicker and clicks 1.000.000 times a seconds or so.
You have to pass some can-execute-logic to the command when creating it:
public ViewModel()
{
MyCommand = new RelayCommand(MyCommandMethod, MyCanExecutePredicate);
}
private bool MyCanExecutePredicate( object commandParameter )
{
// TODO: decide whether or not MyCommandMethod is allowed to execute right now
}
Example: if you want to allow only one command execution at a time, you could come up with something along these lines:
public async void MyCommandMethod(object obj)
{
_myCanExecute = false;
MyCommand.Refresh();
await Task.Run(() =>
{
Thread.Sleep(5000);
ErrorMessage.Error(obj as string);
});
_myCanExecute = true;
MyCommand.Refresh();
}
private bool MyCanExecutePredicate( object commandParameter )
{
return _myCanExecute;
}

Binding Static ICommand Variable grays out button?

Continuing with my struggle with Static variables and XAML, I can't work around the command bindings greying out the button.
The code in View Model:
public static ICommand CancelCalender => _cancelCalender
?? (_cancelCalender = new CommandHandler(CancelCalender_Button, _canExecute));
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();
}
}
The reference,
xmlns:viewModels="clr-namespace:StaffShiftManager.ViewModels"
And these are the ways I tried to bind the command variable:
Command="{x:Static viewModels:ViewModelBase.CancelCalender}"
And
Command="viewModels:ViewModelBase.CancelCalender"
And
Command="{Binding Source={x:Static viewModels:ViewModelBase.CancelCalender}}"
Is there something that I am missing? Any help would be greatly appreciated.
Basically in the ICommand CanExecute() method, this returns wether or not the Command is enabled \ can execute.
Returning false from the CanExecute() will disable (gray out) the button.
Now I have modified your code slightly to provide a Func<bool> as the CanExecute() handler. What will happen here is every time the Command Execution is re-queried it will execute your canExecute method.
public class CommandHandler : ICommand
{
public CommandHandler(Action execute)
:this(execute, null)
{
}
public CommandHandler(Action execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException(nameof(execute));
_executeHandler = execute;
_canExecuteHandler = canExecute ?? (() => true);
}
Func<bool> _canExecuteHandler = () => true;
Action _executeHandler;
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExecuteHandler();
}
public void Execute(object parameter)
{
_executeHandler?.Invoke();
}
}
Not that the default implementation for the canExecute method is to return true. Even passing a null Func to the constructor will still result in true.
Just to add one more thing one of my favorite command binders (much more advanced than above) is using the DelegateCommand. I dont remeber where I found the original source (as I did not write it) but is much more advanced.

Wiring up the code behind of the View with some Action in the ViewModel

In a Silverlight app that is written with MVVM I want to enable/disable my view based on some stuff.
In the constructor of the View class in code behind I can say something like this and it disables the form:
public MyForm1View()
{
InitializeComponent();
if(this.DataContext == null)
{
this.IsEnabled = False;
}
}
The issue is when there is no data to load, I am showing a gray overlay screen on top of my form to the user with a link on that gray overlay that says "Create a New Record"....now the problem is that if I disable my form like that above then How can I re-enable it when they click that CreateNewRecord link?
But how can I reenable it again from the view-model? Maybe I should have an Action on my ViewModel and when it's called on the ViewModel, it calls a method that's wired up in the code behind of the View ? But how to code this idea?
I would suggest few things:
simple wrapper for ICommand Interface:
public class DelegateCommand : ICommand
{
private readonly Action execute;
private readonly Func<bool> canExecute;
public DelegateCommand(Action execute, Func<bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (this.canExecute != null)
{
return this.canExecute();
}
else
{
return true;
}
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
execute();
}
public void RaiseExecuteChanged()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, new EventArgs());
}
}
}
ViewModel:
public class ViewModel : INotifyPropertyChanged {
public void ViewModel() {
SwitchCommand = new DelegateCommand(() => this.IsEnabled = true, () => true);
}
public DelegateCommand SwitchCommand {get;set;}
private bool isEnabled;
public bool IsEnabled {
get {
return isEnabled;
}
set {
isEnabled = value;
NotifyPropertyChanged("IsEnabled");
}
// here, InotifyPropertyChanged implementation, dozens of sample available
}
Xaml:
as example:
<Button Command={Binding SwitchCommand} /> bind command to click.
So, what's left is to set ViewModel to View, via view constructor, of IoC if you use it.
hope that help.

Implement CanExecute for button to be enabled if list item is selected

I am new to MVVM and I would like to create simple command button that will be enabled if any of the items in list is selected and that will add listitem selected to the favorite list.
Here is my AddCommand implementation:
class AddFavCommand : ICommand
{
private readonly Action _favAction;
private readonly bool _canExecute;
public AddFavCommand()
{
}
public AddFavCommand(Action favAction, bool canExecute)
{
_favAction = favAction;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_favAction();
}
}
In my View Model I have public property AddFavCommand in order to do binding with my view:
private AddFavCommand _addFavCommand;
private bool _canAddFavExecute;
public ICommand AddFavCommand
{
get
{
if (_addFavCommand == null)
{
_addFavCommand = new AddFavCommand(AddFav, _canAddFavExecute);
}
return _addFavCommand;
}
}
and for now I have simple function just to check if command will work:
private void AddFav()
{
MessageBox.Show("Add");
}
so this part works perfectly without implementing canExecute property.
But now I want button to be disabled when list item in my list is not selected. I have a property:
CurrentItem
that is binded to List Item and it will be null if item is not selected. My question is how to trigger button to be disabled when item is not selected. I tried to adding:
private void AddFav()
{
MessageBox.Show("Add");
_canAddFavExecute = CurrentItem != null; // to my function, but my button always stays disabled.
}
Thanks
If you have created your own delegate command then you will have to provide the CanExecute delegate like below and you can register this with your CanExecuteChanged event in command.
_addFavCommand = new AddFavCommand(AddFav, CanExecuteCommand);
public bool CanExecuteCommand(object parameter)
{
return CurrentCommand != null;
}
and from the setter of the CurrentCommand you will have to raise the canexecutechanged event for your command. In your case you have provided the bool once to the command and hence it is always disabled.

Categories