6

我正在编写一个自定义ItemsControl(选项卡式文档容器),其中每个项目(选项卡)都可以在用户关闭它时从 UI 中删除。但是,我不能直接从ItemsControl.Items集合中删除它,因为这些项目可以是数据绑定的。所以我必须从 中删除它ItemsSource,它可以是任何东西(ICollection,,DataTable... DataSourceProvider)。

在我的应用程序的上下文中,我知道实际的类型是什么ItemsSource,但我希望该控件更通用,以便以后可以重用它。

所以我正在寻找一种方法来从数据源中删除一个项目,而不知道它的类型。我可以使用反射,但感觉很脏......到目前为止,我想出的最好的解决方案是使用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());
        }
    }

但我对此并不满意,我相信一定有更好的方法。任何建议,将不胜感激 !

4

4 回答 4

12

ItemCollection返回的 by不允许ItemsControl.Items您直接调用 Remove,但它实现IEditableCollectionView并且确实允许您在该接口中调用 Remove 方法。

这仅在绑定到ItemsSource实现IEditableCollectionView自身的集合视图时才有效。ICollection默认集合视图将适用于大多数可变集合,但不适用于实现但未实现的对象IList

IEditableCollectionView items = tabControl.Items; //Cast to interface
if (items.CanRemove)
{
    items.Remove(tabControl.SelectedItem);
}
于 2010-07-17T14:00:20.973 回答
2

好的,我找到了解决方案...

  • 如果它ItemsSource是数据绑定的,我要么引发一个事件(用于代码隐藏)或调用一个命令(用于 ViewModel)以从ItemsSource集合中删除该项目。

  • 如果它不是数据绑定的,我会引发一个事件来提示用户确认,然后我直接从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);
        }
    }
    
于 2010-07-17T14:15:31.070 回答
-1

正如您所发现的,您ItemsControl对被绑定的项目没有内在的了解——这些类型是由您控制的消费者提供的。而且您不能直接修改集合,因为它可能是数据绑定的。

诀窍是确保每个项目都由您选择的自定义类(项目容器)包装。你ItemsControl可以在GetContainerForItemOverride方法中提供这个。

从那里,您可以在自定义项目容器上定义属性,然后在默认模板中绑定到这些属性。例如,您可以有一个名为在、和State之间变化的属性。您的模板将使用此属性来确定如何 - 以及是否 - 显示该项目。DockedFloatingClosed

因此,您实际上根本不会更改底层数据源。相反,您将更改底层数据项之上的特定于控件的层,该层为您提供实现控件所需的信息。

于 2010-07-17T13:15:52.103 回答
-3

设计实践要求你应该真正知道你ItemsSource是什么并且能够直接从那里移除它。然后绑定当然会自动更新视图。

但是,如果您绝对打算使用某种通用功能进行删除,那么将您的ItemsSource转换为ICollectionICollection<T>然后调用Remove听起来比使用 .NET 的动态功能更好/更可靠。

于 2010-07-17T13:11:03.617 回答