Question

I have a headered items control bound to an observable collection. The items in the collection are associated with a data template. The data template is pretty simple in that it only contains two text boxes. The part I am having difficulty with is setting focus to the text boxes programatically.

When the items control is first displayed it always has one item in it. It may or may not have a value that is bound to the first text box. If it has a value, the focus needs to be set to the second text box. I've done this using the following:

    protected override void OnActivated(EventArgs e)
    {
        base.OnActivated(e);

        if (_activated) return;

        _activated = true;

        var dc = DataContext as MyViewModel;
        if (null != dc)
        {

            var cp = theItemsControl.ItemContainerGenerator.ContainerFromIndex(0) as ContentPresenter;
            var tb = FindVisualChildren<TextBox>(cp);
            if (tb != null)
            {
                if (!String.IsNullOrEmpty(dc.TheCollection.First().FirstValue))
                {
                    var t = tb.FirstOrDefault(box => box.Name == "SecondTextBox");
                    if (null != t) t.Focus();
                }
                else
                {
                    var t = tb.FirstOrDefault(box => box.Name == "FirstTextBox");
                    if (null != t) t.Focus();
                }
            }
        }
    }

This seems to work fine. However, now, when a new item is added to the collection I need to set focus to the FirstTextBox. I've tried setting focus in the datatemplate using the FocusManager, but that prohibits the above code from working correctly. I've also tried the following in the view. The method gets called from the ViewModel after the new item has been added to the ObservableCollection. The code runs, but it fails to find any textboxes (ie. tb == null). Have the controls been created at this point?

    public void SetFocusToNewItem(int index)
    {
        var dc = DataContext as MyViewModel;
        if (null != dc)
        {

            var cp = theItemsControl.ItemContainerGenerator.ContainerFromIndex(index) as ContentPresenter;
            var tb = FindVisualChildren<TextBox>(cp);
            if (tb != null)
            {
                  var t = tb.FirstOrDefault(box => box.Name == "FirstTextBox");
                  if (null != t) t.Focus();
            }
        }
    }

Are there other solutions to set focus after the item has been added?

Was it helpful?

Solution

Sometimes the following trick works for me:

// Replace such call:
SetFocusToNewItem(newIndex);

// By this:
Dispatcher.BeginInvoke((Action) (() =>
{
    SetFocusToNewItem(newIndex);
}));

The problem is when you add an item in collection UI elements is not created, so you can't get it. This code above delays your function executing and you method should be executed after UI is created.

PS: You can move Dispatcher call inside of your method.

EDIT:

This may help too (works for me):

var cp = theItemsControl.ItemContainerGenerator.ContainerFromIndex(index) as ContentPresenter;
cp.Loaded += (s, e2) => 
            {
                var tb = FindVisualChildren<TextBox>(e2.Source as UIElement)
                         .FirstOrDefault();
                if (tb != null)
                    tb.Focus();
            };

The reason is the same: UI element was not created.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top