在使其父级可见时,如何为孩子设置键盘焦点?

时间:2018-12-19 22:33:19

标签: wpf xaml

我有一个ListBox带有可编辑的项目。首次编辑项目时,编辑控件(在此最小示例中为TextBox)最初具有键盘焦点。第二次编辑项目时,TextBox没有键盘焦点。如果您测试代码,则通过选择项目并按F2或Return将其置于编辑模式。

是否有任何合理且直接的方法使TextBox在可见时始终获得键盘焦点?如果不这样做,是否存在不合理或间接的可靠方式?

始终使用编辑模板是不可行的,因为实际的编辑模板包括许多内容,例如300 px高的ListBox和一千个选项,以及TextBox用来过滤ListBox的内容。我尝试使用 DevExpress GridControl CellTemplate来执行此操作,但是出于各种原因,那是一罐蠕虫。

我交替显示/隐藏两个内容控件的原因是,当我仅将不同的模板交换到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 :(得分:1)

我通常通过以下行为来做到这一点:

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 ... />