EventToCommand with Xamarin Forms Behaviors

asimmonasimmon GPMember
edited November 2015 in Xamarin.Forms

I wrote an EventToCommand behavior using Xamarin Forms 1.3 behaviors and I would like to share it with you : anthonysimmon.com/eventtocommand-in-xamarin-forms-apps/

You will find an example where the EventToCommand is bound to the ItemTapped of a ListView so that we can get the tapped item data context inside a command of the ListView data context:

<ListView ItemsSource="{Binding People}">
  <ListView.Behaviors>
    <b:EventToCommandBehavior EventName="ItemTapped" Command="{Binding SayHelloCommand}" EventArgsConverter="{StaticResource ItemTappedConverter}" />
  </ListView.Behaviors>
  <ListView.ItemTemplate>
    <DataTemplate>
      <TextCell Text="{Binding Name}"/>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

Posts

  • adamkempadamkemp mod USInsider, Developer Group Leader mod

    Awesome.

  • ThomasBurkhartThomasBurkhart ✭✭✭✭ DEMember ✭✭✭✭

    @asimmon I tried to use your code to convert a TextChanged Event to a Command, but I get an exception when I assign the bound Property of the Entryfield a value in my ViewModel's Constructor.
    If I don't modify the property in my constructor everything is fine.

    Here is my XAML:

          <Label x:Name="nameEntry" Text="Name" FontSize="Medium"/>
          <Entry  Text="{Binding Name}">
            <Entry.Behaviors>
              <TBGUI:EventToCommand EventName="TextChanged" Command="{Binding NameChangedCommand}"/>
            </Entry.Behaviors>
          </Entry>
    

    And here the constructor:

    c# public IngredientViewModel() { SelectImageCommand = new PageFactoryCommand(SelectImage); SaveCommand = new PageFactoryCommand(Save); NameChangedCommand = new PageFactoryCommand(NameChanged); ImageAvailable = false; SaveEnabled = false; Name = ""; }

    I found out the the exception is thrown in the OnFired Eventhandler because "Command" is null.

    Any Idea what can be the reason?

    Best
    Thomas

  • asimmonasimmon GPMember
    edited January 2016

    You're right, @ThomasBurkhart, looks like the Command is not bound yet when the view model constructor is running (but it is just after, that's why we doesn't have the exception if we don't set the Name property).

    Throwing an exception if a command is not bound yet seems too strict. Maybe it is better to do nothing instead.
    So, in the OnFired method of the EventToCommandBehavior class, replace this part:

    if (Command == null)
        throw new InvalidOperationException("No command available, is Command properly properly set up?");
    

    with:

    if (Command == null)
        return;
    

    Now, if I understand well what you are doing, you might not need to use a Command to detect that the Name has changed.
    You just need to use a two-way binding on the Name property:

    <Entry Text="{Binding Name, Mode=TwoWay}" />

    And use an observable property in your view model:

    public string Name
    {
        get { return _name; }
        set
        {
            if (Set(() => Name, ref _name, value)) // Set() is a method coming from MvvmLightLibs
            {
                // call the NameChanged method
            }
        }
    }
    

    Thanks you for helping me improve this behavior. You might be interested in a Xamarin.Forms MVVM framework that I published recently: https://www.nuget.org/packages/Askaiser.Mobile.Pillar/

    Source code and demos : https://github.com/asimmon/Pillar
    (I will update the EventToCommandBehavior :) in the 0.0.3 release)

    Best,
    Anthony

  • ThomasBurkhartThomasBurkhart ✭✭✭✭ DEMember ✭✭✭✭

    @asimmon Thanks for the tip just to use the property setter. Interestingly I did not have to declare it as Two-Way, but it's updated when I enter data.

  • AnkushSharma.5312AnkushSharma.5312 ✭✭ USMember ✭✭
    edited May 2016

    Hello @asimmon

    Below is fragment of my XAML Page in Xamarin.Forms.Portable Project

     <ListView  BackgroundColor="Green" x:Name="lstVehiclePlatformSelection"  ItemsSource="{Binding VehiclePlatforms}"> 
                <b:Interaction.Behaviors>
                    <b:BehaviorCollection>
                      <b:EventToCommand EventName="ItemSelected" Command="{Binding GoToCarSelectionPageCommand}"              
                         CommandParameter="{x:Reference lstVehiclePlatformSelection}"/> 
                      </b:BehaviorCollection>
                </b:Interaction.Behaviors>
                <ListView.ItemTemplate>
              <DataTemplate>
                <ImageCell 
                  Text="{Binding MainText}"
                  Detail="{Binding DetailText}"
                  TextColor="Blue"
                  DetailColor="Red">
               </ImageCell>       
              </DataTemplate>
            </ListView.ItemTemplate>
          </ListView>
    

    I am using CommandParameter="{x:Reference lstVehiclePlatformSelection}" to fetch the value of selectedItem from ListView

    ViewModel

    public class VehiclePlatformSelectionViewModel : ViewModelBase
    {

        private ICommand _goToCarSelectionPageCommand;
        public ICommand GoToCarSelectionPageCommand
        {
            get { return _goToCarSelectionPageCommand; }
            set
            {
                _goToCarSelectionPageCommand = value;
                this.OnPropertyChanged("GoToCarSelectionPageCommand");
            }
        }
    
    
    
        private readonly INavigation _navi;
        private List<VehicleModelEntity> _components;
    
        public List<VehicleModelEntity> VehiclePlatforms
        {
            get { return _components; }
            set
            { _components = value;
              this.OnPropertyChanged("VehiclePlatforms");
    
            }
        }
    
    
    
        public VehiclePlatformSelectionViewModel(INavigation navi)
        {
    
            _navi = navi;
            this.GoToCarSelectionPageCommand = new RelayCommand(ParamArrayAttribute => this.LoadCarSelection(ParamArrayAttribute));
    
    
            VehiclePlatforms = new List<VehicleModelEntity>()
    
    
            {
               new VehicleModelEntity() { MainText="CARS", DetailText="XYZ"},
               new VehicleModelEntity() { MainText="LCV",  DetailText="PQR" }
    
            };
    
        }
    
        private void LoadCarSelection(Object sender) //fetching the selected value of ListView Cell item 
        {
            ListView ItemClicked = (ListView)(sender);
            _navi.PushAsync(new CarSelection(ItemClicked.SelectedItem.ToString()));
        }
    

    Binding happens at Runtime and at Runtime i get ItemClicked.SelectedItem.MainText i.e "CARS" but how to do that when writing code because intellisense doesnot show ItemClicked.SelectedItem.MainText at compiletime.In this way i am not getting the selectedItem MainText Value of ListView

    Can you help with that?

  • IeuanWalkerIeuanWalker ✭✭ USMember ✭✭
    edited January 2018

    Hi, I'm trying to use this method to get an item switch (toggle event) in a listview using the command and commandParemeter, but I keep hitting the Command == null in the OnEvent Method.

    Xaml =

    <Switch IsToggled="{Binding Enabled}">
        <Switch.Behaviors>
            <Behavior:EventToCommandBehavior EventName="Toggled" Command="{Binding DisableCommand}" CommandParameter="{Binding .}"></Behavior:EventToCommandBehavior>
        </Switch.Behaviors>
    </Switch>
    

    ViewModel =

    public Command DisableCommand => new Command (async x => await DisableReminder((WasteCalenderRemindersTimeDatabaseModel)x));

    private async Task DisableReminder(WasteCalenderRemindersTimeDatabaseModel x)
    {
        await Application.Current.MainPage.DisplayAlert("Disable Reminder", $"Property: {x.PropertyAddress}, DaysBefore: {x.DaysBefore}, Time: {x.Time}", "OK");
    }
    
  • masterkomasterko Member

    Hello did anyone found solution how to apply EventToCommandBehavior to swtich ?

  • BillyMartinBillyMartin ✭✭✭ USMember ✭✭✭

    @masterko , That's what I'm trying to do. Have you had any success?

Sign In or Register to comment.