как сфокусироваться на клавиатуре в текстовом поле в WPF listview после item.refresh - PullRequest
0 голосов
/ 09 июня 2019

У меня есть окно WPF со списком с текстовыми полями в нем, и я хочу, чтобы пользователи могли переключаться между текстовыми полями с помощью клавиши TAB. Я создал функцию, которая делает это, но каждый раз, когда текстовое поле теряет фокус, я обновляю представление списка, и поэтому само представление списка фокусируется.

Я знаю, что проблема возникает из-за события обновления, потому что комментирование этой части приведет к тому, что правильный элемент (текстовое поле) будет сфокусирован. Я пробовал много альтернативных решений, но ни одно из них не работает ( предотвращает потерю фокуса при обновлении представления списка в c # ; Фокус на TextBox после события ListView 'SelectionChanged' ).

Кажется, есть что-то проблематичное в фокусировке на этом элементе после обновления списка.

Я попытался запомнить индекс предмета и затем сосредоточиться на нем после нажатия клавиши TAB. попытался также запомнить сфокусированный элемент управления и затем сфокусироваться на нем после обновления.

    private void RTB_Reference_LostFocus(object sender, RoutedEventArgs e)
    {
        int Index = DetailsList.SelectedIndex;
        Index = DetailsList.Items.IndexOf(DetailsList.SelectedItem);

        try
        {
            //Get cell value by using sender Object
            string inTime = ((System.Windows.Controls.TextBox)sender).Text;

            DetailItem item = (DetailItem)DetailsList.Items[Index];
            item.Reference = inTime;

            UpdateExplanation(item);

        }

        catch (Exception)
        {

        }
    }

    private void RTB_Detail_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
    {
        if (e.Key == Key.Tab)
        {
            e.Handled = true;
            //System.Windows.MessageBox.Show("Tab");
            int Idx = DetailsList.SelectedIndex;
            System.Windows.Controls.ListViewItem lvi = (System.Windows.Controls.ListViewItem)DetailsList.ItemContainerGenerator.ContainerFromItem(DetailsList.SelectedItem);
            GUF.FocusItem(DetailsList, Idx, "RTB_Detail");
            //IsLastKeyTAB = true;
        }
        //else
        //    IsLastKeyTAB = false;

    }

    private void UpdateExplanation(DetailItem item)
    {

        item.Explanation = GetExplanation(item.Reference, item.Detail);

        IInputElement focusedControl = Keyboard.FocusedElement;
        DetailsList.Items.Refresh();
        focusedControl.Focus();

        RefreshDetailsList(DetailsList, IsEnglish);


    }

Ожидаемый результат - сохранять фокус на этом текстовом поле после обновления. этого не происходит ...

РЕДАКТИРОВАТЬ 1

Это список просмотра xaml:

    <ListView FlowDirection="RightToLeft" x:Name="DetailsList" VirtualizingStackPanel.IsVirtualizing="False" 
        HorizontalAlignment="Stretch" VerticalAlignment="Top"
        HorizontalContentAlignment="Center" ScrollViewer.VerticalScrollBarVisibility="Visible" Padding="0 0 0 25"
        AllowDrop="True"
        ItemsSource="{Binding DetailItem}"
        Loaded="ListView_Loaded"
        Margin="26,157,23,0"
        dd:DragDrop.IsDragSource="True"
        dd:DragDrop.IsDropTarget="True"
        dd:DragDrop.DropHandler="{Binding}" Height="599">


        <ListView.ItemContainerStyle>
            <Style TargetType="{x:Type ListViewItem}">
                <Setter Property="BorderThickness" Value="0" />
                <Setter Property="Foreground" Value="Black" />
                <Setter Property="Margin" Value="4, 4, 4, 4"/>
                <Setter Property="FontWeight" Value="DemiBold"/>
                <Setter Property="Height" Value="22"/>
                <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListViewItem}">
                            <Border BorderBrush="Transparent" BorderThickness="0" Background="{TemplateBinding Background}">

                                <GridViewRowPresenter HorizontalAlignment="Stretch" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Width="Auto" Margin="0" Content="{TemplateBinding Content}"/>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>

                <Style.Triggers>

                    <Trigger Property="IsMouseOver" Value="True">
                        <!--   <Setter Property="Background" Value="#6B54FF"/> -->
                        <Setter Property="Foreground" Value="#6B57FF"/>
                        <Setter Property="BorderThickness" Value="2" />
                    </Trigger>

                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="BorderThickness" Value="2" />
                        <Setter Property="BorderBrush" Value="#6B57FF"/>
                        <Setter Property="Foreground" Value="#6B57FF" />
                    </Trigger>
                </Style.Triggers>

            </Style>
        </ListView.ItemContainerStyle>




        <ListView.View>
            <GridView>

                <GridViewColumn Width="50" Header="סימון" DisplayMemberBinding="{Binding Mark}"/>

                <!--
                                <GridViewColumn Width="30" >
                                    <GridViewColumn.CellTemplate>
                                        <DataTemplate >
                                            <Button Style="{StaticResource PlusButtonStyle}" x:Name="buttonPlusDocument" Click="buttonPlusDocument_Click"  />
                                        </DataTemplate>
                                    </GridViewColumn.CellTemplate>
                                </GridViewColumn>
                                -->
                <GridViewColumn Header="הפניה במסמך" Width="150">
                    <GridViewColumn.CellTemplate>

                        <DataTemplate>

                            <TextBox x:Name="RTB_Reference" BorderBrush="#5f27ff" BorderThickness="1" KeyDown="RTB_Reference_KeyDown" HorizontalAlignment="Stretch" Height="20" Margin="0" Padding="0" FontSize="12" IsEnabled="True" 
                                LostFocus="RTB_Reference_LostFocus" GotFocus="RTB_Reference_GotFocus">

                                <TextBox.Resources>
                                    <Style TargetType="{x:Type Border}">
                                        <Setter Property="CornerRadius" Value="2"/>
                                        <Setter Property="BorderBrush" Value="#5f27ff"/>
                                        <Setter Property="BorderThickness" Value="1" />
                                    </Style>
                                </TextBox.Resources>
                            </TextBox>
                            <!--DataContext="{Binding SelectedItem, ElementName=ListViewAppendixNameList, Mode=TwoWay}"-->

                        </DataTemplate>

                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                <GridViewColumn Header="פרט" Width="150">
                    <GridViewColumn.CellTemplate>

                        <DataTemplate>

                            <TextBox x:Name="RTB_Detail" BorderBrush="#5f27ff" BorderThickness="1" HorizontalAlignment="Stretch" Height="20" Margin="0" Padding="0" FontSize="12" IsEnabled="True" 
                                KeyDown="RTB_Detail_KeyDown" LostFocus="RTB_Detail_LostFocus" GotFocus="RTB_Detail_GotFocus">

                                <TextBox.Resources>
                                    <Style TargetType="{x:Type Border}">
                                        <Setter Property="CornerRadius" Value="2"/>
                                        <Setter Property="BorderBrush" Value="#5f27ff"/>
                                        <Setter Property="BorderThickness" Value="1" />
                                    </Style>
                                </TextBox.Resources>
                            </TextBox>
                            <!--DataContext="{Binding SelectedItem, ElementName=ListViewAppendixNameList, Mode=TwoWay}"-->

                        </DataTemplate>

                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                <GridViewColumn Header="הסבר" Width="350"  DisplayMemberBinding="{Binding Explanation}"/>



                <GridViewColumn Width="30" >
                    <GridViewColumn.CellTemplate>

                        <DataTemplate >

                            <Button Style="{StaticResource DeleteButtonStyle}" x:Name="BT_DeleteDetail" Click="BT_DeleteDetail_Click"  />

                        </DataTemplate>

                    </GridViewColumn.CellTemplate>
                </GridViewColumn>


            </GridView>
        </ListView.View>
    </ListView>

