简体   繁体   中英

user control loses keyboard focus

I made a user control, which I called InputTextBox, that present text which the user can edit only after clicking the control:

<Grid>  
    <TextBox Name="box"
             Text="{Binding RelativeSource={RelativeSource AncestorType=local:InputTextBlock}, Path=Text, Mode=TwoWay}"
             Visibility="Hidden"
             LostKeyboardFocus="box_LostKeyboardFocus"
             KeyDown="box_KeyDown"/>
    <Button Name="block"
            Background="Transparent"
            BorderThickness="0"
            Click="block_Click">
        <Button.Content>
            <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=local:InputTextBlock}, Path=Text}" />
        </Button.Content>
    </Button>
</Grid>

When the user click the button the following callack is used:

    private void block_Click(object sender, RoutedEventArgs e)
    {
        StartEdit();
    }

    public void StartEdit()
    {
        box.Visibility = Visibility.Visible;
        block.Visibility = Visibility.Hidden;

        box.CaretIndex = box.Text.Length;

        Keyboard.Focus(box);
    }

There are two more important events that are handled within the control. The first is when the control loses keyboard focus:

private void box_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
    {
        box.Visibility = Visibility.Hidden;
        block.Visibility = Visibility.Visible;
    }

And the other is when the user presses the TAB or ENTER keys:

private void box_KeyDown(object sender, KeyEventArgs e)
    {
        Key key = e.Key;

        if (key == Key.Enter || key == Key.Tab)
        {
            RoutedEventArgs args = new RoutedEventArgs(KeyExitEvent, this, e.Key);

            RaiseEvent(args);
        }
    }

This raises a simple routed event I registered for this control called KeyExit.

So, basically, it's like this control has two "modes": "edit mode" which the user can activate by simply clicking on the control, and "view mode", which the user can return to by giving any other control in the UI keyboard focus.

In my UI I have a stack panel with a bunch of these controls in it - each one is wrapped inside a class I created that is similar in concept to ListViewItem. The idea is that when the user is in edit mode inside one of the items in the stack panel and click the TAB or ENTER key, the next control in the panel will get into edit mode.

So, I have the following event callback:

private void item_KeyExit(object sender, RoutedEventArgs e)
    {
        FrameworkElement obj = e.OriginalSource as FrameworkElement;

        if (obj != null)
        {
            var listItem = VisualTreeHelperUtils.FindFirstAncestorOfType<MyListItem>(obj);

            if (listItem != null)
            {
                int itemIndex = stackPanelList.Children.IndexOf(listItem);

                MyListItem nextItem = null;

                if (itemIndex == ucSortableList.Children.Count - 1)
                {
                    nextItem = stackPanelList.Children[itemIndex+1] as MyListItem;
                }

                if (nextItem != null)
                {
                    var item = nextItem.DataContent; // property I made in MyListItem that gives access to the class it wraps

                    InputTextBlock block = item as InputTextBlock;
                    if (block != null)
                    {
                        block.StartEdit();
                    }
                }

            }
        }
    }

Everything is called properly, but immediately after all of it is done, the previous item, which I tabbed out of, gets the keyboard focus back and cause no item in the stack to be in edit mode. So, if, for example, the first item in the stack was in edit mode, once the user hit the tab key, the second item in the stack gets into edit mode, but then the first item gets keyboard focus back immediately. Can anybody understand why this is happening?

StackPanel Focusable is false by default. Suggest ensure it's true.

Also for checking indeces, one generally wants the index to be < count. So your index + 1 could == count if it's == count - 1. Don't you want:

if (itemIndex < ...Count - 1)
That way, item index + 1 will always be <= Count - 1 ?

if (itemIndex == ucSortableList.Children.Count - 1) { nextItem = stackPanelList.Children[itemIndex+1] as MyListItem; }

Also you can use box.Focus() instead of Keyboard.Focus(box) but that may not be necessary.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM