L
Luis RamÃrez
Hello everybody
I have been working on this issue for two days and I still working on...... Basically what I want to do is the next
1. Create a custom web control (it works fine). *
2. Create a custom designer for my custom web control (it works fine)
3. Create a custom component editor (it works fine)
4. My component editor will launch a windows form that will have a combobox. This combobox will be fill with the datafields names that will be taken from de datasource already selected
** My custom web control has a datasource, datamember and datakeyfield properties
Here is a an example of my custom web control design-time behavior
1. I drop my custom web control from the toolbox
2. I drop a dataset and I add n tables and n fields to the tables
3. I assign values to the datasource, datamember and datakeyfield
4. I do right-click to my custom web control, I select "Load editor..." (that's my verb caption
5. A windows form is showed. This window will have a combobox with the fields names of the selected datasource
My problem is number 5. The fields are not showed, when I call DataFieldConverter.GetStandardValues() it return
and empty collection. Why
I have post my all code, my code also has comments about what is happening when I debug it.
If someone can help me I will appreciate a lot.
Thanks in advance
(sorry for my english
Luis RamÃrez
//***************************** custom web control, designer, editor *********
using System;using System.ComponentModel;using System.Collections;using System.ComponentModel.Design
using System.Windows.Forms.Design;using System.Windows.Forms;using System.Web.UI.Design
using System.Web.UI;using System.Diagnostics
namespace TestNamespac
//Step 1
[Designer(typeof(TestNamespace.CustomWebControlDesigner), typeof(IDesigner))
public class CustomWebControl : System.Web.UI.WebControls.WebContro
private object _dataSource
Bindable(true)
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden
public virtual object DataSource
get
return _dataSource
set
if ((value == null) || (value is IListSource) || (value is IEnumerable))
_dataSource = value
else
throw new ArgumentException()
public virtual string DataKeyField
get
string s = (string)ViewState["DataKeyField"]
return (s == null) ? String.Empty : s
set
ViewState["DataKeyField"] = value
public virtual string DataMember
get
string s = (string)ViewState["DataMember"]
return (s == null) ? String.Empty : s
set
ViewState["DataMember"] = value
//Step
public class CustomWebControlDesigner : System.Web.UI.Design.ControlDesigner, IDataSourceProvide
public override System.ComponentModel.Design.DesignerVerbCollection Verb
get
DesignerVerbCollection dvc = new DesignerVerbCollection()
dvc.Add(new DesignerVerb("Load editor...", new EventHandler(LoadEditor)))
return dvc
private void LoadEditor(object sender, EventArgs e
CustomWebControlEditor editor = new CustomWebControlEditor();
//I call EditComponent, I supposed that internally this call will be translated into EditComponent(null, this.Component, null), thats why context is always nul
//Is there any way I can call EditComponent directly, I mean replace Line 1(below) with something like: editor.EditComponent(context, this.Component, owner)
editor.EditComponent(this.Component); //Line
public string DataKeyField
get { return ((CustomWebControl)Component).DataKeyField;
set { ((CustomWebControl)Component).DataKeyField = value;
public string DataMember
get { return ((CustomWebControl)Component).DataMember;
set { ((CustomWebControl)Component).DataMember = value;
public string DataSource
get
DataBinding binding = DataBindings["DataSource"]
if (binding != null)
return binding.Expression
}
return String.Empty;
}
set
{
if ((value == null) || (value.Length == 0))
{
DataBindings.Remove("DataSource");
}
else
{
DataBinding binding = DataBindings["DataSource"];
if (binding == null)
{
binding = new DataBinding("DataSource", typeof(object), value);
}
else
{
binding.Expression = value;
}
DataBindings.Add(binding);
}
OnBindingsCollectionChanged("DataSource");
}
}
protected override void PreFilterProperties(IDictionary properties)
{
base.PreFilterProperties(properties);
PropertyDescriptor prop;
prop = (PropertyDescriptor)properties["DataSource"];
Debug.Assert(prop != null);
// We can't create the designer DataSource property based on the run-time property because these
// types do not match. Therefore, we have to copy over all the attributes from the runtime
// and use them that way.
System.ComponentModel.AttributeCollection runtimeAttributes = prop.Attributes;
Attribute[] attrs = new Attribute[runtimeAttributes.Count + 1];
runtimeAttributes.CopyTo(attrs, 0);
attrs[runtimeAttributes.Count] = new TypeConverterAttribute(typeof(DataSourceConverter));
prop = TypeDescriptor.CreateProperty(this.GetType(), "DataSource", typeof(string),
attrs);
properties["DataSource"] = prop;
prop = (PropertyDescriptor)properties["DataMember"];
Debug.Assert(prop != null);
prop = TypeDescriptor.CreateProperty(this.GetType(), prop,
new Attribute[] {
new TypeConverterAttribute(typeof(DataMemberConverter))
});
properties["DataMember"] = prop;
prop = (PropertyDescriptor)properties["DataKeyField"];
Debug.Assert(prop != null);
prop = TypeDescriptor.CreateProperty(this.GetType(), prop,
new Attribute[] {
new TypeConverterAttribute(typeof(DataFieldConverter))
});
properties["DataKeyField"] = prop;
}
#region Implementation of IDataSourceProvider
object IDataSourceProvider.GetSelectedDataSource()
{
object selectedDataSource = null;
DataBinding binding = DataBindings["DataSource"];
if (binding != null)
{
selectedDataSource = DesignTimeData.GetSelectedDataSource(Component, binding.Expression);
}
return selectedDataSource;
}
IEnumerable IDataSourceProvider.GetResolvedSelectedDataSource()
{
IEnumerable selectedDataSource = null;
DataBinding binding = DataBindings["DataSource"];
if (binding != null)
{
selectedDataSource = DesignTimeData.GetSelectedDataSource(Component, binding.Expression, DataMember);
}
return selectedDataSource;
}
#endregion
}
//step 3
public class CustomWebControlEditor : WindowsFormsComponentEditor
{
public override bool EditComponent(ITypeDescriptorContext context, object component, System.Windows.Forms.IWin32Window owner)
{
CustomWebControl myCustomWebControl = (CustomWebControl)component;
IServiceProvider site = myCustomWebControl.Site;
IComponentChangeService changeService = null;
DesignerTransaction transaction = null;
bool changed = false;
try
{
if (site!=null)
{
IDesignerHost designerHost= (IDesignerHost)site.GetService(typeof(IDesignerHost));
transaction = designerHost.CreateTransaction("TestTransaction");
changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
if (changeService!=null)
{
try
{
changeService.OnComponentChanging(myCustomWebControl, null);
}
catch (CheckoutException ex)
{
if (ex==CheckoutException.Canceled)
return false;
throw ex;
}
}
}
try
{
CustomWebControlEditorForm frmEditor = new CustomWebControlEditorForm(myCustomWebControl, context);
if (frmEditor.ShowDialog(owner) == DialogResult.OK)
changed = true;
}
finally
{
if (changed && (changeService != null))
changeService.OnComponentChanged(myCustomWebControl, null,null,null);
}
}
finally
{
if (transaction != null)
if (changed)
transaction.Commit();
else
transaction.Cancel();
}
return changed;
}
}
}
//****** editor ************ I just send de constructor.
public class CustomWebControlEditorForm : System.Windows.Forms.Form
//step 5
public CustomWebControlEditorForm(CustomWebControl myCustomWebControl, ITypeDescriptorContext context)
{
InitializeComponent();
//context is null, why? always context will be null?
PropertyDescriptor pd = TypeDescriptor.GetProperties(myCustomWebControl)["DataKeyField"];
//The microsoft documentation says that if I call DataFieldConverter.GetStandardValues() it will return the data fields for the selected datasource.
//Note: pd.Converter is of type DataFieldConverter.
TypeConverter.StandardValuesCollection svc = pd.Converter.GetStandardValues(context);
//I call GetStandardValues() without context just to see what happens.
ICollection coll = pd.Converter.GetStandardValues();
// When I call pd.Converter.GetStandardValuesSupported() an exception is raised. why? the microsoft documentation doesn't mention anything about it.
// I supposed it happens because context is null.
// if (pd.Converter.GetStandardValuesSupported())
// {
// svc = pd.Converter.GetStandardValues(context);
// coll = pd.Converter.GetStandardValues();
// }
//If I would have the fields, here a would fill a combobox with the fields names.
}
I have been working on this issue for two days and I still working on...... Basically what I want to do is the next
1. Create a custom web control (it works fine). *
2. Create a custom designer for my custom web control (it works fine)
3. Create a custom component editor (it works fine)
4. My component editor will launch a windows form that will have a combobox. This combobox will be fill with the datafields names that will be taken from de datasource already selected
** My custom web control has a datasource, datamember and datakeyfield properties
Here is a an example of my custom web control design-time behavior
1. I drop my custom web control from the toolbox
2. I drop a dataset and I add n tables and n fields to the tables
3. I assign values to the datasource, datamember and datakeyfield
4. I do right-click to my custom web control, I select "Load editor..." (that's my verb caption
5. A windows form is showed. This window will have a combobox with the fields names of the selected datasource
My problem is number 5. The fields are not showed, when I call DataFieldConverter.GetStandardValues() it return
and empty collection. Why
I have post my all code, my code also has comments about what is happening when I debug it.
If someone can help me I will appreciate a lot.
Thanks in advance
(sorry for my english
Luis RamÃrez
//***************************** custom web control, designer, editor *********
using System;using System.ComponentModel;using System.Collections;using System.ComponentModel.Design
using System.Windows.Forms.Design;using System.Windows.Forms;using System.Web.UI.Design
using System.Web.UI;using System.Diagnostics
namespace TestNamespac
//Step 1
[Designer(typeof(TestNamespace.CustomWebControlDesigner), typeof(IDesigner))
public class CustomWebControl : System.Web.UI.WebControls.WebContro
private object _dataSource
Bindable(true)
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden
public virtual object DataSource
get
return _dataSource
set
if ((value == null) || (value is IListSource) || (value is IEnumerable))
_dataSource = value
else
throw new ArgumentException()
public virtual string DataKeyField
get
string s = (string)ViewState["DataKeyField"]
return (s == null) ? String.Empty : s
set
ViewState["DataKeyField"] = value
public virtual string DataMember
get
string s = (string)ViewState["DataMember"]
return (s == null) ? String.Empty : s
set
ViewState["DataMember"] = value
//Step
public class CustomWebControlDesigner : System.Web.UI.Design.ControlDesigner, IDataSourceProvide
public override System.ComponentModel.Design.DesignerVerbCollection Verb
get
DesignerVerbCollection dvc = new DesignerVerbCollection()
dvc.Add(new DesignerVerb("Load editor...", new EventHandler(LoadEditor)))
return dvc
private void LoadEditor(object sender, EventArgs e
CustomWebControlEditor editor = new CustomWebControlEditor();
//I call EditComponent, I supposed that internally this call will be translated into EditComponent(null, this.Component, null), thats why context is always nul
//Is there any way I can call EditComponent directly, I mean replace Line 1(below) with something like: editor.EditComponent(context, this.Component, owner)
editor.EditComponent(this.Component); //Line
public string DataKeyField
get { return ((CustomWebControl)Component).DataKeyField;
set { ((CustomWebControl)Component).DataKeyField = value;
public string DataMember
get { return ((CustomWebControl)Component).DataMember;
set { ((CustomWebControl)Component).DataMember = value;
public string DataSource
get
DataBinding binding = DataBindings["DataSource"]
if (binding != null)
return binding.Expression
}
return String.Empty;
}
set
{
if ((value == null) || (value.Length == 0))
{
DataBindings.Remove("DataSource");
}
else
{
DataBinding binding = DataBindings["DataSource"];
if (binding == null)
{
binding = new DataBinding("DataSource", typeof(object), value);
}
else
{
binding.Expression = value;
}
DataBindings.Add(binding);
}
OnBindingsCollectionChanged("DataSource");
}
}
protected override void PreFilterProperties(IDictionary properties)
{
base.PreFilterProperties(properties);
PropertyDescriptor prop;
prop = (PropertyDescriptor)properties["DataSource"];
Debug.Assert(prop != null);
// We can't create the designer DataSource property based on the run-time property because these
// types do not match. Therefore, we have to copy over all the attributes from the runtime
// and use them that way.
System.ComponentModel.AttributeCollection runtimeAttributes = prop.Attributes;
Attribute[] attrs = new Attribute[runtimeAttributes.Count + 1];
runtimeAttributes.CopyTo(attrs, 0);
attrs[runtimeAttributes.Count] = new TypeConverterAttribute(typeof(DataSourceConverter));
prop = TypeDescriptor.CreateProperty(this.GetType(), "DataSource", typeof(string),
attrs);
properties["DataSource"] = prop;
prop = (PropertyDescriptor)properties["DataMember"];
Debug.Assert(prop != null);
prop = TypeDescriptor.CreateProperty(this.GetType(), prop,
new Attribute[] {
new TypeConverterAttribute(typeof(DataMemberConverter))
});
properties["DataMember"] = prop;
prop = (PropertyDescriptor)properties["DataKeyField"];
Debug.Assert(prop != null);
prop = TypeDescriptor.CreateProperty(this.GetType(), prop,
new Attribute[] {
new TypeConverterAttribute(typeof(DataFieldConverter))
});
properties["DataKeyField"] = prop;
}
#region Implementation of IDataSourceProvider
object IDataSourceProvider.GetSelectedDataSource()
{
object selectedDataSource = null;
DataBinding binding = DataBindings["DataSource"];
if (binding != null)
{
selectedDataSource = DesignTimeData.GetSelectedDataSource(Component, binding.Expression);
}
return selectedDataSource;
}
IEnumerable IDataSourceProvider.GetResolvedSelectedDataSource()
{
IEnumerable selectedDataSource = null;
DataBinding binding = DataBindings["DataSource"];
if (binding != null)
{
selectedDataSource = DesignTimeData.GetSelectedDataSource(Component, binding.Expression, DataMember);
}
return selectedDataSource;
}
#endregion
}
//step 3
public class CustomWebControlEditor : WindowsFormsComponentEditor
{
public override bool EditComponent(ITypeDescriptorContext context, object component, System.Windows.Forms.IWin32Window owner)
{
CustomWebControl myCustomWebControl = (CustomWebControl)component;
IServiceProvider site = myCustomWebControl.Site;
IComponentChangeService changeService = null;
DesignerTransaction transaction = null;
bool changed = false;
try
{
if (site!=null)
{
IDesignerHost designerHost= (IDesignerHost)site.GetService(typeof(IDesignerHost));
transaction = designerHost.CreateTransaction("TestTransaction");
changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
if (changeService!=null)
{
try
{
changeService.OnComponentChanging(myCustomWebControl, null);
}
catch (CheckoutException ex)
{
if (ex==CheckoutException.Canceled)
return false;
throw ex;
}
}
}
try
{
CustomWebControlEditorForm frmEditor = new CustomWebControlEditorForm(myCustomWebControl, context);
if (frmEditor.ShowDialog(owner) == DialogResult.OK)
changed = true;
}
finally
{
if (changed && (changeService != null))
changeService.OnComponentChanged(myCustomWebControl, null,null,null);
}
}
finally
{
if (transaction != null)
if (changed)
transaction.Commit();
else
transaction.Cancel();
}
return changed;
}
}
}
//****** editor ************ I just send de constructor.
public class CustomWebControlEditorForm : System.Windows.Forms.Form
//step 5
public CustomWebControlEditorForm(CustomWebControl myCustomWebControl, ITypeDescriptorContext context)
{
InitializeComponent();
//context is null, why? always context will be null?
PropertyDescriptor pd = TypeDescriptor.GetProperties(myCustomWebControl)["DataKeyField"];
//The microsoft documentation says that if I call DataFieldConverter.GetStandardValues() it will return the data fields for the selected datasource.
//Note: pd.Converter is of type DataFieldConverter.
TypeConverter.StandardValuesCollection svc = pd.Converter.GetStandardValues(context);
//I call GetStandardValues() without context just to see what happens.
ICollection coll = pd.Converter.GetStandardValues();
// When I call pd.Converter.GetStandardValuesSupported() an exception is raised. why? the microsoft documentation doesn't mention anything about it.
// I supposed it happens because context is null.
// if (pd.Converter.GetStandardValuesSupported())
// {
// svc = pd.Converter.GetStandardValues(context);
// coll = pd.Converter.GetStandardValues();
// }
//If I would have the fields, here a would fill a combobox with the fields names.
}