Это класс деталей:

    public class DetailItem
    {
        public string Mark { get; set; }
        public string Reference { get; set; }
        public string Detail { get; set; }
        public string Explanation { get; set; }
    }

Я обновляю просмотр списка, чтобы текст объяснения обновлялся.

1 Ответ

0 голосов
/ 11 июня 2019

Я прошёл не весь ваш код, а только те части, которые имеют отношение к вашему вопросу. Для упрощения я буду использовать прикрепленное свойство TabNavigation и функцию привязки данных . Привязка автоматически обновит управляющие значения, так что обновление ListView и обработка фокуса станут избыточными.

Примечание. Чтобы сократить код XAML, я покажу только соответствующие части кода.

Вкладка навигации

Установите прикрепленное свойство KeyboardNavigation.TabNavigation на ListView на Continue, чтобы фокус проходил по списку:

  <ListView x:Name="DetailsList"
            KeyboardNavigation.TabNavigation="Continue"
            ...
  </ListView>

Вы можете контролировать, какие элементы могут получать фокус на нажатой клавише Tab, установив свойство IsTabStop для элементов. Значение по умолчанию True. Поэтому установите False для тех элементов, которые вы хотите исключить. Полезно отключить фокус Tab на самом ListViewItem, чтобы клавиша Tab действовала только для содержащих элементы управления:

    <ListView.ItemContainerStyle>
      <Style TargetType="{x:Type ListViewItem}">
        <Setter Property="IsTabStop"
                Value="False" />
        ...
      </Style>
    </ListView.ItemContainerStyle>

