How to set focus from ViewModel in Xamarin Forms

11,966

Solution 1

One of options is to use Triggers (XAML way):

 <SearchBar x:Name="searchBar"
       Text=""
       Placeholder="type something">
    <SearchBar.Triggers>

        <DataTrigger TargetType="SearchBar"
                     Binding="{Binding ViewModelIsSearchBarFocused}"
                     Value="True">

            <Trigger.EnterActions>
                <local:FocusTriggerAction Focused="True" />
            </Trigger.EnterActions>

            <Trigger.ExitActions>
                <local:FocusTriggerAction Focused="False" />
            </Trigger.ExitActions>

        </DataTrigger>   

        <EventTrigger Event="Unfocused">
            <local:UnfocusedTriggerAction />
        </EventTrigger>    

    </SearchBar.Triggers>       
</SearchBar>
public class FocusTriggerAction : TriggerAction<SearchBar>
{
    public bool Focused { get; set; }

    protected override async void Invoke (SearchBar searchBar)
    {
        await Task.Delay(1000);

        if (Focused)
        {
            searchBar.Focus();
        }
        else
        {
            searchBar.UnFocus();
        }
    }
}

public class UnfocusedTriggerAction : TriggerAction<SearchBar>
{
    protected override void Invoke (SearchBar searchBar)
    {
        YourViewModel.ViewModelIsSearchBarFocused = false;
    }
}

Read more here: https://developer.xamarin.com/guides/cross-platform/xamarin-forms/working-with/triggers/

Solution 2

This is an example of how I do, before this I used to do using MessagingCenter

in xaml , you need to give an x:Name to the obj you want to make focus.

<!-- PICKER's DEFINITION -->
<DatePicker
    x:Name="Datepicker"
    Date="{Binding SelectedDate, Mode=TwoWay}"
    IsEnabled="true"
    IsVisible="false">
</DatePicker>

then you have to make reference to that control in your command parameter on a button or for example in this case I use a toolbar item.

<!-- MENU TOOLBAR -->
<ContentPage.ToolbarItems>
    <ToolbarItem
        Command="{Binding ShowCalendarCommand}"
        Icon="Calendar"
        CommandParameter="{x:Reference Datepicker}" />
</ContentPage.ToolbarItems>

then in your vm command :

#region toolbar commands

public ICommand ShowCalendarCommand => new RelayCommand<Object>(ShowCalendar);

#endregion

private void ShowCalendar(Object obj)
{
    var calendar = (DatePicker)obj;
    calendar.Focus();
    //  MessagingCenter.Send(this, "Calendar");
}
Share:
11,966
Fran_gg7
Author by

Fran_gg7

Updated on July 02, 2022

