在UserControls之间传递具有不同类型参数的泛型类

时间:2010-12-08 21:47:24

标签: c# wpf generics user-controls covariance

我目前有几个不同的用户控件提供相同的功能:三个不同的按钮,名为Select All,Deselect All和Toggle Selected。这些操作对在每个用户控件中实现我的ICheckable接口的项列表执行操作。我想统一这些东西,以便命令和按钮都只在一个地方定义 - 一个新的用户控件 - 而不是在两个不同的用户控件中复制。我的问题是,在一个用户控件中,我正在处理我的Template类的列表,而另一个用户控件有一个Defect类的列表。 TemplateDefect都会实现ICheckable,这意味着全部选择,取消全选和切换选择都适用于它们。

我有一个通用容器类SelectableItems<T>,需要T符合这些约束:where T : ICheckable, IEquatable<T>, IDeepCloneable<T>SelectableItems<T>提供了ObservableCollection<T> Items属性以及其他有用的属性,例如bool IsAnyItemSelectedT SelectedItem等。这些属性将用于实现Select All等命令。 TemplateDefect都适合所有这些约束。我打算在我的新用户控件中创建一个依赖项属性,我将从其他用户控件绑定SelectableItems<Template>SelectableItems<Defect>属性。我认为不可能做一个通用依赖属性,因为我使用XAML时不能有一个通用的UserControl类。我该怎么办呢?我正在使用.NET 3.5。

总结一下,这就是我想要的:

TemplateList user control                             ItemSelection user control
-------------------------------------------           --------------------------
SelectableItems<Template> TemplateContainer ==Bind==> unknownType? ItemContainer

DefectList user control                           ItemSelection user control
---------------------------------------           --------------------------
SelectableItems<Defect> DefectContainer ==Bind==> unknownType? ItemContainer

修改:我考虑为ItemSelection视图模型中的所有有用属性添加依赖项属性到我的新SelectableItems<T>用户控件,例如IsAnyItemSelected对于大多数属性来说都没问题,但是对于ObservableCollection<T> Items我做的很犹豫,因为我遇到了如上所述的相同的通用问题,如果我只是我不相信事情可以正常工作使用IEnumerable代替ObservableCollection<something>。也许我应该创建一个非通用的ObservableCollection类(比如在this question中)?

1 个答案:

答案 0 :(得分:0)

创建非通用ObservableCollection类,然后使用值转换器将ObservableCollection<T>值转换为ObservableCollection似乎已经有效。

以下是我ObservableCollection课程的重要部分:

public class ObservableCollection : ICollection<object>,
    INotifyCollectionChanged
{
    private ObservableCollection<object> _collection;

    public ObservableCollection()
    {
        _collection = new ObservableCollection<object>();
        _collection.CollectionChanged +=
            new NotifyCollectionChangedEventHandler(collectionChanged);
    }

    public ObservableCollection(IEnumerable items)
    {
        if (null == items)
        {
            throw new ArgumentNullException("items");
        }
        _collection = new ObservableCollection<object>();
        foreach (object item in items)
        {
            Add(item);
        }
        _collection.CollectionChanged +=
            new NotifyCollectionChangedEventHandler(collectionChanged);
    }

    ...stuff necessary to implement ICollection<object>...

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    void collectionChanged(object sender,
        NotifyCollectionChangedEventArgs e)
    {
        NotifyCollectionChangedEventHandler handler = CollectionChanged;
        if (null != handler)
        {
            handler(this, e);
        }
    }
}

这是价值转换器:

public class EnumerableToObservableCollectionConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
        CultureInfo culture)
    {
        if (targetType != typeof(ObservableCollection))
        {
            throw new ArgumentException("Do not use this converter except " +
                "when going to ObservableCollection");
        }
        var enumerable = value as IEnumerable;
        if (null == enumerable)
        {
            return new ObservableCollection();
        }
        return new ObservableCollection(enumerable);
    }

    public object ConvertBack(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        return value;
    }
}

我这样绑定:

<local:ItemSelection SelectedItems="{Binding Path=MyViewModel.SelectedItems,
    Mode=OneWay}"
    Items="{Binding Path=MyViewModel.Items, Mode=OneWay,
        Converter={StaticResource observCollConverter}}"
    IsAnyItemSelected="{Binding Path=MyViewModel.IsAnyItemSelected,
        Mode=OneWay}"/>