我遇到的问题是:当选择DataGrid中的前两行并删除第一行时,下面的选定行将成为第一行并取消选择。如果我对任何列进行排序,则会返回该行的选择。或者,如果我关闭窗口,我会得到实际选择了取消选择行的信息(查询UsersViewModel中底层绑定属性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); }
}
}
答案 0 :(得分:1)
如评论所述,当第一行被删除时,新的第一行IsSelected
属性与其单元格IsSelected
属性不同步。我不知道为什么会发生这种情况,但如果你主要想让风格保持工作,它就表明了一种解决方法:只需在触发器中使用rows属性
<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>
答案 1 :(得分:1)
我遇到了实施SelectedItemsList
的类似问题。主要问题是,当选择更新时,支持列表会尝试删除不再包含在选择中的项目。但是,如果某个项目已更改(或不再存在),则比较将失败,并且该项目仍保留在列表中,从而导致此行为。
您可以通过添加CollectionChanged
EventHandler来保持列表同步。
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);
}