Я прошёл не весь ваш код, а только те части, которые имеют отношение к вашему вопросу. Для упрощения я буду использовать прикрепленное свойство 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();
}
}
}