I'm trying to implement some kind of Object Picker in WPF. So far I've created the Window with a DataGrid which ItemsSource is bound to an ObservableCollection. I also set AutoGenerateColumns to 'true' due to the fact that the Item to be picked can be any kind ob object. The Objects inside the Collection are wrapped in a SelectionWrapper< T> which contains an IsSelected Property in order to select them.
class SelectionWrapper<T> : INotifyPropertyChanged
{
// Following Properties including PropertyChanged
public bool IsSelected { [...] }
public T Model { [...] }
}
I also added a CustomColumn to the DataGrid.Columns in order to bind the IsSelected Property like so
<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding SourceView}">
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="Selected" Binding="{Binding IsSelected}" />
</DataGrid.Columns>
</DataGrid>
The result I get with this solution is not very satisfying because there is just my defined Column 'Selected' and two GeneratedColumns 'IsSelected' and 'Model'.
Is there a way to change the target for the AutoGeneration to display all Properties of Model instead? Also it is necessary to make the AutoGeneratedColumns ReadOnly because no one should edit the displayed Entries.
It is no option to turn off AutoGenerateColumns and add some more manual Columns like
<DataGridTextColumn Binding="{Binding Model.[SomeProperty]}"/>
because the Model can be of any kind of Object. Maybe there is a way to route the target for AutoGeneration to the Model Property?
Thanks in Advance
Edit
After accepting the Answer of @grek40 I came up with the following
First I created a general class of SelectionProperty which is inherited in SelectionProperty<T>
. Here I implement the Interface ICustomTypeDescriptor
which finally looked like:
public abstract class SelectionProperty : NotificationalViewModel, ICustomTypeDescriptor
{
bool isSelected = false;
public bool IsSelected
{
get { return this.isSelected; }
set
{
if (this.isSelected != value)
{
this.isSelected = value;
this.OnPropertyChanged("IsSelected");
}
}
}
object model = null;
public object Model
{
get { return this.model; }
set
{
if (this.model != value)
{
this.model = value;
this.OnPropertyChanged("Model");
}
}
}
public SelectionProperty(object model)
{
this.Model = model;
}
#region ICustomTypeDescriptor
[...]
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return TypeDescriptor.GetProperties(this.Model.GetType());
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
if (pd.DisplayName == "IsSelected")
return this;
return this.Model;
}
#endregion
Then I created a specialized ObservableCollection
class SelectionPropertyCollection<T> : ObservableCollection<T>, ITypedList
where T : SelectionProperty
{
public SelectionPropertyCollection(IEnumerable<T> collection) : base(collection)
{
}
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
return TypeDescriptor.GetProperties(typeof(T).GenericTypeArguments[0]);
}
public string GetListName(PropertyDescriptor[] listAccessors)
{
return null;
}
}
Well and the last thing is the ViewModel. The most significant lines are
class ObjectPickerViewModel<ObjectType> : BaseViewModel
{
public ICollectionView SourceView { get; set; }
SelectionPropertyCollection<SelectionProperty<ObjectType>> source = null;
public SelectionPropertyCollection<SelectionProperty<ObjectType>> Source
{
get { return this.source; }
set
{
if (this.source != value)
{
this.source = value;
this.OnPropertyChanged("Source");
}
}
}
// [...]
this.Source = new SelectionPropertyCollection<SelectionProperty<ObjectType>>(source.Select(x => new SelectionProperty<ObjectType>(x)));
this.SourceView = CollectionViewSource.GetDefaultView(this.Source);
}
The good thing here is, that I can still add more Columns in the XAML but also have all public Properties of the Wrapped Object!