I have following class
public abstract class AbsTrinityEvent
{
public event IsSelected OnSelectedEvent;
bool _IsSelected;
ITrinityEvent _objTrinityEvent;
public AbsTrinityEvent(ITrinityEvent objTrinityEvent)
{
_objTrinityEvent = objTrinityEvent;
}
public ITrinityEvent TrinityEventObj
{
set
{
_objTrinityEvent = value;
}
get
{
return _objTrinityEvent;
}
}
public int EventRefID
{
get
{
return _objTrinityEvent.EventRefID;
}
}
public string EventDescription
{
get
{
return _objTrinityEvent.EventDescription;
}
}
public string EventDateTime
{
get
{
return _objTrinityEvent.EventDateTime;
}
}
public string Site
{
get
{
return _objTrinityEvent.Site;
}
}
public int Priority
{
get
{
return _objTrinityEvent.Priority;
}
}
public string DeviceName
{
get
{
return _objTrinityEvent.DeviceName;
}
}
public bool IsAlarm
{
get
{
return _objTrinityEvent.IsAlarm;
}
}
public string OperatorName
{
get
{
return _objTrinityEvent.OperatorName;
}
}
public int SiteID
{
get
{
return _objTrinityEvent.SiteID;
}
}
public int EventSrcInstanceID
{
get
{
return _objTrinityEvent.EventSrcInstanceID;
}
}
public int EventSrcInstanceMasterDeviceID
{
get
{
return _objTrinityEvent.EventSrcInstanceMasterDeviceID;
}
}
public bool IsSelected
{
set
{
_IsSelected = value;
ItemSelectedEventArgs obj = new ItemSelectedEventArgs(_objTrinityEvent);
OnSelectedEvent(this, obj);
}
get
{
return _IsSelected;
}
}
}
public class ItemSelectedEventArgs : EventArgs
{
private ITrinityEvent _objItem;
public ItemSelectedEventArgs(ITrinityEvent objItem)
{
_objItem = objItem;
}
public ITrinityEvent SlectedNode
{
get
{
return _objItem;
}
}
}
public sealed class TrinityEventData : AbsTrinityEvent
{
public TrinityEventData(ITrinityEvent objEvent)
: base(objEvent)
{
}
}
I am binding this to my listview in code behind ( Not in XAML ) using following function
public void SetupColumnsForUnAcklist()
{
//Create Columns for listview
GridView grdView = new GridView();
grdView.Columns.Add(new GridViewColumn() { DisplayMemberBinding = new Binding() { Path = new PropertyPath("EventDescription") }, Header = "Description" });
grdView.Columns.Add(new GridViewColumn() { DisplayMemberBinding = new Binding() { Path = new PropertyPath("EventDateTime") }, Header = "Date:Time" });
grdView.Columns.Add(new GridViewColumn() { DisplayMemberBinding = new Binding() { Path = new PropertyPath("Site") }, Header = "Site" });
grdView.Columns.Add(new GridViewColumn() { DisplayMemberBinding = new Binding() { Path = new PropertyPath("DeviceName") }, Header = "Device" });
grdView.Columns.Add(new GridViewColumn() { DisplayMemberBinding = new Binding() { Path = new PropertyPath("Priority") }, Header = "Priority" });
lstview_Unack.View = grdView;
//Do Binding
if (_alarmUnAckList != null)
{
lstview_Unack.SetBinding(ListView.ItemsSourceProperty, new Binding() { Source = _alarmUnAckList });
lstview_Unack.SetBinding(ListView.IsSelectedProperty, new Binding() { Path = new PropertyPath("IsSelected") });
}
lstview_Unack.ContextMenu = contextMenu;
foreach (GridViewColumn col in grdView.Columns)
{
comboColumnList.Items.Add(col.Header as string);
}
}
My problem is, I want bind ListViewItem "IsSelected" Property to the TrinityEventData's "IsSelected" Property. How I should I do it in code behind?
First off, you're much better off doing this in XAML. It makes things much clearer and shorter. I'm going to answer in both XAML and code-behind to demonstrate this.
The easiest way is to make a Style applied to ListViewItem and using a Setter to apply the binding. On a ListViewItem, the DataContext is going to be your bound item (TrinityEventData in this case).
Assuming you had your ListView in XAML:
<ListView x:Name="lstview_Unack">
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</ListView.Resources>
</ListView>
In code, you have to construct the Style, Setter, and Binding by hand:
Style listViewItemStyle = new Style { TargetType = typeof(ListViewItem) };
listViewItemStyle.Setters.Add(new Setter
{
Property = ListViewItem.IsSelectedProperty,
Value = new Binding { Path = new PropertyPath("IsSelected") }
});
lstview_Unack.Resources.Add(typeof(ListViewItem), listViewItemStyle);
There are issues with this and virtualization, however. If your ListViewItems get virtualized, you might be unselecting items in the ListView but the binding won't be firing because your ListViewItem won't exist.
What worked for me is:
<ListView x:Name="lstview_Unack">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
Related
Being new to WPF and MVVM I've been struggling for the last few days trying to solve this issue. I've searched all over stackoverflow and google/Youtube for help.
I have a DataGrid (biound from OrderListView) that is populated from a BindableCollection (Caliburn Micro) of a model. However I need to bring in a property ('Program') of linked data from another BindableCollection ProductList, (both collections share a common property 'Code'.
Basically I want the DataGrid to show all the OrderModel based columns and fill a column called Programs with the related data from the Products collection just at run time.
OrderModel.cs
public class OrderModel : BaseModel
{
private DateTime _orderDate;
public DateTime OrderDate
{
get { return _orderDate; }
set { _orderDate = value; OnPropertyChanged(); }
}
private string _code;
public string Code
{
get { return _code; }
set { _code = value; OnPropertyChanged(); }
}
private int _qty;
public int Qty
{
get { return _qty; }
set { _qty = value; OnPropertyChanged(); }
}
ProductModel.cs
public class ProductModel : BaseModel
{
private string _code;
public string Code
{
get { return _code; }
set { _code = value; OnPropertyChanged(); }
}
private int _program;
public int Program
{
get { return _program; }
set { _program = value; OnPropertyChanged(); }
}
DataGrid in OrderView.xaml
<DataGrid ItemsSource="{Binding OrderListView}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Code}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Qty}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Program}"/> <- This from ProductList ??
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
OrderViewModel.cs
public class OrderViewModel : Screen
{
private readonly IDataConnection _connect;
private ICollectionView _orderView;
public ICollectionView OrderListView
{
get => _orderView;
set
{
_orderView = value;
NotifyOfPropertyChange(() => OrderListView);
}
}
private BindableCollection<OrderModel> _orderList;
public BindableCollection<OrderModel> OrderList
{
get => _orderList;
set
{
_orderList = value;
NotifyOfPropertyChange(() => OrderList);
}
}
private BindableCollection<ProductModel> _productList;
public BindableCollection<ProductModel> ProductList
{
get { return _productList; }
set { _productList = value; }
}
private string _code;
public string Code
{
get { return _code; }
set { _code = value; NotifyOfPropertyChange(() => Code); }
}
private int _qty;
public int Qty
{
get { return _qty; }
set { _qty = value; NotifyOfPropertyChange(() => Qty); }
}
private int _program;
public int Program
{
get { return _program; }
set
{
_program = value;
NotifyOfPropertyChange(() => Program);
}
}
public OrderViewModel(IDataConnection connect)
{
DisplayName = "Orders";
var allOrders = await _connect.Orders_GetByDateRange(StartDate, EndDate);
OrderList = new BindableCollection<OrderModel>(allOrders);
OrderListView = CollectionViewSource.GetDefaultView(OrderList);
var allProducts = await _connect.Products_GetAll();
ProductList = new BindableCollection<ProductModel>(allProducts);
}
}
Basically where 'Code' Matches in the models i want to pull the associated Program into the column.
The ItemsSource that a DataGrid binds to needs to contain all the properties for the column bindings.
Which means either a Program property needs to be added to your current OrderModel or create a new model that contains both properties.
public class OrderModel : BaseModel
{
private DateTime _orderDate;
public DateTime OrderDate
{
get { return _orderDate; }
set { _orderDate = value; OnPropertyChanged(); }
}
private string _code;
public string Code
{
get { return _code; }
set { _code = value; OnPropertyChanged(); }
}
private int _qty;
public int Qty
{
get { return _qty; }
set { _qty = value; OnPropertyChanged(); }
}
private int _program;
public int Program
{
get { return _program; }
set { _program = value; OnPropertyChanged(); }
}
}
Then there will be logic needed to link the new Program value with the value from the other collection.
The cleanest place for this would most likely be to modify your query (assuming you are using a database connection)
Most likely this would be to include a JOIN when doing the selection for the Orders.
However, doing this on the client side, meaning after the data has been recieved from the IDataConnection would look like this:
using System.Linq;
...
public OrderViewModel(IDataConnection connect)
{
DisplayName = "Orders";
var allOrders = await _connect.Orders_GetByDateRange(StartDate, EndDate);
var allProducts = await _connect.Products_GetAll();
ProductList = new BindableCollection<ProductModel>(allProducts);
foreach(var order in allOrders)
{
//assumes "Code" is unique within `ProductList`
order.Program = ProductList.Single(p => p.Code == order.Code);
}
OrderList = new BindableCollection<OrderModel>(allOrders);
OrderListView = CollectionViewSource.GetDefaultView(OrderList);
}
I would like to enable in-place editing for cells in my GridControl. I have set property AllowEditing on TableView and Columns, but data in my cells still cannot be edited after doubleclick on cell. Only "copy" is enabled in context menu. I failed to find a solution.
NavigationStyle property in TableView is set to "Cell"
My xaml.
<dx:PLinqInstantFeedbackDataSource x:Name="PLinqInstantFeedbackDataSource" ListSource="{Binding Path=TestCollectionSource}" DefaultSorting="Property1 ASC"/>
<dxg:GridControl EnableSmartColumnsGeneration="True" ItemsSource="{Binding Path=Data, ElementName=PLinqInstantFeedbackDataSource}" SelectionMode="Cell" IsManipulationEnabled="True">
<dxg:GridControl.View>
<dxg:TableView AllowEditing="True" NavigationStyle="Cell" AllowFilterEditor="True" AlternateRowBackground="CornflowerBlue" ShowAutoFilterRow="True" />
</dxg:GridControl.View>
<dxg:GridControl.Columns>
<dxg:GridColumn FieldName="Property1" AllowEditing="True" ReadOnly="False" />
<dxg:GridColumn FieldName="Number" AllowEditing="True" ReadOnly="False" />
</dxg:GridControl.Columns>
</dxg:GridControl>
EDIT
ViewModel and ListSource class
public class MainWindowViewModel : ObservableObject
{
public MainWindowViewModel()
{
}
private BindingList<TestClass> testCollection;
public BindingList<TestClass> TestCollection
{
get
{
var result = this.testCollection;
if (null == result)
{
lock(this)
{
result = this.testCollection;
if (null == result)
{
result = new BindingList<TestClass>();
for (int i = 0; i < 1000000; i++)
{
result.Add(new TestClass() { Property1 = "test" + i, Number = i % 20 });
}
this.testCollection = result;
}
}
}
return result;
}
}
public IListSource TestCollectionSource
{
get { return new ListSource( ()=> this.TestCollection); }
}
}
public class ListSource : IListSource
{
public readonly Func<IList> innerListProvider;
public ListSource(Func<IList> innerListProvider)
{
this.innerListProvider = innerListProvider;
}
public IList GetList()
{
return this.innerListProvider();
}
public bool ContainsListCollection
{
get { return false; }
}
}
public class TestClass : ObservableObject
{
private string property1;
public string Property1
{
get { return this.property1; }
set
{
this.property1 = value;
RaisePropertyChanged(() => this.Property1);
}
}
private int number;
public int Number
{
get { return this.number; }
set
{
this.number = value;
RaisePropertyChanged(() => this.Number);
}
}
}
I have got an answer from DevExpress support.
PLinqInstantFeedbackDataSource is a read-only data source. If you wish to edit data directly in the grid, bind your source collection to the GridControl.ItemsSource property without using an Instant Feedback data provider.
I have a textbox and a Datagrid. The datagrid has two columns name and Email address. I want to Filter the datagrid values with the value in the textbox.
You can use a ICollectionView for the DataGrid ItemSource then you can apply a Filter predicate and refesh the list when needed.
Here is a very quick example.
Xaml:
<Window x:Class="WpfApplication10.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="188" Width="288" Name="UI" >
<StackPanel DataContext="{Binding ElementName=UI}">
<TextBox Text="{Binding FilterString, UpdateSourceTrigger=PropertyChanged}" />
<DataGrid ItemsSource="{Binding DataGridCollection}" />
</StackPanel>
</Window>
Code:
namespace WpfApplication10
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ICollectionView _dataGridCollection;
private string _filterString;
public MainWindow()
{
InitializeComponent();
DataGridCollection = CollectionViewSource.GetDefaultView(TestData);
DataGridCollection.Filter = new Predicate<object>(Filter);
}
public ICollectionView DataGridCollection
{
get { return _dataGridCollection; }
set { _dataGridCollection = value; NotifyPropertyChanged("DataGridCollection"); }
}
public string FilterString
{
get { return _filterString; }
set
{
_filterString = value;
NotifyPropertyChanged("FilterString");
FilterCollection();
}
}
private void FilterCollection()
{
if (_dataGridCollection != null)
{
_dataGridCollection.Refresh();
}
}
public bool Filter(object obj)
{
var data = obj as TestClass;
if (data != null)
{
if (!string.IsNullOrEmpty(_filterString))
{
return data.Name.Contains(_filterString) || data.Email.Contains(_filterString);
}
return true;
}
return false;
}
public IEnumerable<TestClass> TestData
{
get
{
yield return new TestClass { Name = "1", Email = "1#test.com" };
yield return new TestClass { Name = "2", Email = "2#test.com" };
yield return new TestClass { Name = "3", Email = "3#test.com" };
yield return new TestClass { Name = "4", Email = "4#test.com" };
yield return new TestClass { Name = "5", Email = "5#test.com" };
yield return new TestClass { Name = "6", Email = "6#test.com" };
yield return new TestClass { Name = "7", Email = "7#test.com" };
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public class TestClass
{
public string Name { get; set; }
public string Email { get; set; }
}
}
Result:
In my canonical example, I have two tabs the first with a button and the second with a text box that is bound to a validation rule. When the button is clicked an action occurs that should cause the validation to fail and therefore the Validation.Error event to fire. However, the event will only fire when I click the second tab.
The reason this is so important to me is I have a form with complex validation that occurs across multiple tabs and I want to highlight those tabs containing errors in some - and I especially want to display the errors when the form first loads.
I've already used a technique to force the validation to fire when the form loads but I just don't know why when the forms loaded it doesn't fire when the button is clicked.
The XAML for my test case :
<Window x:Class="WpfApplication1.TabsDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="Tabs" Height="300" Width="300" Validation.Error="Window_Error">
<TabControl Grid.Row="0">
<TabItem>
<TabItem.Header>First</TabItem.Header>
<StackPanel Margin="5">
<Button Click="Button_Click">Clear the second textbox</Button>
</StackPanel>
</TabItem>
<TabItem>
<TabItem.Header>MyDataItem</TabItem.Header>
<TextBox>
<TextBox.Text>
<local:ValidationBinding Path="MyDataItem" UpdateSourceTrigger="LostFocus">
<local:ValidationBinding.ValidationRules>
<local:ValidateText />
</local:ValidationBinding.ValidationRules>
</local:ValidationBinding>
</TextBox.Text>
</TextBox>
</TabItem>
</TabControl>
</Window>
My code behind :
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class TabsDemo : Window
{
public TabsDemo()
{
InitializeComponent();
DataContext = new MyViewModel();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
(DataContext as MyViewModel).MyDataItem = String.Empty;
}
private void Window_Error(object sender, ValidationErrorEventArgs e)
{
MessageBox.Show("Validation Error : " + e.Error.RuleInError);
}
}
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _myDataItem = "Default Value";
public string MyDataItem
{
get { return _myDataItem; }
set
{
if (_myDataItem != value)
{
_myDataItem = value;
NotifyPropertyChanged(new PropertyChangedEventArgs("MyDataItem"));
}
}
}
private void NotifyPropertyChanged(PropertyChangedEventArgs args)
{
if (PropertyChanged != null)
PropertyChanged(this, args);
}
}
}
And for completeness here's the validation binding markup extension :
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;
namespace WpfApplication1
{
public class ValidationBinding : MarkupExtension
{
private readonly Binding _binding = new Binding();
private DependencyObject _dependencyObject;
private DependencyProperty _dependencyProperty;
public ValidationBinding()
{
_binding.ValidatesOnDataErrors = true;
_binding.ValidatesOnExceptions = true;
_binding.NotifyOnValidationError = true;
}
public ValidationBinding(string path)
{
_binding.Path = new PropertyPath(path);
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var valueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
_dependencyObject = valueTarget.TargetObject as DependencyObject;
_dependencyProperty = valueTarget.TargetProperty as DependencyProperty;
var element = _dependencyObject as FrameworkElement;
if (element != null)
{
if (element.IsLoaded)
ForceValidation();
else
element.Loaded += (sender, args) => ForceValidation();
}
else
{
ForceValidation();
}
Debug.WriteLine("MarkupExtension.ProvideValue called for element " + element.Name);
return _binding.ProvideValue(serviceProvider);
}
private void ForceValidation()
{
BindingOperations.GetBindingExpression(_dependencyObject, _dependencyProperty).UpdateSource();
}
public object FallbackValue
{
get { return _binding.FallbackValue; }
set { _binding.FallbackValue = value; }
}
public string StringFormat
{
get { return _binding.StringFormat; }
set { _binding.StringFormat = value; }
}
public object TargetNullValue
{
get { return _binding.TargetNullValue; }
set { _binding.TargetNullValue = value; }
}
public string BindingGroupName
{
get { return _binding.BindingGroupName; }
set { _binding.BindingGroupName = value; }
}
public Collection<ValidationRule> ValidationRules
{
get { return _binding.ValidationRules; }
}
public bool ValidatesOnExceptions
{
get { return _binding.ValidatesOnExceptions; }
set { _binding.ValidatesOnExceptions = value; }
}
public bool ValidatesOnDataErrors
{
get { return _binding.ValidatesOnDataErrors; }
set { _binding.ValidatesOnDataErrors = value; }
}
public PropertyPath Path
{
get { return _binding.Path; }
set { _binding.Path = value; }
}
public string XPath
{
get { return _binding.XPath; }
set { _binding.XPath = value; }
}
public BindingMode Mode
{
get { return _binding.Mode; }
set { _binding.Mode = value; }
}
public UpdateSourceTrigger UpdateSourceTrigger
{
get { return _binding.UpdateSourceTrigger; }
set { _binding.UpdateSourceTrigger = value; }
}
public bool NotifyOnSourceUpdated
{
get { return _binding.NotifyOnSourceUpdated; }
set { _binding.NotifyOnSourceUpdated = value; }
}
public bool NotifyOnTargetUpdated
{
get { return _binding.NotifyOnTargetUpdated; }
set { _binding.NotifyOnTargetUpdated = value; }
}
public bool NotifyOnValidationError
{
get { return _binding.NotifyOnValidationError; }
set { _binding.NotifyOnValidationError = value; }
}
public IValueConverter Converter
{
get { return _binding.Converter; }
set { _binding.Converter = value; }
}
public object ConverterParameter
{
get { return _binding.ConverterParameter; }
set { _binding.ConverterParameter = value; }
}
public CultureInfo ConverterCulture
{
get { return _binding.ConverterCulture; }
set { _binding.ConverterCulture = value; }
}
public object Source
{
get { return _binding.Source; }
set { _binding.Source = value; }
}
public RelativeSource RelativeSource
{
get { return _binding.RelativeSource; }
set { _binding.RelativeSource = value; }
}
public string ElementName
{
get { return _binding.ElementName; }
set { _binding.ElementName = value; }
}
public bool IsAsync
{
get { return _binding.IsAsync; }
set { _binding.IsAsync = value; }
}
public object AsyncState
{
get { return _binding.AsyncState; }
set { _binding.AsyncState = value; }
}
public bool BindsDirectlyToSource
{
get { return _binding.BindsDirectlyToSource; }
set { _binding.BindsDirectlyToSource = value; }
}
public UpdateSourceExceptionFilterCallback UpdateSourceExceptionFilter
{
get { return _binding.UpdateSourceExceptionFilter; }
set { _binding.UpdateSourceExceptionFilter = value; }
}
}
}
Hi all this is my first question :)
This exemple tested on winform application and wpf application and the problem with binding on WPF
winform all works fine with ICustomTypeDescriptor and grid draw only columns added to Dictionary Properties (Name Age) and Male excluded
WPF all properties of the class person drawed on grid (Name Age Male)
any idea about this situation or interfaces equivalent of ICustomTypeDescriptor in wpf ?
<Grid>
<DataGrid AutoGenerateColumns="True" Height="200" HorizontalAlignment="Left" Margin="90,30,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="325" />
</Grid>
List<Person> persons = new List<Person>();
persons.Add(new Person("Aymane", 30));
persons.Add(new Person("Raouia", 30));
grid.ItemsSource = persons; //wpf
grid.DataSource = persons; //winform
public class Person : ICustomTypeDescriptor
{
Dictionary<string, object> Properties = new Dictionary<string, object>();
public Person()
{
Properties.Add("Name", null);
Properties.Add("Age", null);
}
public Person(string name, object value)
: base()
{
Male = true;
Name = name;
Age = value;
}
public bool Male { get; set; }
public object Age { get { return Properties["Age"]; } set { Properties["Age"] = value; } }
public object Name { get { return Properties["Name"]; } set { Properties["Name"] = value; } }
#region ICustomTypeDescriptor Members
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
string ICustomTypeDescriptor.GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
string ICustomTypeDescriptor.GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(attributes, true);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return ((ICustomTypeDescriptor)this).GetEvents(null);
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
List<PropertyDescriptor> props = new List<PropertyDescriptor>();
props.Add(new PersonPropertyDescriptor("Name", attributes));
props.Add(new PersonPropertyDescriptor("Age", attributes));
return new PropertyDescriptorCollection(props.ToArray());
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return ((ICustomTypeDescriptor)this).GetProperties(null);
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
#endregion
class PersonPropertyDescriptor : PropertyDescriptor
{
public PersonPropertyDescriptor(string name, Attribute[] attrs)
: base(name, attrs)
{
}
public override bool CanResetValue(object component)
{
return true;
}
public override Type ComponentType
{
get { return typeof(Person); }
}
public override object GetValue(object component)
{
return ((Person)component).Properties[Name];
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type PropertyType
{
get { return typeof(object); }
}
public override void ResetValue(object component)
{
((Person)component).Properties[Name] = null;
}
public override void SetValue(object component, object value)
{
((Person)component).Properties[Name] = value;
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
}
To gain control over the column generation handle the AutoGeneratingColumn event, have you can suppress the generation of a column by seting e.Cancel = true;
In your case:
private void DataGridAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var dataGrid = sender as DataGrid;
if (dataGrid != null)
{
ICustomTypeDescriptor typeDescriptor =
dataGrid.Items[0] as ICustomTypeDescriptor;
if (typeDescriptor != null)
{
var props = typeDescriptor.GetProperties();
if (!props.Contains((PropertyDescriptor)e.PropertyDescriptor))
{
e.Cancel = true;
}
}
}
}
With the DataGrid definition of:
<DataGrid
AutoGenerateColumns="True"
Height="311"
HorizontalAlignment="Left"
Name="dataGrid1"
VerticalAlignment="Top"
Width="509"
AutoGeneratingColumn="DataGridAutoGeneratingColumn">
Gives the desired result.
Here the correct implementation of ICustomTypeDescriptor & ITypedList
namespace CustomTypeDescriptor
{
class Row : ICustomTypeDescriptor { }
class RowsCollection : List<Row>, ITypedList { }
class Table : IListSource, IEnumerable<Row>, IEnumerator<Row>
{
RowsCollection Rows { get; set; }
}
}