Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF - Best way to remove an item from the ItemsSource

I'm writing a custom ItemsControl (a tabbed document container), where each item (tab) can remove itself from the UI when the user closes it. However, I can't remove it directly from the ItemsControl.Items collection, because the items can be databound. So I have to remove it from the ItemsSource, which can be anything (ICollection, DataTable, DataSourceProvider...).

In the context of my application, I know what the actual type of the ItemsSource will be, but I want that control to be more generic so that I can reuse it later.

So I'm looking for a way to remove an item from a data source, without knowing its type. I could use reflection, but it feels dirty... So far the best solution I came up with is using dynamic :

    internal void CloseTab(TabDocumentContainerItem tabDocumentContainerItem)
    {
        // TODO prompt user for confirmation (CancelEventHandler ?)

        var item = ItemContainerGenerator.ItemFromContainer(tabDocumentContainerItem);

        // TODO find a better way...
        try
        {
            dynamic items = ItemsSource;
            dynamic it = item;
            items.Remove(it);
        }
        catch(RuntimeBinderException ex)
        {
            Trace.TraceError("Oops... " + ex.ToString());
        }
    }

But I'm not really happy with it, I'm sure there must be a better way. Any suggestions would be appreciated !

like image 436
Thomas Levesque Avatar asked Jul 17 '10 13:07

Thomas Levesque


2 Answers

The ItemCollection returned by ItemsControl.Items won't allow you to call Remove directly, but it implements IEditableCollectionView and does allow you to call the Remove method in that interface.

This will only work if the collection view bound to ItemsSource implements IEditableCollectionView itself. The default collection view will for most mutable collections, although not for objects that implement ICollection but not IList.

IEditableCollectionView items = tabControl.Items; //Cast to interface
if (items.CanRemove)
{
    items.Remove(tabControl.SelectedItem);
}
like image 133
Quartermeister Avatar answered Nov 16 '22 15:11

Quartermeister


OK, I found a solution...

  • If the ItemsSource is databound, I either raise an event (for use with code-behind) or invoke a command (for use with a ViewModel) to remove the item from the ItemsSource collection.

  • If it's not databound, I raise an event to prompt the user for confirmation, and I remove the container directly from Items

    public static readonly DependencyProperty CloseTabCommandProperty =
        DependencyProperty.Register(
            "CloseTabCommand",
            typeof(ICommand),
            typeof(TabDocumentContainer),
            new UIPropertyMetadata(null));
    
    public ICommand CloseTabCommand
    {
        get { return (ICommand)GetValue(CloseTabCommandProperty); }
        set { SetValue(CloseTabCommandProperty, value); }
    }
    
    public event EventHandler<RequestCloseTabEventArgs> RequestCloseTab;
    public event EventHandler<TabClosingEventArgs> TabClosing;
    
    internal void CloseTab(TabDocumentContainerItem tabDocumentContainerItem)
    {
        if (ItemsSource != null) // Databound
        {
            object item = ItemContainerGenerator.ItemFromContainer(tabDocumentContainerItem);
            if (item == null || item == DependencyProperty.UnsetValue)
            {
                return;
            }
            if (RequestCloseTab != null)
            {
                var args = new RequestCloseTabEventArgs(item);
                RequestCloseTab(this, args);
            }
            else if (CloseTabCommand != null)
            {
                if (CloseTabCommand.CanExecute(item))
                {
                    CloseTabCommand.Execute(item);
                }
            }
        }
        else // Not databound
        {
            if (TabClosing != null)
            {
                var args = new TabClosingEventArgs(tabDocumentContainerItem);
                TabClosing(this, args);
                if (args.Cancel)
                    return;
            }
            Items.Remove(tabDocumentContainerItem);
        }
    }
    
like image 34
Thomas Levesque Avatar answered Nov 16 '22 15:11

Thomas Levesque