Sterno
Sterno

Reputation: 1658

How can I make a textbox select all text after a new item is added to a listbox?

I've got a ListBox on my page which contains the names of some items. To the right of the ListBox is an area that displays information about the selected items.

When the user clicks the "Add Item" button, a new item is created with an auto-generated name, such as "New Item". What I would like to be able to do is immediately after the item is added to the ListBox, focus on the TextBox and select all of the selected text so that if they start typing it immediately replaces what was auto-generated.

Here's an idea of what it would look like:

enter image description here

I've created a behavior which I attached to the ListBox which gives the TextBox focus when a new item is created. However, I'm having a problem when trying to select the text. At the time my behavior executes, the new name is not yet bound. So for example if I have "Item Three" selected and then click the Add Item button, when the behavior executes the text of the text box is still "Item Three", and I select all that, but then the binding of the TextBox updates and changes it to "New Item", and the text is no longer selected.

Here's my Behavior:

public class ListBoxFocusAnotherControlOnAddBehavior : Behavior<ListBox>
    {
        public static readonly DependencyProperty AttachedTextBoxProperty = DependencyProperty.Register("AttachedTextBox", typeof (TextBox),
            typeof (ListBoxFocusAnotherControlOnAddBehavior), new PropertyMetadata(default(TextBox)));

        public TextBox AttachedTextBox
        {
            get { return (TextBox)GetValue(AttachedTextBoxProperty); }
            set { SetValue(AttachedTextBoxProperty, value); }
        }

        protected override void OnAttached()
        {
            ((INotifyCollectionChanged)AssociatedObject.Items).CollectionChanged += onListBoxCollectionChanged;
        }

        private void onListBoxCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                AttachedTextBox.Focus();
                AttachedTextBox.SelectAll();
                // The problem being that at the time of this SelectAll, the TextBox's Text binding hasn't updated yet.
            }
        }
    }

Related XAML:

<ListBox Name="ThingsList" ItemsSource="{Binding Things}" SelectedItem="{Binding SelectedThing}">
    <i:Interaction.Behaviors>
        <behaviors:ListBoxFocusAnotherControlOnAddBehavior AttachedTextBox="{Binding ElementName=ThingNameTextBox}" />
    </i:Interaction.Behaviors>
    <ListBox.ItemTemplate>
        <DataTemplate DataType="{x:Type ThingViewModel}">
            <TextBlock Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

<TextBox Name="ThingNameTextBox"  Text="{Binding Path=SelectedThing.Name, UpdateSourceTrigger=PropertyChanged}" />  

What can I do to get the behavior I'm looking for?

Upvotes: 2

Views: 376

Answers (1)

Sterno
Sterno

Reputation: 1658

I did find a workable solution that involves subscribing to the TextChanged event from the handler, selecting all text when it's handled, and then removing the event, but this feels completely terrible and I'd like something that's less of a hack.

private void onListBoxCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
    {
        AttachedTextBox.Focus();
        AttachedTextBox.TextChanged += AttachedTextBox_TextChanged;
    }
}

private void AttachedTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    AttachedTextBox.SelectAll();
    AttachedTextBox.TextChanged -= AttachedTextBox_TextChanged;
}

Upvotes: 1

Related Questions