How to set focus from ViewModel in Xamarin Forms
Asked Answered
G

3

16

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();
    }
}
Grazynagreabe answered 25/8, 2015 at 14:4 Comment(0)
B
11

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/

Biotin answered 25/8, 2015 at 23:1 Comment(18)
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?Grazynagreabe
Does your ViewModel implements IOnNotifyPropertyChanged correctly? I'll need your ViewModel code to see why.Biotin
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...Grazynagreabe
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?Grazynagreabe
1. Did you try to use Task.Delay before focus? I modified my example. 2. Do you really need Mode=TwoWay in binding?Biotin
Also please note that successful Unfocus/Focus operation returns true if succeeded. Can you check what results do you get for each call?Biotin
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=TwoWayGrazynagreabe
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.Biotin
typo: SearchBarFocused = false; at the beginning of InitializeData methodBiotin
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!Grazynagreabe
You could use OnAppearing of other page override combined with Xamarin.Forms MessagingCenter or SearchBar.Unfocused event.Biotin
And is there possibility to do this with Triggers?Grazynagreabe
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/…Biotin
I have readed the full page, but I don't understand how I can do it....could you help me please?Grazynagreabe
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 :-(Grazynagreabe
eg you can pass it the same as I did in FocusTriggerAction with Focused property.Biotin
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.Devilfish
Trigger.EnterActions and Trigger.ExitActions should be renamed to DataTrigger.EnterActions and DataTrigger.ExitActions respectively.Sclerodermatous
P
4

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");
}
Pugilist answered 5/9, 2017 at 13:4 Comment(1)
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.Mcclanahan
C
0

I resolved my issue by passing contentpage as a parameter in viewmodel

BindingContext = new YourViewModel(this);

and created a property in view model

public ContentPage m_View { get; set; }

and then set the value to this property from constructor

 public YourViewModel(ContentPage view)
    {
        m_View = view;
    }

and then set the focus on my DatePicker by using its name

  var DatePicker = m_View.FindByName("YourDatePickerName") as DatePicker;
  DatePicker.Focus();

my DatePicker code was

 <DatePicker x:Name="YourDatePickerName"
             Date="{Binding SelectedDate}" />
Coalfield answered 15/10, 2019 at 7:29 Comment(1)
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.Far

© 2022 - 2024 — McMap. All rights reserved.