Как установить фокус клавиатуры для ребенка, когда его родитель становится видимым? - PullRequest
0 голосов
/ 20 декабря 2018

У меня есть ListBox с редактируемыми элементами.Когда вы редактируете элемент в первый раз, элемент управления редактирования (a TextBox в этом минимальном примере) изначально имеет фокус клавиатуры.При втором редактировании элемента TextBox не имеет фокусировки клавиатуры.Если вы тестируете код, элементы переводятся в режим редактирования, выбирая их и нажимая F2 или Return.

Есть ли какой-нибудь разумный и прямой способ заставить TextBox всегда получать фокус клавиатуры, когда он становится видимым?В противном случае, есть ли необоснованный или косвенный способ, который работает надежно?

Не всегда возможно использовать шаблон редактирования, потому что настоящий шаблон редактирования включает в себя много вещей, таких как высота 300 пикселей ListBox с тысячей опций и TextBox для фильтрации содержимогоиз ListBox.Я пытался сделать это с CellTemplate из DevExpress GridControl , но это было червем по разным причинам.

Причина, по которой я поочередно показываю / скрываю два элемента управления контентом, заключается в том, что когда я просто меняю различные шаблоны в ListBox.ItemTemplate, фокус переходит на окно.

XAML:

<Window.DataContext>
    <local:ViewModel />
</Window.DataContext>
<Grid>
    <ListBox
        ItemsSource="{Binding Items}"
        >
        <ListBox.Resources>
            <DataTemplate x:Key="DisplayTemplate">
                <Label Content="{Binding Value}" />
            </DataTemplate>
            <DataTemplate x:Key="EditTemplate">
                <WrapPanel FocusManager.FocusedElement="{Binding ElementName=TextBox}" Focusable="False">
                    <Label>Editing:</Label>
                    <TextBox Margin="4,2,2,2" Text="{Binding Value}" x:Name="TextBox" />
                </WrapPanel>
            </DataTemplate>
        </ListBox.Resources>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <ContentControl x:Name="Display" Content="{Binding}" ContentTemplate="{StaticResource DisplayTemplate}" />
                    <ContentControl x:Name="Edit" Content="{Binding}" ContentTemplate="{StaticResource EditTemplate}" Visibility="Collapsed" />
                </Grid>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding IsEditing}" Value="True">
                        <Setter TargetName="Edit" Property="Visibility" Value="Visible" />
                        <Setter TargetName="Display" Property="Visibility" Value="Collapsed" />
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ListBox.ItemTemplate>
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
                <EventSetter Event="KeyDown" Handler="ListBoxItem_KeyDown" />
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>
</Grid>

ViewModels.cs

public class ViewModel : ViewModelBase
{
    public ViewModel()
    {
        Items = new ObservableCollection<ItemViewModel>(
            new[] { "ytesadamy", "ugexudunamo", "wovaxatytol", "imuq" }.Select(s => new ItemViewModel() { Value = s }));
    }

    public ObservableCollection<ItemViewModel> Items { get; private set; }
}

public class ItemViewModel : ViewModelBase
{
    #region Value Property
    private String _value = default(String);
    public String Value
    {
        get { return _value; }
        set
        {
            if (value != _value)
            {
                _value = value;
                OnPropertyChanged();
            }
        }
    }
    #endregion Value Property

    #region IsEditing Property
    private bool _isEditing = default(bool);
    public bool IsEditing
    {
        get { return _isEditing; }
        set
        {
            if (value != _isEditing)
            {
                _isEditing = value;
                OnPropertyChanged();
            }
        }
    }
    #endregion IsEditing Property
}

#region ViewModelBase Class
public class ViewModelBase : INotifyPropertyChanged
{
    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    #endregion INotifyPropertyChanged
}
#endregion ViewModelBase Class

1 Ответ

0 голосов
/ 20 декабря 2018

Я обычно делаю это с помощью поведения:

public static class FocusOnVisibleBehavior
{
    public static readonly DependencyProperty FocusProperty = DependencyProperty.RegisterAttached(
        "Focus",
        typeof(bool),
        typeof(FocusOnVisibleBehavior),
        new PropertyMetadata(false, OnFocusChange));

    public static void SetFocus(DependencyObject source, bool value)
    {
        source.SetValue(FocusProperty, value);
    }

    public static bool GetFocus(DependencyObject source)
    {
        return (bool)source.GetValue(FocusProperty);
    }

    private static void OnFocusChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var element = d as FrameworkElement;
        DependencyPropertyChangedEventHandler handler = (sender, args) =>
        {
            if ((bool)args.NewValue)
            {
                // see http://stackoverflow.com/questions/13955340/keyboard-focus-does-not-work-on-text-box-in-wpf
                element.Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(delegate()
                {
                    element.Focus();         // Set Logical Focus
                    Keyboard.Focus(element); // Set Keyboard Focus
                    //element.SelectAll();
                }));

            }
        };

        if (e.NewValue != null)
        {
            if ((bool)e.NewValue)
            {
                element.IsVisibleChanged += handler;
                element.Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(delegate ()
                {
                    element.Focus();         // Set Logical Focus
                    Keyboard.Focus(element); // Set Keyboard Focus
                                             //element.SelectAll();
                }));
            }
            else
            {
                element.IsVisibleChanged -= handler;
            }
        }

        //  e.OldValue is never null because it's initialized to false via the PropertyMetadata()
        //  Hence, the effect here is that regardless of the value that's set, we first add the 
        //  handler and then immediately remove it. 
        //if (e.NewValue != null)
        //{
        //    element.IsVisibleChanged += handler;
        //    element.Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(delegate ()
        //    {
        //        element.Focus();         // Set Logical Focus
        //        Keyboard.Focus(element); // Set Keyboard Focus
        //                                 //element.SelectAll();
        //    }));
        //}
        //if (e.OldValue != null)
        //    element.IsVisibleChanged -= handler;
    }

Не могу вспомнить, написал ли я этот код сам или получил его откуда-то еще, в любом случае вы используете его так:

<TextBox behaviors:FocusOnVisibleBehavior.Focus="True" ... etc ... />
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...