views:

462

answers:

3

I am trying to figure out the best way to select all the text in a TextBox the first time the control is loaded. I am using the MVVM pattern, so I am using two-way binding for the Text property of the TextBox to a string on my ViewModel. I am using this TextBox to "rename" something that already has a name, so I would like to select the old name when the control loads so it can easily be deleted and renamed. The initial text (old name) is populated by setting it in my ViewModel, and it is then reflected in the TextBox after the data binding completes.

What I would really like to do is something like this:

<TextBox x:Name="NameTextBox" Text="{Binding NameViewModelProperty, Mode=TwoWay}" SelectedText="{Binding NameViewModelProperty, Mode=OneTime}" />

Basically just use the entire text as the SelectedText with OneTime binding. However, that does not work since the SelectedText is not a DependencyProperty.

I am not completely against adding the selection code in the code-behind of my view, but my problem in that case is determining when the initial text binding has completed. The TextBox always starts empty, so it can not be done in the constructor. The TextChanged event only seems to fire when a user enters new text, not when the text is changed from the initial binding of the ViewModel.

Any ideas are greatly appreciated!

+3  A: 

Dan,

I wrote a very simple derived class, TextBoxEx, that offers this functionality. The TextBoxEx class derives from TextBox, and can be referenced in XAML for any and all of your TextBox’s. There are no methods to call. It just listens for Focus events and selects it own text. Very simple.

Usage is as follows:

In XAML, reference the assembly where you implement the TextBoxEx class listed below, and add as many TextBoxEx elements as you need. The example below uses data binding to display a username.

<UserControl x:Class="MyApp.MainPage"
    xmlns="http://schemas.microsoft.com/client/2007"     
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"     
    xmlns:c="clr-namespace:ClassLibrary;assembly=ClassLibrary"  
>  
.     
.     
.     
<c:TextBoxEx x:Name="NameTextBox" Text="{Binding NameViewModelProperty, Mode=TwoWay}" Width="120" />

This code below works with Silverlight 3.

using System.Windows;
using System.Windows.Controls;

namespace ClassLibrary
{
    // This TextBox derived class selects all text when it receives focus
    public class TextBoxEx : TextBox
    {
        public TextBoxEx()
        {
            base.GotFocus += OnGotFocus;
        }

        private void OnGotFocus(object sender, RoutedEventArgs e)
        {
            base.SelectAll();
        }
    }
}

Good luck,
Jim McCurdy
Face to Face Software and YinYangMoney

Jim McCurdy
I don't believe subclassing was really necessary for this, but calling SelectAll() on the GotFocus event of the TextBox did achieve what I wanted even with data bound text. I don't see any real reason for it to be a subclass, I just did this in my code-behind and it could easily be refactored into a Blend behavior if I wanted to get more MVVMish. Thanks!
Dan Auclair
A: 

I'm leaving Jim's solution as the answer, since calling SelectAll() on the GotFocus event of the TextBox did the trick.

I actually ended up making a Blend TriggerAction and an EventTrigger to do this instead of subclassing the TextBox or doing it in code-behind. It was really simple to do and nice to be able to keep the behavior logic encapsulated and just add it declaratively in XAML to an existing TextBox.

Just posting this in case anyone else comes across this thread and is interested:

XAML:

<TextBox x:Name="NameTextBox" Text="{Binding NameViewModelProperty, Mode=TwoWay}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="GotFocus">
            <local:SelectAllAction/>
        </i:EventTrigger>
     </i:Interaction.Triggers>
</TextBox>

C#

public class SelectAllAction : TriggerAction<TextBox>
{
    protected override void Invoke(object parameter)
    {
        if (this.AssociatedObject != null)
        {
            this.AssociatedObject.SelectAll();
        }
    }
}
Dan Auclair
Is this for WPF of Silverlight? When I tried to extend TriggerAction with a type, the compiler complained that TriggerAction isn't generic so it can't accept a type.
Trinition
This is for Silveright, I am not sure if Blend triggers and actions are available for WPF. You need to make sure you are inheriting from System.Windows.Interactivity.TriggerAction<> not System.Windows.TriggerAction. You will have to reference the DLL from the Blend SDK.
Dan Auclair
A: 

Just wanna add a link I found pertaining to this - here is a fantastic discussion (read comments) on Behaviours vs subclassing vvs attached properties...

Rodney