TapGestureRecognizer doesnt work when tapping label - c#

I'm trying to make a label tappable. But when I try to tap on the label. Nothing is happening. I doesn't call the command.
When I debug it, it never calls the command I have setup.
From ViewModel:
public ICommand TappedDateCommand => new Command<int>(ChangeToTappedDate);
private void ChangeToTappedDate(int position)
{
Position = position;
PositionChanged(position);
OnPropertyChanged("Position");
}
Xaml:
<Label ...">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TappedDateCommand}" CommandParameter="0" NumberOfTapsRequired="1" />
</Label.GestureRecognizers>
</Label>

Try using String instead of int.
public ICommand TappedDateCommand => new Command<string>(ChangeToTappedDate);
private void ChangeToTappedDate(string position)
{
Position = Convert.ToInt32(position);
PositionChanged(position);
OnPropertyChanged("Position");
}

About binding command for lanel TapGestureRecognizer, I do one sample that you can take a look:
Firstly, changing the commandhandler to accept parameter and changing the method to accept the commandparameter.
public class RelayCommand1 : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public RelayCommand1(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand1(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_execute(parameter);
}
}
Then use the code:
<Label HorizontalOptions="CenterAndExpand" Text="Welcome to Xamarin.Forms!">
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding command1}"
CommandParameter="55"
NumberOfTapsRequired="1" />
</Label.GestureRecognizers>
</Label>
public partial class Page20 : ContentPage
{
public RelayCommand1 command1 { get; set; }
public Page20 ()
{
InitializeComponent ();
command1 = new RelayCommand1(obj => ChangeToTappedDate((string)obj));
this.BindingContext = this;
}
public void ChangeToTappedDate(string position)
{
int value = int.Parse(position);
Console.WriteLine("the position is {0}",value);
}
}

Related

WPF MVVM AsyncCommand CanExecute not working

