0

I'm trying to make some generic commands (move, delete rows) for WPF DataGrid. So far I have managed to write functions that work when I know DataGrid's ItemsSource is of type ObservableCollection<OrderRow>. But in future I will need to apply same commands to other types of lists so I thought that it should work on IList<> for maximal reusability. I tried table.ItemsSource is IList<object> but that didn't work.

I can test if it's IList<>: table.ItemsSource.GetType().GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>) but I still can't assign it to IList<> object. Surely I'm missing something simple...

For purpose of context, this bit shows what I'm currently doing:

public static readonly RoutedUICommand DeleteRows = new RoutedUICommand()

private void EditRow_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    if (e.OriginalSource is DataGrid table)
        e.CanExecute = table.SelectedItems.Count > 0;
}
private void DeleteRows_Executed(object sender, ExecutedRoutedEventArgs e)
{
    if (e.OriginalSource is DataGrid table && table.ItemsSource is ObservableCollection<OrderRow> rows)
    {
        //Copy the selected items list contents so that it doesn't get cleared upon editing itemssource
        var selected = new List<OrderRow>();
        foreach (var item in table.SelectedItems)
            if (item is OrderRow row)   //This check is needed that item isn't a new item placeholder
                selected.Add(row);
                
        foreach (var row in selected)
            rows.Remove(row);
    }
}
<Window.CommandBindings>
    <CommandBinding Command="local:CustomCommands.DeleteRows" CanExecute="EditRow_CanExecute" Executed="DeleteRows_Executed" />
</Window.CommandBindings>

Here's more reproducible example:

static void Main()
{
    List<string> names = new List<string>() { "Alex", "Bart", "Charlie", "David", "Ellie" };
    DeleteItems(names, new[] { "Bart", "David" });
    foreach (string name in names)
        Console.WriteLine(name);
    Console.ReadLine();
}

//Takes IEnumerable as an argument because real use case is ItemsSource from DataGrid.
static void DeleteItems(IEnumerable<object> items, object[] selectedItems)
{
    //This bit obviously doesn't work, IList<string> cannot be casted to IList<object>
    if (items is IList<object> list)
    {
        foreach (var obj in selectedItems)
            list.Remove(obj);
    }
}
aXu_AP
  • 63
  • 6
  • Not quite sure what you are trying to do. Show us an example and we'll help you. – Athanasios Kataras Oct 27 '20 at 21:20
  • 1
    _"I tried `table.ItemsSource is IList` but that didn't work."_ -- "didn't work" is not a question. And there's no such code in the question. Do note that a collection only _is_ `IList` if the element type of the collection is actually literally `object`. A `List` is _not_ an `IList`, for example. See e.g. https://stackoverflow.com/questions/16966961/cannot-convert-from-listderivedclass-to-listbaseclass – Peter Duniho Oct 27 '20 at 21:26
  • @PeterDuniho Yes I figured out as such, I included that bit as to what I'm trying to achieve. The question is, how I can modify provided code to work with IList<> instead of ObservableCollection. Code I included is code that's currently working, but doesn't work in generic cases. – aXu_AP Oct 27 '20 at 21:31
  • 1
    The question is presently somewhat vaguely. But `IList` should work fine. See also https://stackoverflow.com/questions/1817300/convert-listderivedclass-to-listbaseclass – Peter Duniho Oct 27 '20 at 21:33
  • Note that your other two questions don't belong here (_"check if the new row..."_ and _"where these functions..."_). Please include only one question at a time, otherwise your whole post is likely to be closed as "Needs focus" (as it has been here, as it happens) – Peter Duniho Oct 27 '20 at 21:34
  • @PeterDuniho Thanks for suggestions, it's step to right direction. IList works. But still need something to be able to work on IList of any type... I also edited out mentioned questions, you're right about those. – aXu_AP Oct 27 '20 at 21:47
  • @AthanasiosKataras I added a bit more of the code needed to understand the context. I'm not sure what else I could show. Any suggestions? – aXu_AP Oct 27 '20 at 21:52
  • _"still need something to be able to work on IList of any type"_ -- why? your code only handles objects of `OrderRow`. Why would you need to handle `IList` where `T` is some type other than `OrderRow`? I do caution you: this business of wanting to handle dynamically-typed collections has been asked and answered many times on this site already. At the very least, you should do more investigation, and only ask a question if you can provide a [mcve] along with details of what your investigation turned up and what _specifically_ you still need help with. You've addressed the ... – Peter Duniho Oct 27 '20 at 21:53
  • ...nominal flaw that led to the question's closure, but even as it stands now, if reopened it likely would be closed again for other reasons (principally "Needs details" and/or "Needs debugging information") – Peter Duniho Oct 27 '20 at 21:54
  • @PeterDuniho Very well, I'll see what I can do to improve the question. I have been investigating this problem for a few good hours, but maybe I'm lacking right terminology to find answers... – aXu_AP Oct 27 '20 at 22:00

1 Answers1

0

I found out that ObservableCollection<T> inherits Collection<T> which in turn implements IList (non-generic variant). Since IList works always with objects, it can be used instead.

private void DeleteRows_Executed(object sender, ExecutedRoutedEventArgs e)
{
    if (e.OriginalSource is DataGrid table && table.ItemsSource is System.Collections.IList rows)
    {
        //Copy the selected items list contents so that it doesn't get cleared upon editing itemssource
        var selected = new List<object>();
        foreach (var row in table.SelectedItems)
            selected.Add(row);

        foreach (var row in selected)
            rows.Remove(row);
    }
}

Second example:

//Note, it doesn't work if items doesn't implement IList!
static void DeleteItems(IEnumerable<object> items, object[] selectedItems)
{
    if (items is System.Collections.IList list)
    {
        foreach (var obj in selectedItems)
            list.Remove(obj);
    }
}
aXu_AP
  • 63
  • 6