在WPF的ListView中,有时候对数据行的数据模板进行了自定义使用了TextBox等控件,在默认点中TextBox进行编辑时,其所在数据行并不会被选中,这时候我们只需要给ListView添加一个PreviewMouseDown事件,并添加下面的代码,即可实现可以通过点击自定义的数据模板中的控件进行数据行的选择,包括单选及多选。
#region Make the row be selected when click on it
private T FindAncestor<T>(DependencyObject obj) where T : DependencyObject
{
while (obj != null)
{
var ancestor = obj as T;
if (ancestor != null)
{
return ancestor;
}
obj = VisualTreeHelper.GetParent(obj);
}
return null;
}
private void ListView_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var listView = sender as ListView;
if (listView == null) return;
var hitTestResult = VisualTreeHelper.HitTest(listView, e.GetPosition(listView));
if (hitTestResult != null)
{
var listViewItem = FindAncestor<ListViewItem>(hitTestResult.VisualHit);
if (listViewItem != null)
{
// 获取当前点击的项
var clickedItem = listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
// 检查 SelectionMode
if (listView.SelectionMode == SelectionMode.Single)
{
// 在 Single 模式下,直接选中当前点击的项
listView.SelectedItem = clickedItem;
}
else if (listView.SelectionMode == SelectionMode.Multiple || listView.SelectionMode == SelectionMode.Extended)
{
// 检查是否按下了 Shift 键
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
{
// 获取第一个选中的项
var firstSelectedItem = listView.SelectedItems.Cast<object>().FirstOrDefault();
if (firstSelectedItem != null)
{
// 获取第一个选中项和当前点击项的索引
int firstIndex = listView.Items.IndexOf(firstSelectedItem);
int clickedIndex = listView.Items.IndexOf(clickedItem);
// 确定范围的起始和结束索引
int startIndex = Math.Min(firstIndex, clickedIndex);
int endIndex = Math.Max(firstIndex, clickedIndex);
// 清除所有选中项
listView.SelectedItems.Clear();
// 选中范围内的所有项
for (int i = startIndex; i <= endIndex; i++)
{
listView.SelectedItems.Add(listView.Items[i]);
}
}
else
{
// 如果没有第一个选中的项,则只选中当前点击的项
listView.SelectedItems.Clear();
listViewItem.IsSelected = true;
}
}
// 检查是否按下了 Ctrl 键
else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
// 如果按下了 Ctrl 键,则切换当前点击项的选中状态
listViewItem.IsSelected = !listViewItem.IsSelected;
}
else
{
// 如果没有按下 Ctrl 或 Shift,则只选中当前点击的项
listView.SelectedItems.Clear();
listViewItem.IsSelected = true;
}
}
}
}
}
#endregion
XAML中的实现如下,关键代码是第一行的PreviewMouseDown="ListView_PreviewMouseDown"
:
<ListView x:Name="lvDBUsers" ItemsSource="{Binding Path=SelectedEntity.SelectedDepartment.Users}" SelectedItem="{Binding Path=SelectedEntity.SelectedDepartment.SelectedUser}" PreviewMouseDown="ListView_PreviewMouseDown">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="Enroll Number" x:Name="colEnrollNumber">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid Width="{Binding ElementName=colEnrollNumber, Path=ActualWidth}">
<TextBox Text="{Binding Path=UIEnrollNumber, UpdateSourceTrigger=Default , NotifyOnValidationError=True , ValidatesOnDataErrors=True}" Margin="0,0,12,0"/>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>