Comments

  • Fran_gg7
    Fran_gg7 almost 2 years

    I want to set the focus in a SearchBox control after do some asynchronous operations, and I would like to do it from my ViewModel.

    How could I do this possible?

    EDIT

    ViewModel code:

        private bool _searchBarFocused;
    
        public bool SearchBarFocused
        {
            get { return _searchBarFocused; }
            set
            {
                _searchBarFocused = value;
                base.OnPropertyChanged("SearchBarFocused");
            }
        }
    
        public async Task InitializeData()
        {
            // Other operations...
    
            SearchBarFocused = true;
        }
    

    View's code-behind code:

        protected override void OnAppearing()
        {
            base.OnAppearing();
            (this.BindingContext as MyViewModel).InitializeData();
        }
    

    SearchBar XAML code:

      <SearchBar SearchCommand="{Binding SearchItemsCommand}">
        <SearchBar.Triggers>
          <DataTrigger TargetType="SearchBar"
                       Binding="{Binding SearchBarFocused, Mode=TwoWay}" Value="True">
            <Trigger.EnterActions>
              <triggers:SearchBarFocusTriggerAction Focused="True" />
            </Trigger.EnterActions>
    
            <Trigger.ExitActions>
              <triggers:SearchBarFocusTriggerAction Focused="False" />
            </Trigger.ExitActions>
          </DataTrigger>
        </SearchBar.Triggers>
      </SearchBar>
    

    Trigger action code:

    public class SearchBarFocusTriggerAction : TriggerAction<SearchBar>
    {
        public bool Focused { get; set; }
    
        protected override void Invoke(SearchBar searchBar)
        {
            if (Focused)
                searchBar.Focus();
            else
                searchBar.Unfocus();
        }
    }
    
  • Fran_gg7
    Fran_gg7 almost 9 years
    Thanks for your reply, Daniel. I am using your approach in my project and it seemed to work, but the Invoke method is only raised the first time I assign the ViewModelIsSearchBarFocused value to true. The rest of the times this method is not called...do you know why?
  • Daniel Luberda
    Daniel Luberda almost 9 years
    Does your ViewModel implements IOnNotifyPropertyChanged correctly? I'll need your ViewModel code to see why.
  • Fran_gg7
    Fran_gg7 almost 9 years
    Yes, it does. I just have updated the question with the source code I am using. It works the first time the method InitializeData() is called, but not the second...
  • Fran_gg7
    Fran_gg7 almost 9 years
    I think that it is because when the focus is lost in UI, the ViewModelIsSearchBarFocused property is not setted to false. Would it be possible?
  • Daniel Luberda
    Daniel Luberda almost 9 years
    1. Did you try to use Task.Delay before focus? I modified my example. 2. Do you really need Mode=TwoWay in binding?
  • Daniel Luberda
    Daniel Luberda almost 9 years
    Also please note that successful Unfocus/Focus operation returns true if succeeded. Can you check what results do you get for each call?
  • Fran_gg7
    Fran_gg7 almost 9 years
    Daniel, I put beakpoints inside Invoke method but only is called the first time I navigate to the screen. If I navigate to another screen and then go back, the InitializeData method is called, the SearchBarFocused property is setted to true but the Invoke method is not raised... I also have tried without Mode=TwoWay
  • Daniel Luberda
    Daniel Luberda almost 9 years
    Are you sure that before you open a new page SearchBarFocused is set to false? OR Just put SearchBarFocused = true; at the beginning of InitializeData method.
  • Daniel Luberda
    Daniel Luberda almost 9 years
    typo: SearchBarFocused = false; at the beginning of InitializeData method
  • Fran_gg7
    Fran_gg7 almost 9 years
    If I set to false the property SearchBarFocused before leave the page, this works when I go back :-) But...is there any way to automatically set the property SearchBarFocused = false when the focus is missed in UI? (when Unfocus() method is called...) thanks!
  • Daniel Luberda
    Daniel Luberda almost 9 years
    You could use OnAppearing of other page override combined with Xamarin.Forms MessagingCenter or SearchBar.Unfocused event.
  • Fran_gg7
    Fran_gg7 almost 9 years
    And is there possibility to do this with Triggers?
  • Daniel Luberda
    Daniel Luberda almost 9 years
    Why just SearchBarFocused = false; at the beginning of InitializeData method won't work for you? You can add a Trigger which is paired with Searchbar.Unfocused event. Read more here (look for Event Triggers): developer.xamarin.com/guides/cross-platform/xamarin-forms/…
  • Fran_gg7
    Fran_gg7 almost 9 years
    I have readed the full page, but I don't understand how I can do it....could you help me please?
  • Fran_gg7
    Fran_gg7 almost 9 years
    Thanks, but how do I access to my ViewModel from the UnfocusedTriggerAction? In addition, this approach only would work in only a page, it wouldn't be generical :-(
  • Daniel Luberda
    Daniel Luberda almost 9 years
    eg you can pass it the same as I did in FocusTriggerAction with Focused property.
  • Shimmy Weitzhandler
    Shimmy Weitzhandler over 6 years
    Is there a way to raise a datatrigger without setting a property? In my scenario I'd just want to raise an event or call a method in the VM.
  • pixel
    pixel about 5 years
    What if I want to focus back on a view each time I type a character in an Entry for example. Say I use entry or SearchBar to search and on each character entered, I want filtering to happen. But my filtering logic causes the Entry to loose focus, so at the end I want to focus back on that Entry so user can type next character.
  • Chris W
    Chris W over 3 years
    Trigger.EnterActions and Trigger.ExitActions should be renamed to DataTrigger.EnterActions and DataTrigger.ExitActions respectively.
  • Wolfgang Schreurs
    Wolfgang Schreurs about 3 years
    I believe your answer has been downvoted as it's generally considered an ugly approach in context of MVVM. The ViewModel should not know anything about the view, communication between ViewModel and View should be one directional to keep the codebase flexible.