0

I have a datagrid that displays all of the records that exist in an SQL database table and I would like to add a single button to my UI that allows users to delete the record(s) he or she has selected. Most of the references I have found for this revolve around adding a delete button to each row or involve code behind. I am using an MVVM pattern and I do not want a button in each row. Currently I am able to delete one record at a time but need assistance with how to iterate through the selected items. My code is as follows:

XAML

         <Button x:Name="revokeBtn"
                Grid.Row="0"
                Grid.Column="4"
                ToolTip="Revoke Selected License or Licenses"
                Content="Revoke"
                Command="{Binding RevokeSelectedCommand}"
                CommandParameter="{Binding}">
        </Button>

        <DataGrid x:Name="licenseGrid"
                  ItemsSource="{Binding LoggedUsers}"
                  SelectedItem="{Binding SelectedLicenses}"
                  Style="{DynamicResource DataGridStyle}"
                  Grid.Row="2"
                  Grid.Column="1"
                  Grid.ColumnSpan="6"
                  Height="535"
                  VerticalAlignment="Top"
                  IsReadOnly="True"
                  AutoGenerateColumns="False" 
                  HeadersVisibility="Column"
                  SelectionMode="Extended"
                  CanUserDeleteRows="True"
                  EnableRowVirtualization="False">
            <DataGrid.RowStyle>
                <Style TargetType="DataGridRow">
                    <Setter Property="IsSelected" Value="{Binding IsSelected}" />
                </Style>
            </DataGrid.RowStyle>

With DataGrid.Columns that are bound to the table columns.

ViewModel

    public ObservableCollection<MD_LoggedUsersModel> LoggedUsers
    {
        get { return _loggedUsers; }
        set { _loggedUsers = value; NotifyPropertyChanged(nameof(LoggedUsers)); }
    }

    public MD_LoggedUsersModel SelectedLicenses
    {
        get
        {
            return _selectedLicenses;
        }
        set
        {
            if (_selectedLicenses != value)
            {
                _selectedLicenses = value;
                OnPropertyChanged(nameof(SelectedLicenses));
            }
            if (_selectedLicenses == null)
            {
                LoadData();
            }
        }
    }

    public bool IsSelected
    {
        get
        {
            return _isSelected;
        }
        set
        {
            if (_isSelected == value) return;
            _isSelected = value;
            OnPropertyChanged(nameof(IsSelected));
        }
    }

    public ICommand RevokeSelectedCommand
    {
        get
        {
            return _revokeSelectedCommand ?? (_revokeSelectedCommand = new CommandHandler(() => RevokeSelected(), _canExecute));
        }
    }
    private void RevokeSelected()
    {need to iterate through selected rows here}

What is the best way to accomplish this?

2
  • The way I've always dealt with selected rows is to add a CheckBox column to the grid which is bound to an IsSelected Property on the class. Then you can iterate your collection, remove items from the DataSource and Notify the UI of the change Commented Nov 30, 2016 at 16:47
  • If the ability to select multiple rows with a ctrl + mouse click is available, I have to believe that a checkbox isn't necessary and I'd rather not take that route. Commented Nov 30, 2016 at 17:00

2 Answers 2

1

