我遇到的问题如下:当选择DataGrid中的前两行并删除第一行时,下面的选定行将成为第一行并被取消选择。如果我对任何一列进行排序,该行的选择就会回来。或者,如果我关闭窗口,我会得到实际选择取消选择的行的信息(在用户视图模型中查询基础绑定属性 SelectedUsers 的内容 - 在 OnClosing 方法中完成(。如果我做错了什么,或者这可能是一个错误,有没有人可以帮助或解释我。我在下面提供了完整的源代码。感谢您的帮助。
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); }
}
}
我在实现SelectedItemsList
时遇到了类似的问题。主要问题是,当更新选择时,支持列表会尝试删除不再在选择中的项目。但是,如果某个项目被更改(或不再存在(,则比较将失败,并且该项目仍保留在列表中,从而导致此行为。
可以通过添加CollectionChanged
事件处理程序来保持列表同步。
public UsersViewModel()
{
...
Users.CollectionChanged += new NotifyCollectionChangedEventHandler(CollectionChanged);
}
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove)
foreach (UserModel item in e.OldItems) SelectedUsers.Remove(item);
}
如前所述,不知何故,当删除第一行时,新的第一行IsSelected
属性与其单元格IsSelected
属性不同步。我不知道为什么会发生这种情况,但如果您主要对保持样式正常工作感兴趣,它表明了一种解决方法: 只需使用触发器中的行属性
<DataTrigger Binding="{Binding IsSelected,RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>