passing generic class with different type parameters between UserControls
I currently have a couple different user controls that provide the same functionality: three different buttons called Select All, Deselect All, and Toggle Selected. These perform actions on a list of items that implement my ICheckable
interface in each user control. I wanted to unify this stuff so that the commands and buttons would all be defined in one place only--a new user control--instead of being duplicated in two different user controls. My problem is that in one user control I'm dealing with a list of my Template
class, and the other user control has a list of a Defect
class. Both Template
and Defect
implement ICheckable
, meaning the Select All, Deselect All, and Toggle Selected apply to them.
I have a generic container class SelectableItems<T>
that requires T
fit these constraints: where T : ICheckable, IEquatable<T>, IDeepCloneable<T>
. SelectableItems<T>
provides an ObservableCollection<T> Items
property, along with other useful properties such as bool IsAnyItemSelected
, T SelectedItem
, etc. These properties would be used in implementing the Select All, etc. commands. Both Template
and Defect
fit all those constraints. I was going to create a dependency property in my new user control to which I would bind a SelectableItems<Template>
and SelectableItems<Defect>
property from my other user controls. I don't think it's possible to do a generic dependency property, though, because I can't have a generic UserControl
class since I'm using XAML. How should I go about this? I'm using .NET 3.5.
To sum up, this is what I want:
TemplateList user con开发者_运维技巧trol ItemSelection user control
------------------------------------------- --------------------------
SelectableItems<Template> TemplateContainer ==Bind==> unknownType? ItemContainer
DefectList user control ItemSelection user control
--------------------------------------- --------------------------
SelectableItems<Defect> DefectContainer ==Bind==> unknownType? ItemContainer
Edit: I considered just adding dependency properties to my new ItemSelection
user control for all the useful properties in the SelectableItems<T>
view model, such as IsAnyItemSelected
, etc. That would be fine for most of the properties, but I was hesitant to do it for ObservableCollection<T> Items
because I hit the same generic problem as described above, and I didn't trust things to work okay if I just used IEnumerable
instead of ObservableCollection<something>
. Maybe I should make an ObservableCollection
class that isn't generic (like in this question)?
Creating a non-generic ObservableCollection
class and then using a value converter to convert my ObservableCollection<T>
value to an ObservableCollection
seems to have worked.
Here are the important parts of my ObservableCollection
class:
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);
}
}
}
And here's the value converter:
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;
}
}
And I bind like so:
<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}"/>
精彩评论