I'm trying to extend the DataGridViewColumn's dialog with some properties. It works fine with the DataGrid control and the table styles, respectively column styles. With .NET 2.0 and DataGridViewColumn the extending component's "CanExtend" is still called with the DataGridViewColumn instances and returns "true", but no properties are being displayed in the edit columns dialog of the grid.
And now the question is: how can I extend the column properties using the IExtenderProvider-interface
The extender component is shown below.
using System; using System.ComponentModel; using System.Collections.Generic; using System.Windows.Forms; using System.Diagnostics; using System.Text;namespace UA.DataBinding { [ ProvideProperty("ColumnParser", typeof(DataGridViewColumn))][ ProvideProperty("ColumnFormatter", typeof(DataGridViewColumn))] public class UAGridFormatter : Component, IExtenderProvider, ISupportInitialize { public UAGridFormatter(){ InitializeComponent(); } public UAGridFormatter(IContainer container){ container.Add( this);InitializeComponent(); } [ Category( "Layout")][ DefaultValue(null)] public IParser GetColumnParser(DataGridViewColumn column){ return null;} [ Category( "Layout")][ DefaultValue(null)] public void SetColumnParser(DataGridViewColumn column, IParser parser){ } [ Category("Layout")][ DefaultValue(null)] public IFormatter GetColumnFormatter(DataGridViewColumn column){ return null;} [ Category( "Layout")][ DefaultValue(null)] public void SetColumnFormatter(DataGridViewColumn column, IFormatter formatter){ } public bool CanExtend(object extendee) { return (extendee is DataGridViewColumn);}
#region ISupportInitialize Member public void BeginInit(){ } public void EndInit(){ if (!DesignMode){ } } #endregion private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing){ if (disposing && (components != null)){ components.Dispose(); } base.Dispose(disposing);} #region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent(){ components = new System.ComponentModel.Container();} #endregion }} |

IExtenderProvider for DataGridViewColumn's doesn't work
Ilyck
probably, my description is a little bit imprecise. My problem relates to differences between DataGrid and DataGridView.
I wrote an extender with VS2003 to add some properties to the DataGridColumnStyle class. This works fine in VS2003 and VS2005. Doing the same with DataGridView, respectively DataGridViewColumn doesn't work.
So, the question is why The CanExtend of the DataGridView version is called for every column and the method returns true.
But I cannot see any of my properties in the column's properties.
Probably, the cause is that the editor for DataGridColumnStyle's is using real instances of derived classes whereas the DatasGridViewColumn editor is using another class (something like ListBoxItem).
The following component will add a property to the column style of a DataGrid:
And the next component won't add any property to the columns of a DataGridView:using
System;using
System.ComponentModel;using
System.Collections.Generic;using
System.Diagnostics;using
System.Text;using
System.Windows.Forms;using
System.Collections;namespace
UA.DataBinding{
[ProvideProperty("MyProperty", typeof(DataGridColumnStyle))]
public class DataGridExtender : Component, IExtenderProvider
{
private Hashtable mTable = new Hashtable();
public DataGridExtender()
{
InitializeComponent();
}
public DataGridExtender(IContainer container)
{
container.Add(this);
InitializeComponent();
}
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
Component Designer generated code/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
#region
IExtenderProvider Memberspublic bool CanExtend(object extendee)
{
return (extendee is DataGridColumnStyle);
}
#endregion
[Category("Layout")]
[DefaultValue(null)]
public void SetMyProperty(DataGridColumnStyle style, string value)
{
mTable[style] = value;
}
[Category("Layout")]
[DefaultValue(null)]
public string GetMyProperty(DataGridColumnStyle style)
{
return (string)mTable[style];
}
}
}
using
System;using
System.ComponentModel;using
System.Collections.Generic;using
System.Diagnostics;using
System.Text;using
System.Windows.Forms;using
System.Collections;namespace
UA.DataBinding{
[ProvideProperty("MyProperty", typeof(DataGridColumnStyle))]
public class DataGridViewExtender : Component, IExtenderProvider
{
private Hashtable mTable = new Hashtable();
public DataGridViewExtender()
{
InitializeComponent();
}
public DataGridViewExtender(IContainer container)
{
container.Add(this);
InitializeComponent();
}
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
Component Designer generated code/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
#region
IExtenderProvider Memberspublic bool CanExtend(object extendee)
{
return (extendee is DataGridViewColumn);
}
#endregion
[Category("Layout")]
[DefaultValue(null)]
public void SetMyProperty(DataGridViewColumn column, string value)
{
mTable[column] = value;
}
[Category("Layout")]
[DefaultValue(null)]
public string GetMyProperty(DataGridViewColumn column)
{
return (string)mTable[column];
}
}
}
Your hint regarding the GetPropertyOwner is correct. I'm using always the component which added the provider to the type descriptor. On the other hand, I have no idea how to get the correct instance of my component in GetPropertyOwner.
Is there any documentation about the architecture of the design time part of VS It's really hard to develop extensions based on try and error.
thanks a million,
Stephan
JeepATLGuy
I've tried your TypeDescriptor.AddProvider(...) hint and it seems to me that I'm reimplementing VS.
So, I've started with a
- TypeDescriptionProvider
returning an object implementing
- ICustomTypeDescriptor as result of TypeDescriptionProvider.GetExtendedTypeDescriptor().
The ICustomTypeDescriptor returns a collection of my personal - PropertyDescriptor objects wrapping the GetValue, SetValue methods and delegating them to the correct instances.
All this stuff gave me finally the desired properties in the DataGridViewColumn dialog. But it's not persistent!
By leaving the dialog with OK all the property values disappear, ie the properties are showning the default values the next time the dialog is opened. Is this problem caused by object identity (I'm using the objects as key of the hashtables).
How can I persist the properties
If I return "true" in PropertyDescriptor.ShouldSerializeValue(...) the serializer tries to initialize the "virtual" extended properties of the column. Do I have to write my own serializer
And why is Microsoft torturing innocent developers with such poorly conceived constructs. In VS 2003 I simply derived from IExtenderProvider and everything works almost fine...
- Stephan
Krijn
What this means is that only objects that show up in the main propertygrid will support extenders. The attribute DesignTimeVisible(false) controls whether instances of that class show up in the propertygrid of VS -- as opposed to showing up in a popup editor.
Its an unfortunate current limitation of the design.
Take a look at TypeDescriptor.AddProvider(instance). This will allow you to add properties on instances that are not "sited" in the designer -- but you know about the instaces. Hope this helps.
-Ben
ArchMcFry
Ben is correct - we do not support extended properties in the DataGridView's edit column dialog because the columns contained there are not sited, but extended properties do work when you select the column in the Visual Studio property grid since the column is sited there.< xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
-mark
DataGridView Program Manager
Microsoft
This post is provided "as-is"
svega
So are you saying that GetValue and SetValue are not being called in your code as expected
If you are keying off a separate object (returning some other object for GetPropertyOwner), then make sure you are actually using more than one object -- otherwise, you are just setting a global every time.
MorningStar
Notice that if you select the column in the property grid, the extender will appear (after you fix the type to target "DataGridViewColumn" instead of "DataGridColumnStyle"). This is because the Column is "sited" when in the designer -- but not in the editor.
You are almost there with your TypeDescriptionProvider fix. What you can do to track the correct instance is have two different TypeDescriptionProviders.
1. Create a TypeDescriptionProvider targetting "typeof(DataGridViewColumn)". Capture CreateInstance on the first Provider. Forward to the base Provider -- and then use the instance to register a NEW Provider targetting "instance" (the newly created instance). That is the sole job of the first provider.
2. The second provider (that targets a specific instance) is the one where you send back a custom type descriptor with a new property. You should have the correct instance now because you are creating a separate provider for each column instance.
It is a lot of work and sorry for the pain. Unfortunately, editors tend to not be very consistent in thier behavior (in terms of them siting their objects -- which allows extenders).
Note that another option you have is you could try to clobber the ColumnEditor and create your own. Try calling TypeDescriptor.AddAttribute on typeof(DataGridViewColumn) and replace the EditorAttribute. In general this is not a great idea since if two customers do this, one of thier editors will be disabled.
JimmyFo
The workaround -- selection in the property grid -- works fine, so I'm tending to take this way.
Additionally, I will follow up the TypeDescriptionProvider solution in order to have a fall-back solution. Here, my problem is code generation. How to recurve the initialisation of properties to the extending component in both directions (serialization and deserialization).
-Stephan
graydippy
Stephen,
could you please share your code
I've tried your TypeDescriptor.AddProvider(...) hint and it seems to me that I'm reimplementing VS.
So, I've started with a
- TypeDescriptionProvider
returning an object implementing
- ICustomTypeDescriptor as result of TypeDescriptionProvider.GetExtendedTypeDescriptor().
The ICustomTypeDescriptor returns a collection of my personal - PropertyDescriptor objects wrapping the GetValue, SetValue methods and delegating them to the correct instances.
Some how I still cant get it to work even though I created two classes and doing everything that I know.
Any help would be greatly appreciated.