Element to support changing focus in MVVM

The other day I moaned that setting focus to a control in an MVVM environment felt like a painful disconnect. There are probably a dozen ways to resolve this issue, but I thought I’d try to use a method that at least isn’t a Behaviour.

Consider the following snippet of XAML

   <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <local:FocusManagerElement ControlName="{Binding SuggestedFocusName}" />
        <StackPanel>
            <TextBox x:Name="Barny" />
            <TextBox x:Name="Fred" />
            <TextBox x:Name="Bert" />
            <Button Content="Test" Tapped="Button_Tapped" />
        </StackPanel>
    </Grid>

The idea is that the FocusManagerElement is bound to the current view model and will set focus to whatever control is named by the View Model via the SuggestedFocusName property. When the logic in the View Model changes and we want to change the focus the we can simply update that property.

    public class TestViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string suggestedFocusName;
        public string SuggestedFocusName
        {
            get { return this.suggestedFocusName; }

            set
            {
                if (this.suggestedFocusName != value)
                {
                    this.suggestedFocusName = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged.Invoke(this, new PropertyChangedEventArgs("SuggestedFocusName"));
                    }
                }
            }
        }

        public void DoWork()
        {
            // do some logic that means we need to change the focus
            this.SuggestedFocusName = "Fred";
        }
    }

From a developers point of view I think that’s pretty easy and flexible to use. All that remains is to show the implementation of the Focus Manager Element itself;

namespace AppFocusManager
{
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;

    /// <summary>
    /// Element to support setting the focus via a bindable object
    /// </summary>
    public class FocusManagerElement : FrameworkElement
    {
        // Using a DependencyProperty as the backing store for ControlName. 
        public static readonly DependencyProperty ControlNameProperty =
            DependencyProperty.Register("ControlName", typeof(string), typeof(FocusManagerElement), new PropertyMetadata(null, new PropertyChangedCallback(ControlNamePropertyChanged)));

        /// <summary>
        /// Name of control to set focus to
        /// </summary>
        public string ControlName
        {
            get { return (string)GetValue(ControlNameProperty); }
            set { SetValue(ControlNameProperty, value); }
        }

        /// <summary>
        /// Control Name has changed
        /// </summary>
        /// <param name="dependencyObject">the instance of FocusManagerElement</param>
        /// <param name="eventArgs">new name of control to set focus to</param>
        private static void ControlNamePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            var focusManagerElement = dependencyObject as FocusManagerElement;
            var controlName = eventArgs.NewValue as string;
            if (!string.IsNullOrEmpty(controlName))
            {
                var elementToFocusOn = focusManagerElement.FindName(controlName) as Control;
                if (elementToFocusOn != null)
                {
                    elementToFocusOn.Focus(FocusState.Programmatic);
                }
            }
        }
    }
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s