Arvind Chourasiya
Arvind Chourasiya

Reputation: 17432

How to get Label view in ViewModel to set accessibility focus in xamarin forms

I have Label in view, I need that Label's view in my ViewModel. I am using Dependency Service to set focus on Controls for Accessibility service, DS requires view as a param.

This is my Label

<Label
    AutomationProperties.IsInAccessibleTree="{Binding ShowNoResults}"
    IsVisible="{Binding ShowNoResults}"
    Text="{Binding ResultsHeader}"/>

I tried Command but Label doesn't support command. Below code also not working

var view = GetView() as HomeworkView;

I am getting view always null. How can I fix this?

Upvotes: 1

Views: 270

Answers (2)

ToolmakerSteve
ToolmakerSteve

Reputation: 21321

More detail added to Mohammad's answer.

Message Center doc.

In your ViewModel (with class name "YourViewModel"):

// Here we use control name directly.
// OR could make an "enum" with a value for each control.
string controlName = ...;
MessagingCenter.Send<YourViewModel>(this, "focus", controlName);

then in your page, subscribe to this message and do the desired action

.xaml.cs:

protected override void OnAppearing() {
{
    base.OnAppearing();

    // Unsubscribe before Subscribe ensures you don't Subscribe twice, if the page is shown again.
    MessagingCenter.Instance.Unsubscribe<YourViewModel>(this, "focus");    
    MessagingCenter.Instance.Subscribe<YourViewModel>(this, "focus", (controlName) =>
    {
        View v = null;
        switch (controlName) {
        case "name1":
            v = this.name1;
            break;
        case "name2":
            v = this.name2;
            break;
        }

        if (v != null) {
            //v.Focus();
            // Tell VM to use v as view.
            ((YourViewModel)BindingContext).SetFocus(v);
        }
    });
}

protected override void OnDisappearing() {
    MessagingCenter.Instance.Unsubscribe<YourViewModel>(this, "focus");    

    base.OnDisappearing();
}

If need to pass View v back to VM, because that has the logic to use it:

public class YourViewModel
{

    public void SetFocus(View view)
    {
       ... your code that needs label's view ...
    }
}

Not tested. Might need some slight changes. Might need

...(this, "focus", (sender, controlName) =>

instead of

...(this, "focus", (controlName) =>

UPDATE

Simple approach, if there is only ONE View that is needed in VM.

public class YourViewModel
{
    public View ViewToFocus { get; set; }

    // The method that needs ViewToFocus.
    void SomeMethod()
    {
        ...
        if (ViewToFocus != null)
            ... do something with it ...
    }
}

public class YourView
{
    public YourView()
    {
        InitializeComponent();
        ...
        // After BindingContext is set.
        ((YourViewModel)BindingContext).ViewToFocus = this.yourLabelThatShouldBeFocused;
    }

}

ALTERNATIVE: It might be cleaner/more robust to set ViewToFocus in page's OnAppearing, and clear it in OnDisappearing. This ensures it is never used while the page is not visible (or in some delayed action after the page has gone away). I would probably do it this way.

protected override void OnAppearing()
{
    base.OnAppearing();
    ((YourViewModel)BindingContext).ViewToFocus = this.yourLabelThatShouldBeFocused;
}

protected override void OnDisappearing()
{
    ((YourViewModel)BindingContext).ViewToFocus = null;
    base.OnDisappearing();
}

Upvotes: 2

Mohammad Shaban
Mohammad Shaban

Reputation: 186

I am not quite sure what are you trying to achieve, but you can't access the View elements from you view model.

If you want to do something with the control, you can use the messaging center to do it, here is an example

in your ViewModel

MessagingCenter.Send(this, "your message here");

then in your page, you need to subscribe to this message from that view model and do the desired action

MessagingCenter.Instance.Unsubscribe<ViewModelClassNamedel>(this, "your message here");    
MessagingCenter.Instance.Subscribe<ViewModelClassName>(this, "your message here", (data) =>
    {
        this.youControlName.Focus();
    });

Upvotes: 3

Related Questions