首页 > 解决方案 > item.refresh 之后如何在 WPF listview 中的文本框上专注于键盘

问题描述

我有一个 WPF 窗口,其中有一个带有文本框的列表视图,我想让用户能够使用 TAB 键在文本框之间切换。我创建了一个这样做的函数,但是每次文本框失去焦点时,我都会刷新列表视图,因此列表视图本身就会获得焦点。

我知道问题是由于刷新事件而发生的,因为评论该部分将导致正确的元素(文本框)被聚焦。我尝试了许多替代解决方案,但没有一个有效(在 c# 中刷新列表视图时防止项目失去焦点在 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; }
    }

我刷新列表视图,以便更新说明文本。

标签: c#wpf

解决方案


我没有浏览您的整个代码,而只浏览了与您的问题相关的那些部分。为简化起见,我将使用附加属性TabNavigation数据绑定功能。绑定将自动更新控件值,因此刷新ListView和焦点处理变得多余。

备注:为了缩短 XAML 代码,我将只显示相关的代码部分。

标签导航

KeyboardNavigation.TabNavigation在 to 上设置附加属性,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'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) 的类型访问视图模型。请记住始终设置DataTypea 的DataTemplate。然后添加BindingTextBox. 请注意,Mode绑定的(方向)设置为OneWayToSource。这将绑定限制TextBoxDetailItem仅将数据设置为:

          <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()方法直接从ReferenceandDetail属性的设置器中调用,因此每次更改这些属性时,Explanation值都会更新。使用更新ReferenceDetail设置器,该类的最终版本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();
      }
    }
  }

推荐阅读