2017-06-19 14 views
3

問題は次のとおりです。DataGridの最初の2行が選択され、最初の行が削除されると、下の選択された行が最初の行になり、選択解除されます。列を並べ替えると、その行の選択が戻ってきます。または、ウィンドウを閉じると、選択解除された行が実際に選択された情報が取得されます(UsersViewModelの基本的なバウンドプロパティSelectedUsersのコンテンツのクエリ - OnClosingメソッドで行われます)。私が何か間違ったことをしたり、これがバグかもしれないと、私を助けたり解説したりできる人はいますか?私は以下の完全なソースコードを提供しました。手伝ってくれてありがとう。DataGridの選択問題

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

興味深い...最初の3行を選択すると、行1を削除した後、前の行3(現在は2)が選択状態を表示しますが、新しい行1は表示されません。 – grek40

+0

楽しい事実:スヌープでチェックしただけです。「DataGridRow.IsSelected」が最初の行の 'DataGridCell.IsSelected'プロパティと同期しなくなりました。このため、セルベースのスタイルはもはや正しくトリガされなくなりましたが、行ベースの選択リストにアイテムが含まれています。しかし、私は、なぜこのデシンクが詳細に起こるのか分かりません。 – grek40

答えて

1

の行プロパティを使用します。主な問題は、選択が更新されると、バッキングリストはもはや選択範囲にない項目を削除しようとすることです。ただし、アイテムが変更された場合(または存在しなくなった場合)、比較は失敗し、アイテムはリストに残り、この動作が発生します。

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); 
} 
+0

非常に速いソリューションと説明をありがとう。 – Cobek

1

としては、最初の行が削除された場合、新たな第一行IsSelectedプロパティは、その細胞と同期しなくなる何とか、コメントIsSelectedプロパティ。これがなぜ起こるか私は知りませんが、あなたはスタイルの作業を維持するのに主に興味を持っている場合には、回避策を示している。ただ、私はSelectedItemsListを実施し、同様の問題が発生したトリガ

<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> 
+0

非常に速い助けをありがとう、ありがとう。ニースの回避策。あなたが本当の理由を説明するために私がファンクにクレジットを与えるかどうか気にしないでください。 – Cobek