Если вы также хотите исключить Button, чтобы переходить только между элементами TextBox, установите для свойства Button s IsTabStop также False:

        <GridViewColumn Width="30">
          <GridViewColumn.CellTemplate>
            <DataTemplate>
              <Button x:Name="BT_DeleteDetail"
                      IsTabStop="False" 
                      ...
              />
              ...
            </DataTemplate>
          </GridViewColumn.CellTemplate>
        </GridViewColumn>

Если вы также хотите изменить порядок, в котором элементы получат фокус при нажатии клавиши Tab, вы можете сделать это, установив свойство элемента TabIndex. Элемент управления с более низким индексом вкладки получает фокус перед элементом управления с более высоким индексом.

Связывание TextBox

Чтобы включить привязку к модели данных, требуется, чтобы модель данных (модель представления) реализовала INotifyPropertxChanged:

public class DetailItem : INotifyPropertyChanged
{
  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
    this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }

  public event PropertyChangedEventHandler PropertyChanged;

  private string mark;
  public string Mark
  {
    get => this.mark;
    set
    {
      if (value == this.mark) return;
      this.mark = value;
      OnPropertyChanged();
    }
  }

  private string reference;
  public string Reference
  {
    get => this.reference;
    set
    {
      if (value == this.reference) return;
      this.reference = value;
      OnPropertyChanged();
    }
  }

  private string detail;
  public string Detail
  {
    get => this.detail;
    set
    {
      if (value == this.detail) return;
      this.detail = value;
      OnPropertyChanged();
    }
  }

  private string explanation;
  public string Explanation
  {
    get => this.explanation;
    set
    {
      if (value == this.explanation) return;
      this.explanation = value;
      OnPropertyChanged();
    }
  }
}

Чтобы получить доступ к модели вида из вашего DataTemplate, установите в свойстве DataType тип модели представления (DetailItem). Не забывайте всегда устанавливать DataType из DataTemplate. Затем добавьте Binding к TextBox. Обратите внимание, что Mode (направление) переплета установлено на OneWayToSource. Это ограничивает привязку для установки данных только с TextBox до DetailItem:

          <GridViewColumn.CellTemplate>
            <DataTemplate DataType="viewModels:DetailItem">
              <TextBox x:Name="RTB_Reference"
                       Text="{Binding Reference, Mode=OneWayToSource}"
                       ... 
              </TextBox>
              ...
            </DataTemplate>
          </GridViewColumn.CellTemplate>

          <GridViewColumn.CellTemplate>
            <DataTemplate DataType="viewModels:DetailItem">
              <TextBox x:Name="RTB_Detail"
                       Text="{Binding Detail, Mode=OneWayToSource}"
                       ... 
              </TextBox>
              ...
            </DataTemplate>
          </GridViewColumn.CellTemplate>

Последний шаг - обновить свойство DetailItem.Explanation. Для этого мы перемещаем метод UpdateExplanation() в модель представления DetailItem. Поскольку мы используем связывание, теперь мы можем избавиться от всей логики обновления и фокусировки ListView, поэтому метод становится меньше. Обратите внимание, что, поскольку мы переместили метод в класс DetailItem, мы также можем удалить параметр метода:

private void UpdateExplanation()
{
  this.Explanation = GetExplanation(this.Reference, this.Detail);
}

Метод UpdateExplanation() вызывается непосредственно из установщика свойства Reference и Detail, поэтому при каждом изменении этих свойств значение Explanation будет обновляться. С обновленным установщиком Reference и Detail окончательная версия класса DetailItem будет выглядеть следующим образом:

public class DetailItem : INotifyPropertyChanged
{
    private void UpdateExplanation()
    {
      this.Explanation = GetExplanation(this.Reference, this.Detail);
    }

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
      this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private string mark;
    public string Mark
    {
      get => this.mark;
      set
      {
        if (value == this.mark) return;
        this.mark = value;
        OnPropertyChanged();
      }
    }

    private string reference;
    public string Reference
    {
      get => this.reference;
      set
      {
        if (value == this.reference) return;
        this.reference = value;
        OnPropertyChanged();
        UpdateExplanation();
      }
    }

    private string detail;
    public string Detail
    {
      get => this.detail;
      set
      {
        if (value == this.detail) return;
        this.detail = value;
        OnPropertyChanged();
        UpdateExplanation();
      }
    }

    private string explanation;
    public string Explanation
    {
      get => this.explanation;
      set
      {
        if (value == this.explanation) return;
        this.explanation = value;
        OnPropertyChanged();
      }
    }
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...