I've already had this problem a couple of month ago and therefor I have written the following attached-property:

 public class MultiSelectorExtensions
    {
        public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.RegisterAttached(
            "SelectedItems",
            typeof(INotifyCollectionChanged),
            typeof(MultiSelectorExtensions),
            new PropertyMetadata(default(INotifyCollectionChanged), OnSelectedItemsChanged));

        private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MultiSelector multiSelectorControl = d as MultiSelector;

            NotifyCollectionChangedEventHandler handler = (sender, args) =>
            {
                if (multiSelectorControl != null)
                {
                    IList listSelectedItems = multiSelectorControl.SelectedItems;
                    if (args.OldItems != null)
                    {
                        foreach (var item in args.OldItems)
                        {
                            if (listSelectedItems.Contains(item))
                                listSelectedItems.Remove(item);
                        }
                    }

                    if (args.NewItems != null)
                    {
                        foreach (var item in args.NewItems)
                        {
                            if (!listSelectedItems.Contains(item))
                                listSelectedItems.Add(item);
                        }
                    }
                }
            };

            if (e.OldValue == null && multiSelectorControl != null)
            {
                multiSelectorControl.SelectionChanged += OnSelectionChanged;
            }

            if (e.OldValue is INotifyCollectionChanged)
            {
                (e.OldValue as INotifyCollectionChanged).CollectionChanged -= handler;
            }

            if (e.NewValue is INotifyCollectionChanged)
            {
                (e.NewValue as INotifyCollectionChanged).CollectionChanged += handler;
            }

        }

        private static void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            DependencyObject d = sender as DependencyObject;

            if (GetSelectionChangedInProgress(d))
                return;

            SetSelectionChangedInProgress(d, true);

            dynamic selectedItems = GetSelectedItems(d);

            try
            {
                foreach (dynamic item in e.RemovedItems.Cast<dynamic>().Where(item => selectedItems.Contains(item)))
                {
                    selectedItems.Remove(item);
                }
            }
            catch (Exception) { }

            try
            {
                foreach (dynamic item in e.AddedItems.Cast<dynamic>().Where(item => !selectedItems.Contains(item)))
                {
                    selectedItems.Add(item);
                }
            }
            catch (Exception){}

            SetSelectionChangedInProgress(d, false);
        }

        public static void SetSelectedItems(DependencyObject element, INotifyCollectionChanged value)
        {
            element.SetValue(SelectedItemsProperty, value);
        }

        public static INotifyCollectionChanged GetSelectedItems(DependencyObject element)
        {
            return (INotifyCollectionChanged)element.GetValue(SelectedItemsProperty);
        }

        private static readonly DependencyProperty SelectionChangedInProgressProperty = DependencyProperty.RegisterAttached(
            "SelectionChangedInProgress",
            typeof(bool),
            typeof(MultiSelectorExtensions),
            new PropertyMetadata(default(bool)));

        private static void SetSelectionChangedInProgress(DependencyObject element, bool value)
        {
            element.SetValue(SelectionChangedInProgressProperty, value);
        }

        private static bool GetSelectionChangedInProgress(DependencyObject element)
        {
            return (bool)element.GetValue(SelectionChangedInProgressProperty);
        }
    }

At your DataGrid in the view you'll have to use this attached property like:

<DataGrid x:Name="licenseGrid"
          ItemsSource="{Binding LoggedUsers}"
          SelectedItem="{Binding SelectedLicenses}"
          Style="{DynamicResource DataGridStyle}"
          Grid.Row="2"
          Grid.Column="1"
          Grid.ColumnSpan="6"
          Height="535"
          VerticalAlignment="Top"
          IsReadOnly="True"
          AutoGenerateColumns="False" 
          HeadersVisibility="Column"
          SelectionMode="Extended"
          CanUserDeleteRows="True"
          EnableRowVirtualization="False"
          MyAttachedProperties:MultiSelectorExtensions.SelectedItems="{Binding SelectedLoggedUsers, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></DataGrid>

MyAttachedProperties is the namespace where the MultiSelectorExtension-class is located.

And in your viewmodel you then have a property which looks like:

private ObservableCollection<MD_LoggedUsersModel> selectedLoggedUsers;
public ObservableCollection<MD_LoggedUsersModel> SelectedLoggedUsers
{
    get { return selectedLoggedUsers; }
    set 
    { 
        selectedLoggedUsers = value;
        NotifyPropertyChanged(nameof(SelectedLoggedUsers)); 
    }
}

Important: In the constructor of your viewmodel you have to initialize the SelectedLoggedUsers with

SelectedLoggedUsers = new ObservableCollection<MD_LoggedUsersModel>();

otherwise the property will be null and the attached property will not work.

Sign up to request clarification or add additional context in comments.

Comments

0

Given the lack of responses to this post as well as the lack of resources I can find on this matter, I have opted for a button in each row which is working correctly.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.