Focus a TextBox from ViewModel using a simple behavior

Focus a TextBox from ViewModel using a simple behavior

Let's assume a simple scenario - you have a Windows Phone page connected to a ViewModel (VM) with everything bound nicely. A TextBox text property is connected to a string in VM, and when you change it, you wish to put focus on the TextBox. How to accomplish this considering that all your logic is actually in the VM separated from the View? Simply, using a focus behavior.

Let's start with a simple UI. We have a MainPage with a TextBox and a Button in the main Grid, and the DataContext bound to a MainViewModel object.

<phone:PhoneApplicationPage x:Class="FocusBehaviorSample.MainPage"  
                            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                            xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
                            xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
                            DataContext="{StaticResource MainViewModelContext}"
                            FontFamily="{StaticResource PhoneFontFamilyNormal}"
                            FontSize="{StaticResource PhoneFontSizeNormal}"
                            Foreground="{StaticResource PhoneForegroundBrush}"
                            Orientation="Portrait"
                            SupportedOrientations="Portrait"
                            shell:SystemTray.IsVisible="True"
                            mc:Ignorable="d">

    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <StackPanel x:Name="TitlePanel"
                    Grid.Row="0"
                    Margin="12,17,0,28">
            <TextBlock Margin="12,0"
                       Style="{StaticResource PhoneTextNormalStyle}"
                       Text="BEHAVE" />
            <TextBlock Margin="9,-7,0,0"
                       Style="{StaticResource PhoneTextTitle1Style}"
                       Text="nicely" />
        </StackPanel>

        <StackPanel x:Name="ContentPanel"
                    Grid.Row="1"
                    Margin="12,0,12,0">
            <TextBox Name="SomeTextBox"
                     Height="100"
                     Text="{Binding Message}" />
            <Button Command="{Binding ChangeTextCommand}" Content="Change text" />
        </StackPanel>
    </Grid>

</phone:PhoneApplicationPage>  

The MainViewModel is created in App.xaml. The code behind MainPage is completely clean:

using Microsoft.Phone.Controls;

namespace FocusBehaviorSample  
{
    public partial class MainPage : PhoneApplicationPage
    {
        public MainPage()
        {
            InitializeComponent();
        }
    }
}

The MainViewModel is also very simple:

using FocusBehaviorSample;  
using System.ComponentModel;  
using System.Windows.Input;

namespace FocusBehaviorSample.ViewModels  
{
    public class MainViewModel : INotifyPropertyChanged
    {
        private string _message;

        public MainViewModel()
        {
            ChangeTextCommand = new DelegateCommand(() => Message = "Hello dear, how are you?");
        }

        public string Message
        {
            get
            {
                return _message;
            }
            set
            {
                _message = value;
                NotifyChange("Message");
            }
        }

        public ICommand ChangeTextCommand { get; private set; }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyChange(string propertyName)
        {
            var eventHandler = this.PropertyChanged;
            if (eventHandler != null)
            {
                eventHandler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

where DelegateCommand is an open source implementation available in a bunch of libraries online, for example see here: http://www.geekchamp.com/articles/building-a-reusable-icommand-implementation-for-windows-phone-mango-mvvm-apps

When you put it all together, you get the following UI. When you click on the button, the TextBox gets filled with some arbitrary text:

Focus behavior app

Obviously, if this was a text messaging app which makes it easier for you to get started with simple text messages for different occasions (such as "happy birthday wishes" etc.), then you'd probably want to edit it or continue writing once the draft part was inside the TextBox. How do you put focus on the TextBox after the text in the TextBox gets updated? Or even better, how do you put focus and go to the end of the text? That's where the focus behavior comes in handy!

Behave and focus!

Behavior is a concept introduced on XAML platforms which makes it simple to add a certain functionality to a control in a clean and reusable way. Think about focusing a TextBox when text property changes. It's really a simple task that can easily be accomplished in code behind. Just change the text property and call Focus() method. But what if you wanted every TextBox to behave this way? What if you wanted to avoid any code behind? What if you wanted it to seem like that is a default behavior of the control? Then simply write a focus behavior and attach it to every TextBox in XAML.

How? First add the System.Windows.Interactivity reference to your project:

Focus behavior TextBox

Then define a focus behavior class called FocusBehavior which inherits from generic Behavior class:

public class FocusBehavior : Behavior<TextBox>  
    {
        protected override void OnAttached()
        {
            AssociatedObject.GotFocus += (sender, args) => IsFocused = true;
            AssociatedObject.LostFocus += (sender, a) => IsFocused = false;
            AssociatedObject.TextChanged += (sender, a) =>
            {
                if (!IsFocused)
                {
                    AssociatedObject.Focus();
                    AssociatedObject.Select(AssociatedObject.Text.Length, 0);
                }
            };
            base.OnAttached();
        }

        public static readonly DependencyProperty IsFocusedProperty =
            DependencyProperty.Register(
                "IsFocused",
                typeof(bool),
                typeof(FocusBehavior),
                new PropertyMetadata(false));

        public bool IsFocused
        {
            get { return (bool)GetValue(IsFocusedProperty); }
            set { SetValue(IsFocusedProperty, value); }
        }
    }

And then add it to the TextBox in MainPage.xaml

...
<TextBox Name="SomeTextBox"  
         Height="100"
         Text="{Binding Message}">
     <interactivity:Interaction.Behaviors>
         <behaviors:FocusBehavior />
     </interactivity:Interaction.Behaviors>
</TextBox>  
...

interactivity and behaviors are namespaces added to the beginning of the MainPage, with all the other namespaces:

xmlns:behaviors="clr-namespace:FocusBehaviorSample.Behaviors"  
xmlns:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"  

This basically adds the bool IsSelected property to TextBox which is false by default. When the GotFocus event gets raised, IsSelected is changed to true, and the opposite happens when LostFocus gets raised. When Text property gets changed, we check if the TextBox is already selected. If not, we select it and go to the end of it using the Select method. And that's it! No code behind, it's reusable in your other projects, it's clean and simple. The end result is almost the same as before, only the TextBox is properly focused! :)

Focus behavior TextBox focused

Hope this helps! :)

Igor Ralic

igor ralic

View Comments
Microsoft Certified Solutions Developer: Windows Store Apps in C#