Question

I have a DataGrid that has checkboxes that are bound to the items' IsSelected property. I'm trying to create a feature to allow you to select rows in the grid by click/dragging or click / shift click / ctrl clicking other rows, then right click > Select to set the IsSelected Property of my items. The checkboxes are working perfectly, properly two-way binding to my IsSelected property of my objects.

I've got it to 90%, but when I send the DataGrid's SelectedItems value, it sometimes sends items that were selected but aren't anymore.

I found the code to send the DataGrid's SelectedItems to the command here.

<DataGrid ItemsSource="{Binding MyItemList}">
    <DataGrid.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Select" 
                      Command="{Binding Data.SelectCommand, Source={StaticResource BindingProxy}}" 
                      CommandParameter="{Binding PlacementTarget.SelectedItems,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}" />
        </ContextMenu>
    </DataGrid.ContextMenu>

C# code from my viewmodel

private void Select(object selectedObjects)
{ 
    var items = (System.Collections.IList)selectedObjects;
    var collection = items.Cast<ItemType>().ToList();
    foreach (var item in collection)
        item.IsSelected = true;
} 

Here's what I do to reproduce it.

Scenario 1:

  1. The control loads, nothing is selected by default
  2. I click and drag to select all options
  3. Right click and choose Select
  4. All the checkboxes are now set as checked, as expected
  5. Click on the first row so only it is highlighted
  6. Right click and choose Deselect
  7. All row's checkboxes are deselected (wrong!)

Scenario 2:

  1. Control loads, nothing is selected
  2. Click and drag and select first 2 entries
  3. Right Click and choose Select, checkboxes get checked
  4. Uncheck rows 1 and 2
  5. Select rows 4 and 5
  6. Right Click > Select
  7. Rows 1, 2, 4, and 5 are now checked. (wrong)
  8. Uncheck 1, 2, 4, and 5
  9. Select rows 7 and 8
  10. Right Click > Select
  11. Rows 1, 2, 7, and 8 are now checked. (just what.)

I'm finding it very hard to come up with some sort of consistent reason that it's acting this way. My best guess is that when I right click on the datagrid, it doesn't act as a normal selection operation. Anyone have any clues why this is happening, or how I can force the grid to properly select the rows I tell it to?

Was it helpful?

Solution

I found the issue. I am using a wrapping class to enable selection on my objects: Selectable<T>. This object contains an IsSelected bool and a T Item. The creator of this class had overridden the Equals and GetHashCode methods. This was causing the equals checks to fail, so the DataGrid wasn't properly deselecting the objects because they were being detected as being different items.

The code in the question above should work correctly if you are doing proper Equals checks on your objects.

OTHER TIPS

You are almost using MVVM, but where is your ViewModel? With a ViewModel this becomes easy.

You have an Model object you are using for your DataSource: AccountJobList It is a list of Model objects, which I assume is AccountJob.

So perhaps AccountJob doesn't have an IsChecked property or it does but it doesn't implement INotifyPropertyChanged. That is why we create ViewModels. So create an AccountJobViewModel that is a wrapper to AccountJob. This AccountJobViewModel must implement INotifyPropertyChanged. Now in our ViewModel we add an IsChecked property.

Now we create an AccountJobListViewModel that inherits ObservableCollection (or List<AccountJobViewModel> if you don't need your collection to be Observable). AccountJobListViewModel takes in an AccountJobList and foreach AccountJob in the list, it creates an AccountJobViewModel and adds it to the collection.

You also need a property for SelectedItems in either the AccountJobListViewModel or its parent. I assume your Select/Deselect methods are in a ViewModel of some sort, so perhaps it goes there.

Now you simply bind IsChecked on your ViewModel.

<CheckBox IsChecked="{Binding IsChecked}" />

Now in your code, you simply set each selected AccountJobViewModel's IsChecked property to true.

private void Select(object selectedObjects)
{
  foreach (AccountJobViewModel item in SelectedItems)
    item.IsChecked = true;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top