我遇到的问题是这样的:当选中DataGrid的前两行并删除第一行时,下面的选择行变成第一行并且取消了选择。如果我对任何一个列进行排序,那么该行的选择会恢复。或者,如果我关闭窗口,会收到信息提示取消选择的行实际上是被选中的(在UsersViewModel的SelectedUsers属性中查询底层绑定属性的内容)。有没有人能够帮忙解释一下我是否做错了什么,或者这可能是个bug。我在下面提供完整的源代码。感谢您的帮助。
MainWindow.xaml
MainWindow.xaml
<Window x:Class="DeleteFirstRowIssue.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DeleteFirstRowIssue"
Title="MainWindow" Height="350" Width="400">
<Window.Resources>
<Style x:Key="CustomDataGridCellStyle" TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="Users:" Grid.Row="0" Grid.Column="0"/>
<local:CustomDataGrid x:Name="UsersDataGrid" ItemsSource="{Binding UsersViewSource.View}" SelectionMode="Extended" AlternatingRowBackground="LightBlue" AlternationCount="2"
SelectionUnit="FullRow" IsReadOnly="True" SnapsToDevicePixels="True" AutoGenerateColumns="False" Grid.Row="1" Grid.Column="0" CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False"
SelectedItemsList="{Binding SelectedUsers, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsSynchronizedWithCurrentItem="False" CellStyle="{StaticResource CustomDataGridCellStyle}">
<local:CustomDataGrid.Columns>
<DataGridTextColumn Header="Nickname:" Width="*" Binding="{Binding Nickname}"/>
<DataGridTextColumn Header="Age:" Width="*" Binding="{Binding Age}"/>
</local:CustomDataGrid.Columns>
</local:CustomDataGrid>
<Button Grid.Row="2" Grid.Column="0" Margin="5" MaxWidth="80" MinWidth="80" Content="Delete 1st row" Command="{Binding DeleteFirstUserCommand}"/>
<Button Grid.Row="3" Grid.Column="0" Margin="5" MaxWidth="80" MinWidth="80" Content="Delete last row" Command="{Binding DeleteLastUserCommand}"/>
<Button Grid.Row="4" Grid.Column="0" Margin="5" MaxWidth="80" MinWidth="80" Content="Initialize Grid" Command="{Binding InitializeListCommand}"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace DeleteFirstRowIssue
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new UsersViewModel();
}
protected override void OnClosing(CancelEventArgs e)
{
UsersViewModel uvm = (UsersViewModel)DataContext;
if (uvm.SelectedUsers.Count > 0)
{
StringBuilder sb = new StringBuilder(uvm.SelectedUsers.Count.ToString() + " selected user(s):\n");
foreach (UserModel um in uvm.SelectedUsers)
{
sb.Append(um.Nickname + "\n");
}
MessageBox.Show(sb.ToString());
}
base.OnClosing(e);
}
}
public class UsersViewModel : INotifyPropertyChanged
{
private IList selectedUsers;
public IList SelectedUsers
{
get { return selectedUsers; }
set
{
selectedUsers = value;
OnPropertyChanged("SelectedUsers");
}
}
public CollectionViewSource UsersViewSource { get; private set; }
public ObservableCollection<UserModel> Users { get; set; }
public ICommand DeleteFirstUserCommand { get; }
public ICommand DeleteLastUserCommand { get; }
public ICommand InitializeListCommand { get; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
public UsersViewModel()
{
SelectedUsers = new ArrayList();
Users = new ObservableCollection<UserModel>();
UsersViewSource = new CollectionViewSource() { Source = Users };
InitializeListCommand = new RelayCommand(p => Users.Count == 0, p => InitializeList());
InitializeListCommand.Execute(null);
DeleteFirstUserCommand = new RelayCommand(p => Users.Count > 0, p => DeleteFirstUser());
DeleteLastUserCommand = new RelayCommand(p => Users.Count > 0, p => DeleteLastUser());
}
private void InitializeList()
{
Users.Add(new UserModel() { Nickname = "John", Age = 35 });
Users.Add(new UserModel() { Nickname = "Jane", Age = 29 });
Users.Add(new UserModel() { Nickname = "Mark", Age = 59 });
Users.Add(new UserModel() { Nickname = "Susan", Age = 79 });
Users.Add(new UserModel() { Nickname = "Joe", Age = 66 });
Users.Add(new UserModel() { Nickname = "Nina", Age = 29 });
Users.Add(new UserModel() { Nickname = "Selma", Age = 44 });
Users.Add(new UserModel() { Nickname = "Katrin", Age = 24 });
Users.Add(new UserModel() { Nickname = "Joel", Age = 32 });
}
private void DeleteFirstUser()
{
ListCollectionView lcw = (ListCollectionView)UsersViewSource.View;
lcw.RemoveAt(0);
}
private void DeleteLastUser()
{
ListCollectionView lcw = (ListCollectionView)UsersViewSource.View;
lcw.RemoveAt(lcw.Count - 1);
}
}
public class UserModel
{
public string Nickname { get; set; }
public int Age { get; set; }
}
public class CustomDataGrid : DataGrid
{
public static readonly DependencyProperty SelectedItemsListProperty = DependencyProperty.Register("SelectedItemsList", typeof(IList), typeof(CustomDataGrid), new PropertyMetadata(null));
public IList SelectedItemsList
{
get { return (IList)GetValue(SelectedItemsListProperty); }
set { SetValue(SelectedItemsListProperty, value); }
}
public CustomDataGrid() { SelectionChanged += CustomDataGrid_SelectionChanged; }
void CustomDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) { SelectedItemsList = SelectedItems; }
}
public class RelayCommand : ICommand
{
private Predicate<object> canExecute;
private Action<object> execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
{
this.canExecute = canExecute;
this.execute = execute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter) { return canExecute(parameter); }
public void Execute(object parameter) { execute(parameter); }
}
}
DataGridRow.IsSelected
与DataGridCell.IsSelected
属性不同步。这就是为什么基于单元格的样式不再正确触发,但基于行的选择列表包含了这些项目。然而,我不知道为什么会出现这种详细的不同步情况。 - grek40