0

I have a TextBox inside a ListView. I dynamically add ListView items on KeyDown event (on adding the new item to my observable collection a new ListView Item is created due to Two-Way Binding).

Now when a new element is added I want to set focus to the TextBox of newly created ListView item. It is a bit complex than I thought, Help me solve this problem.

Martin Zikmund
  • 38,440
  • 7
  • 70
  • 91
Prithvi Venu
  • 264
  • 3
  • 16

1 Answers1

0

I originally thought it could be possible to use ListView.ContainerFromItem to retrieve the newly added item and then use VisualTreeHelper to search for the TextBox within the template and focus it. It turns out however, that this solution does not work. After adding the item, the container for the list item is not materialized immediately (which is logical, as the control just got the notification about the collection change, but didn't have any time to build up the control hierarchy yet, as our code is still executing).

In fact, the problem has a far simpler solution. You can use the Loaded event on the TextBox within the template. This will be called only once, when the template is first materialized. This is not a perfect solution however, see update below.

In my example I have the following template:

<DataTemplate>
    <Grid>
        <TextBox Loaded="InputTextBox_Loaded" />
    </Grid>
</DataTemplate>

And in the code-behind:

private void InputTextBox_Loaded(object sender, RoutedEventArgs e)
{
    var textBox = (TextBox)sender;
    textBox.Focus(FocusState.Programmatic);
}

Update: Virtualization

Turns out there is a catch - virtualization creates a number of copies of the template in memory (depending on the window size) to allow for comfortable scrolling, but after that it will just reuse the existing controls - and the Loaded event will never be called again - that's a problem. Luckily, we can solve this as well - instead of the Loaded event we will use DataContextChanged.

Updated XAML:

<DataTemplate>
    <Grid >
        <TextBox DataContextChanged="TextBox_DataContextChanged" />
    </Grid>
</DataTemplate>

Updated code-behind:

private void TextBox_DataContextChanged(
     FrameworkElement sender, 
     DataContextChangedEventArgs args)
{
    var textBox = (TextBox)sender;
    if ( args.NewValue == Items.Last())
    {
        //last item, focus it
        textBox.Focus(FocusState.Programmatic);
    }
}

Ok, that is better, we are getting there! Only one thing left to make it perfect. The current configuration means that once we scroll the last item into view, it will always get focus, which might not be what we want. Instead, we probably want this to happen only once - when the item is newly added. We can do so by adding a bool flag which we set to true when adding a new item into the collection and flip back to false when we focus it the first time:

//set this to true when a new item is being added to the collection
private bool _focusItem = true;

private void TextBox_DataContextChanged(
     FrameworkElement sender, 
     DataContextChangedEventArgs args)
{
    var textBox = (TextBox)sender;
    if (args.NewValue == Items[Items.Count - 1] && _focusItem)
    {
        //last item, focus it
        textBox.Focus(FocusState.Programmatic);
        _focusItem = false;
    }
}

Result

Martin Zikmund
  • 38,440
  • 7
  • 70
  • 91
  • 1
    @PrithviVenu Realized there is one more problem with my solution, I will update my answer in about 10 minutes – Martin Zikmund Aug 26 '19 at 06:35
  • 1
    @PrithviVenu Updated my answer with what I believe is a more general solution. Probably could still be improved a bit - for example scrolling the new item into view to make sure the focus is done and `_focusItem` is set back to `false`. See this question for more info: https://stackoverflow.com/questions/43781501/scroll-to-new-item-in-listview-for-uwp – Martin Zikmund Aug 26 '19 at 06:50
  • 1
    I tried ur solution But ur updated one doesnt work for me – Prithvi Venu Aug 26 '19 at 11:38
  • The data context changed is called the only first time and also the focus doesn't change, it stays in the same first textbox – Prithvi Venu Aug 26 '19 at 11:48
  • That is unusual, could you create a sample repo on GitHub so I can take a look? I have tried it locally and it worked well – Martin Zikmund Aug 26 '19 at 12:05
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/198489/discussion-between-prithvi-venu-and-martin-zikmund). – Prithvi Venu Aug 26 '19 at 13:04