I am attempting to create a simple WPF application that have a combobox populated with server names and a button to connect to a server.
Expected behaviour:
Button is disabled at first but become avaiable as soon as a server is selected.
I am using the AsyncCommand found this in this blog post that implement the ICommand methods for Async tasks.
My problem is that the button is working properly when using normal RelayCommand but doesnt work when I use AsynCommand. Am I missing something?
Here is the simplified code:
ConnectionWindow.xaml.cs:
<ComboBox Grid.Row="1" Grid.Column="1"
HorizontalAlignment="Left"
x:Name="listSourceServer"
ItemsSource="{Binding ListSourceServer}"
SelectedValue="{Binding SelectedSourceServer}"
VerticalAlignment="Top"
Width="450"
RenderTransformOrigin="0.5,0.5"/>
<Button Grid.Row="1" Grid.Column="2"
Content="Connect"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="100"
Height="25"
FontFamily="Arial"
Foreground="#FFFFFF"
Background="#2e86de"
Command="{Binding ButtonConnectSourceServer}">
</Button>
ConnectionViewModel.cs:
private string _selectedSourceServer;
public string SelectedSourceServer
{
set
{
_selectedSourceServer = value;
OnPropertyChanged(nameof(SelectedSourceServer));
}
get => _selectedSourceServer;
}
private async Task ConnectSourceServerAsync()
{
await ConnectAsync(SelectedSourceServer);
}
private bool CanConnectOnSourceServer()
{
return !string.IsNullOrEmpty(SelectedSourceServer);
}
public ConnectionViewModel() {
ButtonConnectSourceServer = new AsyncCommand(ConnectSourceServerAsync, CanConnectOnSourceServer);
}
AsyncCommand.cs:
public interface IAsyncCommand : ICommand
{
Task ExecuteAsync();
bool CanExecute();
}
public class AsyncCommand : IAsyncCommand
{
public event EventHandler CanExecuteChanged;
private bool _isExecuting;
private readonly Func<Task> _execute;
private readonly Func<bool> _canExecute;
private readonly IErrorHandler _errorHandler;
public AsyncCommand(
Func<Task> execute,
Func<bool> canExecute = null,
IErrorHandler errorHandler = null)
{
_execute = execute;
_canExecute = canExecute;
_errorHandler = errorHandler;
}
public bool CanExecute()
{
return !_isExecuting && (_canExecute?.Invoke() ?? true);
}
public async Task ExecuteAsync()
{
if (CanExecute())
{
try
{
_isExecuting = true;
await _execute();
}
finally
{
_isExecuting = false;
}
}
RaiseCanExecuteChanged();
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
#region Explicit implementations
bool ICommand.CanExecute(object parameter)
{
return CanExecute();
}
void ICommand.Execute(object parameter)
{
ExecuteAsync().FireAndForgetSafeAsync(_errorHandler);
}
#endregion
}
You need to call the RaiseCanExecuteChanged method when the SelectedSourceServer property changes.
public ConnectionViewModel()
{
_buttonConnectSourceServer = new AsyncCommand(ConnectSourceServerAsync, CanConnectOnSourceServer);
}
private readonly AsyncCommand _buttonConnectSourceServer;
public IAsyncCommand ButtonConnectSourceServer => _buttonConnectSourceServer;
private string _selectedSourceServer;
public string SelectedSourceServer
{
get => _selectedSourceServer;
set
{
_selectedSourceServer = value;
OnPropertyChanged(nameof(SelectedSourceServer));
_buttonConnectSourceServer.RaiseCanExecuteChanged();
}
}

How correctly implement ICommand in WPF using MVVM pattern?

I`m trying to do a simple WPF application using MVVM pattern. I wrote a class implementing ICommand interface:
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);
}
}
then I use it, when I click on the button in the view, to show a new page, by assigning a page to the current page
public ICommand bFirst_Click
{
get
{
return new RelayCommand(o => CurrentPage = first);
}
}
XAML code in view
<StackPanel>
<Button Command="{Binding bFirst_Click}" Content="First"/>
</StackPanel>
<Frame
Grid.Column="1"
Content="{Binding CurrentPage}"
NavigationUIVisibility="Hidden"
Opacity="{Binding FrameOpacity}"
/>
But nothing happens. Please help me, did I miss something, or doing it in the wrong way?

Handle UWP AutoSuggestionBox events the MVVM way

I am trying to create an AutoSuggestBox that allows the user to search for a specific weather station.
To handle the TextChanged event I added a binding to the respective ViewModel property in the markup:
<AutoSuggestBox Grid.Row="1"
PlaceholderText="Station"
VerticalAlignment="Center"
QueryIcon="Forward"
Width="300"
Height="50"
DisplayMemberPath="Name"
TextMemberPath="Name"
ItemsSource="{Binding Path=Stations}">
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="TextChanged">
<core:InvokeCommandAction Command="{Binding TextChanged}"></core:InvokeCommandAction>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
</AutoSuggestBox>
My ViewModel looks as follows:
public class StationCollectionVM : INotifyPropertyChanged
{
private IStationManager stationManager;
private ICommand textChanged;
public ObservableCollection<StationVM> Stations { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public StationCollectionVM(IStationManager stationManager)
{
this.stationManager = stationManager;
Stations = new ObservableCollection<StationVM>();
LoadStations();
}
private async void LoadStations()
{
Stations.Clear();
IEnumerable<Station> stations = await stationManager.GetAllStationsAsync();
IEnumerator<Station> e = stations.GetEnumerator();
while (await Task.Factory.StartNew(() => e.MoveNext()))
{
Stations.Add(new StationVM(stationManager, e.Current));
}
}
public ICommand TextChanged
{
get
{
if (textChanged == null)
{
textChanged = new RelayCommand(args =>
{
// ICommand.Execute(...) takes only 1 param.
// How do you get both the AutoSuggestBox and
// AutoSuggestBoxTextChangedEventArgs param
// sent from the AutoSuggestBox?
// Filter stations based on the user input here...
});
}
return textChanged;
}
}
}
Please note that RelayCommand is just an implementation of ICommand:
public class RelayCommand : ICommand
{
readonly Action<object> executeAction;
readonly Predicate<object> canExecutePredicate;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
executeAction = execute ?? throw new ArgumentNullException(nameof(execute));
canExecutePredicate = canExecute;
}
public void Execute(object parameter)
{
executeAction(parameter);
}
public bool CanExecute(object parameter)
{
return canExecutePredicate == null ? true : canExecutePredicate(parameter);
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, new EventArgs());
}
}
How do I access both event parameters in StationCollectionVM's TextChanged property? Also, what is the preferred way to pass the filtered station list back to the AutoSuggestBox?
If you just want to filter data based on the input value of AutoSuggestBox, then only 1 argument is sufficient. You can pass Text property of AutoSuggestBox as a CommandParamenter like below:
<AutoSuggestBox x:Name="autoSuggestBox"
Grid.Row="1"
PlaceholderText="Station"
VerticalAlignment="Center"
QueryIcon="Forward"
Width="300"
Height="50"
DisplayMemberPath="Name"
TextMemberPath="Name"
ItemsSource="{Binding Path=ComboBoxList}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="TextChanged">
<core:InvokeCommandAction Command="{Binding TextChanged}" CommandParameter="{Binding Text, ElementName=autoSuggestBox}"></core:InvokeCommandAction>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AutoSuggestBox>
Also, note that you need additional property to store your actual collection which you can retrieve in case of no filter value.
Your VM:
public class StationCollectionVM : INotifyPropertyChanged
{
private IStationManager stationManager;
private ICommand textChanged;
private IEnumerable<StationVM> stationsVM { get; set; }
public ObservableCollection<StationVM> Stations { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public StationCollectionVM(IStationManager stationManager)
{
this.stationManager = stationManager;
Stations = new ObservableCollection<StationVM>();
LoadStations();
}
private async void LoadStations()
{
Stations.Clear();
IEnumerable<Station> stations = await stationManager.GetAllStationsAsync();
IEnumerator<Station> e = stations.GetEnumerator();
while (await Task.Factory.StartNew(() => e.MoveNext()))
{
stationsVM.Add(new StationVM(stationManager, e.Current));
}
Stations = ObservableCollection<StationVM>(stationsVM);
}
public ICommand TextChanged
{
get
{
if (textChanged == null)
{
textChanged = new RelayCommand(args =>
{
if(!string.IsEmpty(args))
{
Stations = staionsVM.Where(x=>x.SomeTextProperty.StartsWith(args));
}
else
{
Stations = ObservableCollection<StationVM>(stationsVM);
}
});
}
return textChanged;
}
}
}

“Items collection must be empty before using ItemsSource.” error wpf

I am trying to bind an observable collection of strings. But when i launch an app, I receive Exception that Items collection must be empty before using ItemsSource. I have no elements in collection when it is binding, so what can be the issue?
My Xaml
<ListBox ItemsSource="{Binding Users}" Margin="10,77,805,228" Grid.RowSpan="2">
<ListBoxItem>
<DataTemplate>
<StackPanel Orientation="Horizontal">
</StackPanel>
</DataTemplate>
</ListBoxItem>
</ListBox>
<Button x:Name="AddUserButton" Content="Додати" Command="{Binding AddUserCommand}" RenderTransformOrigin="0.512,1.9" />
My ViewModel (command and observablecollection)
public class UsersTabViewModel : ViewModelBase
{
private ObservableCollection<string> users;
private string text;
private ICommand addUserCommand;
private bool _canExecute;
public UsersTabViewModel()
{
_canExecute = true;
Users = new ObservableCollection<string>();
}
public ObservableCollection<string> Users { get; set; }
public ICommand AddUserCommand
{
get
{
return addUserCommand ?? (addUserCommand = new CommandHandler(() => AddUserAction(), _canExecute));
}
}
public string Text
{
get
{
return text;
}
set
{
text = value;
}
}
//text is bound to here
private void AddUserAction()
{
Users.Add("collection");
}
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();
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
As the error is trying to tell you, you can't have any items if you use ItemsSource to bind them instead.
Remove your <ListBoxItem>.
To set a template for the bound items, set <ListBox.ItemTemplate>.
I fixed it clearing my ListBox with Items.Clear()

Wpf binding button with listview.Item

Hi i want to bind button with other listView.Item. What i want is to have something like we have on stackoverflow.
But i have problem with having increasing/decreasing value. I have event Click but i dont knew how to get corresponding item on list and increase/decrease value.
<DataTemplate>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label Width="706" Height="75" Content="{Binding feedback}"/>
<StackPanel Orientation="Vertical">
<Button Name="buttonUp" Content="^" Command="{Binding upVoteCommand}" />
<Label HorizontalContentAlignment="Center" Width="50" Content="{Binding grade}"/>
<Button Name="buttonDown" Content="v" Command="{Binding upVoteCommand}"/>
</StackPanel>
</StackPanel>
<Label>-</Label>
</StackPanel >
EDIT
class A {
public string feedback {
get;
set;
}
public int grade {
get;
set;
}
private ICommand _upVoteCommand;
private ICommand _downVoteCommand;
public ICommand upVoteCommand {
get {
return _upVoteCommand;
}
set {
_upVoteCommand = value;
}
}
public ICommand downVoteCommand {
get {
return _downVoteCommand;
}
set {
_downVoteCommand = value;
}
}
}
EDIT I used this button.Commmand but still it not working. I dont knew what to do with this commands.
First you'll need your implementation of ICommand so you can bind commands from view model to controls, something like this:
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private 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;
}
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); }
}
then in you class, where you publish Feedback, you'll need to publish 2 new RelayCommand for up/down vote that will modify Feedback property accordingly. Below you can find my class that I used for tests:
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private int _feedback = 0;
public int Feedback
{
get { return _feedback; }
set
{
if (_feedback == value) return;
_feedback = value;
OnPropertyChanged("Feedback");
}
}
private RelayCommand _upVoteCmd;
public ICommand UpVoteCmd
{
get
{
if (_upVoteCmd == null) _upVoteCmd = new RelayCommand(o => Feedback += 1);
return _upVoteCmd;
}
}
private RelayCommand _downVoteCmd;
public ICommand DownVoteCmd
{
get
{
if (_downVoteCmd == null) _downVoteCmd = new RelayCommand(o => Feedback -= 1);
return _downVoteCmd;
}
}
}
and then you bind your new commands in XAML like this:
<Button Content="+" Command="{Binding Path=UpVoteCmd}"/>
<TextBlock Text="{Binding Path=Feedback}"/>
<Button Content="-" Command="{Binding Path=DownVoteCmd}"/>
RoutedEvents don't work so easily with DataTemplates, because you don't have a code behind where your event code could be placed. While there are ways to do that, you can just use Commands to do the same. In the view model for each item (i just assume you use MVVM) create properties called UpVoteCommand and DownVoteCommand of type ICommand, DelegateCommands are quiet handy for this. Bind them to the Command property and remove the Click handler in your DataTemplate.
[EDIT]
Small example of a possible Viewmodel for one entry in the list, which can be up or downvoted.
class MyEntryViewModel : INotifyPropertyChanged
{
public MyEntryViewModel()
{
UpVoteCommand = new DelegateCommand(OnUpVoteCommand);
}
public int Votes
{
get {return mVotes;}
set {mVotes = value; RaiseProperty("Votes");}
}
public ICommand UpVoteCommand
{
get; private set;
}
void OnUpVoteCommand(object aParameter)
{
Votes++;
}
}
i left the implementation of INotifyPropertyChanged and the down vote command for sake of simplicity.

Categories