简体   繁体   中英

DataGrid doesn't deselect hidden items properly when SelectionMode=“Extended”

I have an issue with WPF DataGrid , which drives me crazy. Let's consider this view model:

public class ViewModel : INotifyPropertyChanged
{
    public int Id { get; set; }
    public string Name { get; set; }

    public bool IsSelected 
    {
        get { return isSelected; }
        set
        {
            System.Diagnostics.Debug.WriteLine("{0}'s IsSelected new value is: {1}", Name, value);
            if (isSelected != value)
            {
                isSelected = value;
                OnPropertyChanged("IsSelected");
            }
        }
    }
    private bool isSelected;

    // INPC implementation
}

... this XAML:

<Window x:Class="WpfApplication5.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid ItemsSource="{Binding}" IsReadOnly="True" AutoGenerateColumns="False" 
                  SelectionMode="Extended" SelectionUnit="FullRow">
            <DataGrid.ItemContainerStyle>
                <Style TargetType="{x:Type DataGridRow}">
                    <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
                </Style>
            </DataGrid.ItemContainerStyle>

            <DataGrid.Columns>
                <DataGridCheckBoxColumn Header="Is selected" Binding="{Binding IsSelected}"/>
                <DataGridTextColumn Header="Id" Binding="{Binding Id}"/>
                <DataGridTextColumn Header="Name" Width="*" Binding="{Binding Name}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

...and this code-behind:

public partial class MainWindow : Window
{
    private IList<ViewModel> GenerateViewModels()
    {
        var viewModels = new List<ViewModel>();

        for (var i = 0; i < 100; i++)
        {
            viewModels.Add(new ViewModel
            {
                Id = i,
                Name = string.Format("Item {0}", i)
            });
        }

        return viewModels;
    }

    public MainWindow()
    {
        InitializeComponent();
        DataContext = GenerateViewModels();
    }
}

Case 1.

  • Select Item 0. Item 0 is selected, check box is checked.
  • Scroll grid's content to hide Item 0. Let the Item 6 be on top of visible area.
  • Select Item 10. Item 10 is selected, check box is checked.
  • Scroll up to Item 0.
  • Select Item 0. Item 10 is selected, check box is not checked.

Debug output:

Item 0's IsSelected new value is: True
Item 0's IsSelected new value is: False
Item 10's IsSelected new value is: True
Item 10's IsSelected new value is: False

截图:

Case 2.

  • Restart application.
  • Select several items on top of grid (eg, three first items). Items are selected, check boxes are checked.
  • Scroll grid's content to hide Item 0. Let the Item 6 be on top of visible area.
  • Select Item 10. Item 10 is selected, check box is checked.
  • Scroll up to Item 0.
  • Item 0 and Item 1 are still checked . Item 2 isn't checked. All of three first items aren't selected.

Debug output:

Item 0's IsSelected new value is: True
Item 1's IsSelected new value is: True
Item 2's IsSelected new value is: True
Item 2's IsSelected new value is: False
Item 10's IsSelected new value is: True

截图:

The problem is reproducing, when the selection mode is extended. When it is single, everything works fine.

Questions:
1. Am I missing something? 2. Anybody knows a workaround?

UPDATE .

I've added SelectionChanged event handler for the grid:

    private void MyGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems != null)
        {
            foreach (var item in e.AddedItems.Cast<ViewModel>())
            {
                System.Diagnostics.Debug.WriteLine("** Item {0} is added to selection.", item.Id);
            }
        }
        if (e.RemovedItems != null)
        {
            foreach (var item in e.RemovedItems.Cast<ViewModel>())
            {
                System.Diagnostics.Debug.WriteLine("** Item {0} is removed from selection.", item.Id);
            }
        }

        e.Handled = true;
    }

Debug output says, that SelectedItems collection is updated properly . Eg, for the first case output will be:

Item 0's IsSelected new value is: True
** Item 0 is added to selection.
Item 0's IsSelected new value is: False
Item 10's IsSelected new value is: True
** Item 10 is added to selection.
** Item 0 is removed from selection.
Item 10's IsSelected new value is: False
** Item 0 is added to selection.
** Item 10 is removed from selection.

But the bound data property IsSelected isn't updated!

At least, one workaround is found, and its related to the question's update.
Let's modify SelectionChanged event handler a little:

    private void MyGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems != null)
        {
            foreach (var item in e.AddedItems.Cast<ViewModel>())
            {
                System.Diagnostics.Debug.WriteLine("** Item {0} is added to selection.", item.Id);

                if (!item.IsSelected)
                {
                    // if bound data item still isn't selected, fix this
                    item.IsSelected = true;
                }
            }
        }
        if (e.RemovedItems != null)
        {
            foreach (var item in e.RemovedItems.Cast<ViewModel>())
            {
                System.Diagnostics.Debug.WriteLine("** Item {0} is removed from selection.", item.Id);

                if (item.IsSelected)
                {
                    // if bound data item still is selected, fix this
                    item.IsSelected = false;
                }
            }
        }

        e.Handled = true;
    }

But it's definitely a bug in DataGrid , isn't it?

I tried it and I think you need to set the isSelected to false to both condition. It works for me. But thanks for the initial solution! It helped me a lot.

private void MyGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems != null)
        {
            foreach (var item in e.AddedItems.Cast<ViewModel>())
            {
                System.Diagnostics.Debug.WriteLine("** Item {0} is added to selection.", item.Id);

                if (!item.IsSelected)
                {
                    // if bound data item still isn't selected, fix this
                    item.IsSelected = false;
                }
            }
        }
        if (e.RemovedItems != null)
        {
            foreach (var item in e.RemovedItems.Cast<ViewModel>())
            {
                System.Diagnostics.Debug.WriteLine("** Item {0} is removed from selection.", item.Id);

                if (item.IsSelected)
                {
                    // if bound data item still is selected, fix this
                    item.IsSelected = false;
                }
            }
        }

        e.Handled = true;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM