WPF ListBox select 使用 ICommand 删除后的下一个项目
WPF ListBox select next item after deletion with ICommand
这个问题的演变与 this question 类似,但它涉及我因使用绑定而面临的不同问题。
我添加了一个按钮,用于从 ListBox
中删除当前选定的项目(还有一个用于出现相同问题的 DataGrid
)。执行此操作的代码通过 ICommand
对象和 Button.Command
属性:
绑定到按钮
<Button Command="{Binding DeleteRowCommand}"
CommandParameter="{Binding ElementName=MyListBox, Path=SelectedItem}" />
然而,这意味着点击操作直接汇集到 ViewModel 中,ViewModel 当然不知道视图的 ListBox
,因此它无法通知视图或更新任何选择。
在视图模型和视图中触发代码隐藏的正确方法是什么?
也许我可以使用命令-属性 和 Handles
语句,但我不确定这是否可行。
这是我会做的。
在您的 ViewModel 中创建一个 属性 来保存 SelectedItem。
private YourTypeHere _SelectedThing;
public YourTypeHere SelectedThing
{
get { return _SelectedThing; }
set
{
_SelectedThing = value;
//Call INotifyPropertyChanged stuff
}
}
现在,将您的列表的 SelectedItem 绑定到此 属性:
<ListBox SelectedItem="{Binding SelectedThing}" ... />
执行这些操作意味着您的 SelectedThing
现在由 ViewModel 负责,当您调用 Delete 命令时,您只需 将 SelectedThing 属性 更新为列表中的最后一项,您的列表框将自动更新。
XAML:
<ListBox DataContext="{Binding Path=Category, Mode=OneTime}"
ItemsSource="{Binding Path=Fields}"
SelectedItem="{Binding Path=SelectedItem, Mode=OneWay}"
SelectedIndex="{Binding Path=SelectedIndex}" />
CategoryViewModel
中的代码,来自数据上下文的 Category
对象的 class:
Public ReadOnly Property Fields As ObservableCollection(Of FieldViewModel)
Get
Return m_Fields
End Get
End Property
Private ReadOnly m_Fields As New ObservableCollection(Of FieldViewModel)
Public Property SelectedIndex As Integer
Get
Return m_SelectedIndex
End Get
Set(value As Integer)
m_SelectedIndex = value
ValidateSelectedIndex()
RaisePropertyChanged("SelectedIndex")
RaisePropertyChanged("SelectedItem")
End Set
End Property
Private m_SelectedIndex As Integer = -1
Public ReadOnly Property SelectedItem As FieldViewModel
Get
Return If(m_SelectedIndex = -1, Nothing, m_Fields.Item(m_SelectedIndex))
End Get
End Property
Private Sub ValidateSelectedIndex()
Dim count = m_Fields.Count
Dim newIndex As Integer = m_SelectedIndex
If count = 0 Then
' No Items => no selections
newIndex = -1
ElseIf count <= m_SelectedIndex Then
' Index > max index => correction
newIndex = count - 1
ElseIf m_SelectedIndex < 0 Then
' Index < min index => correction
newIndex = 0
End If
m_SelectedIndex = newIndex
RaisePropertyChanged("SelectedIndex")
RaisePropertyChanged("SelectedItem")
End Sub
Public Sub New(model As Category)
' [...] Initialising of the view model,
' especially the m_Fields collection
' Auto update of SelectedIndex after modification
AddHandler m_Fields.CollectionChanged, Sub() ValidateSelectedIndex()
' Set the SelectedIndex to 0 if there are items
' or -1 if there are none
ValidateSelectedIndex()
End Sub
现在,每当您从 m_Fields
集合中删除一个项目时,SelectedItem
将更改为下一个项目,如果删除了最后一个项目,则为上一个项目,或者 Nothing
如果删除了最后一个剩余项目。
这个问题的演变与 this question 类似,但它涉及我因使用绑定而面临的不同问题。
我添加了一个按钮,用于从 ListBox
中删除当前选定的项目(还有一个用于出现相同问题的 DataGrid
)。执行此操作的代码通过 ICommand
对象和 Button.Command
属性:
<Button Command="{Binding DeleteRowCommand}"
CommandParameter="{Binding ElementName=MyListBox, Path=SelectedItem}" />
然而,这意味着点击操作直接汇集到 ViewModel 中,ViewModel 当然不知道视图的 ListBox
,因此它无法通知视图或更新任何选择。
在视图模型和视图中触发代码隐藏的正确方法是什么?
也许我可以使用命令-属性 和 Handles
语句,但我不确定这是否可行。
这是我会做的。
在您的 ViewModel 中创建一个 属性 来保存 SelectedItem。
private YourTypeHere _SelectedThing;
public YourTypeHere SelectedThing
{
get { return _SelectedThing; }
set
{
_SelectedThing = value;
//Call INotifyPropertyChanged stuff
}
}
现在,将您的列表的 SelectedItem 绑定到此 属性:
<ListBox SelectedItem="{Binding SelectedThing}" ... />
执行这些操作意味着您的 SelectedThing
现在由 ViewModel 负责,当您调用 Delete 命令时,您只需 将 SelectedThing 属性 更新为列表中的最后一项,您的列表框将自动更新。
XAML:
<ListBox DataContext="{Binding Path=Category, Mode=OneTime}"
ItemsSource="{Binding Path=Fields}"
SelectedItem="{Binding Path=SelectedItem, Mode=OneWay}"
SelectedIndex="{Binding Path=SelectedIndex}" />
CategoryViewModel
中的代码,来自数据上下文的 Category
对象的 class:
Public ReadOnly Property Fields As ObservableCollection(Of FieldViewModel)
Get
Return m_Fields
End Get
End Property
Private ReadOnly m_Fields As New ObservableCollection(Of FieldViewModel)
Public Property SelectedIndex As Integer
Get
Return m_SelectedIndex
End Get
Set(value As Integer)
m_SelectedIndex = value
ValidateSelectedIndex()
RaisePropertyChanged("SelectedIndex")
RaisePropertyChanged("SelectedItem")
End Set
End Property
Private m_SelectedIndex As Integer = -1
Public ReadOnly Property SelectedItem As FieldViewModel
Get
Return If(m_SelectedIndex = -1, Nothing, m_Fields.Item(m_SelectedIndex))
End Get
End Property
Private Sub ValidateSelectedIndex()
Dim count = m_Fields.Count
Dim newIndex As Integer = m_SelectedIndex
If count = 0 Then
' No Items => no selections
newIndex = -1
ElseIf count <= m_SelectedIndex Then
' Index > max index => correction
newIndex = count - 1
ElseIf m_SelectedIndex < 0 Then
' Index < min index => correction
newIndex = 0
End If
m_SelectedIndex = newIndex
RaisePropertyChanged("SelectedIndex")
RaisePropertyChanged("SelectedItem")
End Sub
Public Sub New(model As Category)
' [...] Initialising of the view model,
' especially the m_Fields collection
' Auto update of SelectedIndex after modification
AddHandler m_Fields.CollectionChanged, Sub() ValidateSelectedIndex()
' Set the SelectedIndex to 0 if there are items
' or -1 if there are none
ValidateSelectedIndex()
End Sub
现在,每当您从 m_Fields
集合中删除一个项目时,SelectedItem
将更改为下一个项目,如果删除了最后一个项目,则为上一个项目,或者 Nothing
如果删除了最后一个剩余项目。