Search code examples
wpfitemscontrolitemssource

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 !


Solution

  • 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);
          }
      }