c# - 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; }
}
我刷新列表视图,以便更新说明文本。
解决方案
我没有浏览您的整个代码,而只浏览了与您的问题相关的那些部分。为简化起见,我将使用附加属性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
) 的类型访问视图模型。请记住始终设置DataType
a 的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
andDetail
属性的设置器中调用,因此每次更改这些属性时,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();
}
}
}
推荐阅读
- json - 如何编写 solr 查询
- node.js - 使用猫鼬从不同文档ID中的数组中删除条目
- c# - 通过检查列表c#的父ID,当有子项时从列表中删除父项
- python - 在远程服务器上运行命令时需要有关 OS 模块的建议
- c# - T4 生成的编码问题
- r - 重塑矩阵并将其转换为跟踪原始行和列索引的数据框
- ruby-on-rails - 如何在 Ruby(2.5) 中使用折纸 gem 在 PDF 中插入图像?
- r - choose.dir 在 RStudio-Server 中不起作用
- nsis - 安装使用 NSIS 开发的软件时,如果安装在服务器操作系统中,我应该如何抛出弹出消息
- python - Python中的“天+= 1”是什么意思?