Порядок вкладок WPF с пользовательскими элементами управления? - PullRequest
3 голосов
/ 18 января 2011

У меня есть страница WPF, которая содержит несколько встроенных элементов управления с установленным порядком вкладок.

У меня есть пользовательский элемент управления (NumericSpinner), который содержит: border / grid / text box / 2 кнопки повтора (вверх / вниз).

Два выпуска:

1) когда я нахожусь в текстовом поле для настраиваемого элемента управления селектором, я не могу перейти из него в другие элементы управления на странице. Однако после нажатия на одну из стрелок вверх / вниз я могу перейти к другим элементам управления.

2) Мне не удается перейти в текстовое поле пользовательского элемента управления по порядку. Только после того, как я прокрутил все элементы управления, курсор попадает в текстовое поле (и не может выйти из него).

Контекст:

<ComboBox Margin="97,315,21,0" Name="txtdweldatcdu" Style="{StaticResource fieldComboBoxStyle}" IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left" VerticalAlignment="Top" TabIndex="10" />
    <WpfControls:NumericSpinner Margin="97,338,21,0" Name="txtdweldatpctcomplete" HorizontalAlignment="Left" VerticalAlignment="Top" AllowNegativeValues="True" MaxValue="100" TabIndex="11" />
    <ComboBox Margin="97,363,21,0" Name="txtdweldatclass" Style="{StaticResource fieldComboBoxStyle}" IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left" VerticalAlignment="Top" TabIndex="12" />

Часть пользовательского элемента управления:

 <Border BorderThickness="1" BorderBrush="Gray" Margin="0" HorizontalAlignment="Left" VerticalAlignment="Top" Height="20" Width="117">
        <Grid Margin="0">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="98"/>
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBox Name="valueText" 
                     BorderThickness="0" 
                     Grid.RowSpan="2"
                     Style="{StaticResource spinnerTextBoxStyle}"
                     PreviewKeyDown="valueText_PreviewKeyDown"
                     PreviewTextInput="valueText_PreviewTextInput"
                     TextChanged="valueText_TextChanged"
                     IsReadOnly="{Binding ElementName=Spinner, Path=IsReadOnly}"
                     Text="{Binding ElementName=Spinner, Path=Value, Mode=TwoWay}"
                     KeyboardNavigation.IsTabStop="True"
                     AcceptsTab="True"/>
            <RepeatButton Name="upButton" Style="{StaticResource spinnerRepeatButtonStyle}" Click="upButton_Click"  Grid.Column="1" Grid.Row="0" Height="10" Width="18" VerticalAlignment="Top" HorizontalAlignment="Right" HorizontalContentAlignment="Center">
                <Polygon  HorizontalAlignment="Center" Points="3,2 2,3 4,3"  Fill="Black"  Stretch="Uniform"  Stroke="Black"  StrokeThickness="0" />
            </RepeatButton>
            <RepeatButton Name="downButton" Style="{StaticResource spinnerRepeatButtonStyle}" Click="downButton_Click"  Grid.Column="1" Grid.Row="1" Height="10" Width="18" VerticalAlignment="Top" HorizontalAlignment="Right" HorizontalContentAlignment="Center">
                <Polygon  HorizontalAlignment="Center" Points="2,2 4,2 3,3"  Fill="Black"  Stretch="Uniform"  Stroke="Black"  StrokeThickness="0" />
            </RepeatButton>
        </Grid>
    </Border>

Пользовательский элемент управления состоит из файла xaml и code-behind.

Родительская страница xaml, содержащая все элементы управления, загружается динамически и не содержит кода.

В конструкторе для пользовательского элемента управления в качестве теста я установил следующее:

    valueText.TabIndex = 3;
    this.TabIndex = 3;

В четвертый раз, когда я нажимаю, я на самом деле помещаю курсор в текстовое поле, однако я не могу выйти из него.

Имея это в виду, первым шагом будет создание параметра элемента управления, чтобы я мог передать номер порядка табуляции, который будет установлен в коде элемента управления.

Я создал свойство CustomTabIndex:

/// <summary>
/// Custom tab index property
/// </summary>
public int CustomTabIndex
{
    get { return (int)GetValue(CustomTabIndexProperty); }
    set { SetValue(CustomTabIndexProperty, value); }
}

public static readonly DependencyProperty CustomTabIndexProperty = 
    DependencyProperty.Register("CustomTabIndex", typeof(int), typeof(NumericSpinner));

А в xaml, когда я пытаюсь установить CustomTabIndex = "3", я получаю ошибку:

Свойство 'CustomTabIndex' не было найдено в типе 'NumericSpinner'.

Некоторая помощь будет признательна.

Ответы [ 5 ]

6 голосов
/ 20 января 2011

У меня есть ответ на первый ... В вашем статическом конструкторе для вашего CustomControl добавьте следующее

KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(NumericSpinner), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local));

Это должно позволить вам входить и выходить из элемента управления и устанавливатьиндекс вкладки для каждого из дочерних элементов вашего пользовательского элемента управления.

Что касается вашего второго вопроса, я работаю над выяснением того же.Я думаю, что это связано с тем, что ваш пользовательский элемент управления имеет Focusable = False.Но если установить для этого параметра значение true, элемент управления получит фокус, а не на реальных детей.Я думаю, что может потребоваться обработчик события в вашем CustomControl для события GotFocus.Когда элемент управления получает фокус, выполните

this.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));

, и это должно переместить фокус на дочерние элементы.

Я опубликую продолжение, когда выясню правильный способ выполнения.it.

Обновление

Так что для вашей второй точки.

Убедитесь, что вы установили focusable в false и TabNavigationProperty, как в статическом конструктореваш элемент управления

FocusableProperty.OverrideMetadata(typeof(NumericSpinner), new FrameworkPropertyMetadata(false));
KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(NumericSpinner), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local));

Это позволит элементу управления работать должным образом и соблюдать порядок вкладок на элементе управления, а также всех дочерних элементах.Убедитесь, что в вашем стиле не установлены свойства Focusable или TabNavigation.

Raul

4 голосов
/ 18 января 2011

У меня нет полного решения для вас, но следующие четыре вещи должны быть проверены для циклического табулирования:

  • TabIndex определяет порядок табуляции
  • IsTabStop сообщает, следует ли использовать элемент управления для циклического переключения табуляции
  • FocusManager.IsFocusScope сообщает, создает ли элемент собственную зону фокусировки
  • Фокусируемый говорит, может ли UIElement получить фокус.

Я мог бы представить, что IsFocusScope будет интересным.

0 голосов
/ 16 июня 2016

Ответ HaxElit работает, если мы не хотим использовать метки для фокусировки нашего элемента управления Alt + hotkey следующим образом:

<!-- Alt + C selects numCount -->
<Label Target="{Binding ElementName=numCount}">Elements _Count:</Label>
<local:NumericSpinner x:Name="numCount"/>

Поэтому мое окончательное решение выглядит следующим образом:

static NumericSpinner()
{
    // Next line prevents focusing our wrapper parent control. The problem with that is that
    // it prevents Label controls to select our control by Alt+<hotkey>
    //FocusableProperty.OverrideMetadata(typeof(NumericSpinner), new FrameworkPropertyMetadata(false));

    // Next line specifies that the children controls have their own tab subtree so a deep
    // traversal is performed when our control is focused
    KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(NumericSpinner), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local));
}

// our wrapper control is focused invisibly. We must relocate the focus.
protected override void OnGotFocus(RoutedEventArgs e)
{
    base.OnGotFocus(e);

    // Next line moves the focus either forward (to out first inner child)
    // or backward if Shift+TAB is pressed (to the previous control)
    MoveFocus(new TraversalRequest(Keyboard.IsKeyDown(Key.Tab) && (Keyboard.Modifiers & ModifierKeys.Shift) != 0 ? FocusNavigationDirection.Previous : FocusNavigationDirection.Next));
}
0 голосов
/ 06 декабря 2011

Я пытался использовать событие GotFocus выбранного ответа, но у меня оно не работает, кажется, что он вообще отключает навигацию по вкладкам, даже если первый TabStop действительно выбран. Для меня сработало использование события WindowActivated и использование там той же команды:

MoveFocus(new TraversalRequest(FocusNavigationDirection.First));

Надеюсь, это кому-нибудь поможет.

0 голосов
/ 21 января 2011

Рауль ответил на основную проблему - возможность вставки в текстовое поле пользовательского элемента управления. Вторичная проблема не заключалась в том, чтобы вывести текст из текстового поля.

Проблема с этим элементом управления состояла в том, что его текстовое поле содержит обработчик нажатия клавиш:

PreviewKeyDown="valueText_PreviewKeyDown"

Обработчик позволяет нажимать только определенные клавиши в текстовом поле:

/// <summary>
/// Since this event handler traps keystrokes within the control, in order to facilitate tabbing order, allowing the 
/// tab key press must be enabled
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void valueText_PreviewKeyDown(object sender, KeyEventArgs e)
{ 
    KeyConverter converter = new KeyConverter();
    string key = converter.ConvertToString(e.Key);
    int index = ((TextBox)sender).CaretIndex;
    if (key != null)
    {
        if (AllowNegativeValues && (e.Key == Key.Subtract || e.Key == Key.OemMinus))
        {
            e.Handled = (valueText.Text.Contains('-') || index > 0) == true;
        }
        else if (AllowDecimal && (e.Key == Key.OemPeriod || e.Key == Key.Decimal))
        {
            e.Handled = valueText.Text.Contains('.') == true;
        }
        else
            e.Handled = ((((e.Key >= Key.D0) && (e.Key <= Key.D9) && (e.KeyboardDevice.Modifiers != ModifierKeys.Shift))
                            || ((e.Key >= Key.NumPad0) && (e.Key <= Key.NumPad9) && (e.KeyboardDevice.Modifiers != ModifierKeys.Shift))
                            || e.Key == Key.Left || e.Key == Key.Right
                            || e.Key == Key.Back || e.Key == Key.Delete 
                            || e.Key == Key.Tab) == false);             
    }
    else
        e.Handled = true;
}

Мне просто нужно было добавить допустимое нажатие клавиши TAB, и пользовательский элемент управления работает нормально:

e.Key == Key.Tab
...