Dynamically Building Property Values for Designer

P

paul reed

Hello,

I would like to source a property for my custom web control from a database.
I see examples out there that are sourced from an enum, but that is
hardcoded and this I needs to load dynamically. Is it as simples as having a
property that returns and ArrayList loaded with those property values?

Thanks in advance,

Paul
 
K

Kevin Yu [MSFT]

Hi Paul,

We are currently working on this issue, and will update you ASAP.

Kevin Yu
=======
"This posting is provided "AS IS" with no warranties, and confers no
rights."
 
J

Jeffrey Tan[MSFT]

Hi paul,

Thank you for posting in the community!

Can you explain "source a property for my custom web control from a
database" more detailed?

Also, you can refer us to your "hardcoded enum sample", then we can
understand what you want.

I will wait for your feedback, thanks.


Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
P

Paul Reed

Jeffrey,

Hi....

Let's say my custom control has a property called District. Using the
property designer at design time, they can select properties for the
control and see this property. When they click on the District, I wan
their to be a drop down of the valid Districts they can choose from.
This valid list of disticts needs to come from a database and not be
hard coded because the number of districts can change over time.

In other examples I have seen, the custom control declares an enum
structure with the allowable values. Then the property just returns a
data type of the enum. That is what I meant be enum. However, this
though limits you to only the values specified in the enum...I need mine
to be derived from a database, because the number of districts are
fluid. I hope that clarifies it for you.

Thanks,

Paul Reed
www.jacksonreed.com
 
A

Alessandro Zifiglio

hi Paul, I know you got his covered Jeffery and hope you dont mind, but I
got a quick answer here coz this is something i uselessly implemented into a
custom control i were building last month and then after all that
work(homework) had to remove it coz the needs of my control changed, lol ;P

Luckily I had come by an article online written by Shaun Wilde
http://www.codeproject.com/aspnet/webcontrolsdatabinding.asp to whom i'm
eternally grateful, and converted his C# implementation to vb.net. So I
really didnt do too much work ;P

Ok so, if i'm not wrong your looking for something like a datasource
property exposed by your control, and when the datasource is selected, you
could have them use the dataMember property to select a particular table
from within the supplied data source and if that is not enough, you can
further expose a DataTextField and DataValueField properties to select a
particular field from the default or selected table(datamember) from the
already selected data source.

Your going to have to use your designer class for this and implement the
IDataSourceProvider interface. IDataSourceProvider defines an interface that
a control designer can implement to provide access to a data source. That
said, when implementing this interface in your designer class you must
override : GetResolvedSelectedDataSource and GetSelectedDataSource. Your
going to have to also override the PreFilterProperties method in your
designer class. This is the method that binds the controls
DataSource,DataTextField,DataValueField,DataMember property and the
designers DataSource, DataTextField, DataValueField, DataMember property
together. This is how you see the dropdowns when selecting the datamember
property for instance showing the available tables.

The IDataSourceProvider interface is also used by DataSourceConverter ,
DataFieldConverter and DataMemberConverter which you will be using in your
designer class.

Here is what you need to do in your control first, depending on what
properties you want to expose, I'm exposing all three properties,
Datasource, DataMember and DataField. Comment out the ones you dont want ;P

in your control class :

<Bindable(True), _
DefaultValue(""), _
Category("Data"), _
Description("The data source.")> _
Public Property DataTextField() As String
Get
Return _dataTextField
End Get
Set(ByVal Value As String)
_dataTextField = Value
End Set
End Property

<Bindable(True), _
DefaultValue(""), _
Category("Data"), _
Description("select a particular field from the default or
selected table")> _
Public Property DataValueField() As String
Get
Return _dataValueField
End Get
Set(ByVal Value As String)
_dataValueField = Value
End Set
End Property

<Bindable(True), _
DefaultValue(""), _
Category("Data"), _
Description("The table from within the selected datasource.")> _
Public Property DataMember() As String
Get
Return _dataMember
End Get
Set(ByVal Value As String)
_dataMember = Value
End Set
End Property




now your designer class :
Implements IDataSourceProvider


'We use the designer to allow us to represent DataSource
'which is an object type property as a string type.
'To this designer class we add a DataSource property that is of type
string
'This property of type string is needed to wire the DataSource
property of the
'control at design-time

Public Property DataSource() As String
Get
Dim binding As DataBinding = DataBindings("DataSource")

If Not (binding Is Nothing) Then
Return binding.Expression
End If
Return [String].Empty
End Get
Set(ByVal Value As String)
If Value Is Nothing Or Value.Length = 0 Then
DataBindings.Remove("DataSource")
Else
Dim binding As DataBinding = DataBindings("DataSource")

If binding Is Nothing Then
binding = New DataBinding("DataSource",
GetType(IEnumerable), Value)
Else
binding.Expression = Value
End If
DataBindings.Add(binding)
End If
'This is the method that actually adds the <%# %>
OnBindingsCollectionChanged("DataSource")
End Set
End Property


'The DataMember is used to select a particular table from within
'a supplied data source such as a DataSet or if it is empty
'then the control should use the first available table or DataView.
'All of the work to implement a DataMember property such that in
'the Properties window it will be represented as a combobox with a
'list of available tables is done in the designer class.
Public Property DataMember() As String
Get
Return CType(Me.Component, MyControl).DataMember
End Get
Set(ByVal Value As String)
CType(Me.Component, MyControl).DataMember = Value
End Set
End Property
'The DataTextField and DataValueField properties are to be
'used to select a particular field from the default or selected
'table from a preselected data source. Again all the work required
to
'allow us to select from a list of available fields is also done in
this class.
'We add a property that is used to attach the required type
converter,
'which is DataFieldConverter, and we also add the type converter to
'the attributes in the PreFilterProperties method.
Public Property DataTextField() As String
Get
Return CType(Me.Component, MyControl).DataTextField
End Get
Set(ByVal Value As String)
CType(Me.Component, MyControl).DataTextField = Value
End Set
End Property


Public Property DataValueField() As String
Get
Return CType(Me.Component, MyControl).DataValueField
End Get
Set(ByVal Value As String)
CType(Me.Component, MyControl).DataValueField = Value
End Set
End Property





' Adding a type converter called
DataSourceConverter,DataFieldConverter,DataMemberConverter to the above
property
'so that it will correctly enumerate the available data
'sources that exist on the WebForm and present them in the
'Properties window as a combobox.
'To add this converter we need to override the PreFilterProperties
method
'and add the TypeConverter attribute dynamically to the
'DataSource,DataTextField,DataValueField,DataMember property at
Design runtime
'This is the glue that binds the controls
'DataSource,DataTextField,DataValueField,DataMember property and
'the designers DataSource,DataTextField,DataValueField,DataMember
property
'together as well as gives us the
'dropdown list of the datasources available to choose from.

Protected Overrides Sub PreFilterProperties(ByVal properties As
IDictionary)
MyBase.PreFilterProperties(properties)
Dim prop As PropertyDescriptor = CType(properties("DataSource"),
PropertyDescriptor)
If Not (prop Is Nothing) Then
Dim runtimeAttributes As AttributeCollection =
prop.Attributes
Dim attrs(runtimeAttributes.Count) As Attribute

runtimeAttributes.CopyTo(attrs, 0)
attrs(runtimeAttributes.Count) = New
TypeConverterAttribute(GetType(DataSourceConverter))
prop = TypeDescriptor.CreateProperty(Me.GetType(),
"DataSource", GetType(String), attrs)
properties("DataSource") = prop
End If

prop = CType(properties("DataMember"), PropertyDescriptor)
If Not (prop Is Nothing) Then
Dim runtimeAttributes As AttributeCollection =
prop.Attributes
Dim attrs(runtimeAttributes.Count) As Attribute

runtimeAttributes.CopyTo(attrs, 0)
attrs(runtimeAttributes.Count) = New
TypeConverterAttribute(GetType(DataMemberConverter))
prop = TypeDescriptor.CreateProperty(Me.GetType(),
"DataMember", GetType(String), attrs)
properties("DataMember") = prop
End If

prop = CType(properties("DataValueField"), PropertyDescriptor)
If Not (prop Is Nothing) Then
Dim runtimeAttributes As AttributeCollection =
prop.Attributes
Dim attrs(runtimeAttributes.Count) As Attribute

runtimeAttributes.CopyTo(attrs, 0)
attrs(runtimeAttributes.Count) = New
TypeConverterAttribute(GetType(DataFieldConverter))
prop = TypeDescriptor.CreateProperty(Me.GetType(),
"DataValueField", GetType(String), attrs)
properties("DataValueField") = prop
End If

prop = CType(properties("DataTextField"), PropertyDescriptor)
If Not (prop Is Nothing) Then
Dim runtimeAttributes As AttributeCollection =
prop.Attributes
Dim attrs(runtimeAttributes.Count) As Attribute

runtimeAttributes.CopyTo(attrs, 0)
attrs(runtimeAttributes.Count) = New
TypeConverterAttribute(GetType(DataFieldConverter))
prop = TypeDescriptor.CreateProperty(Me.GetType(),
"DataTextField", GetType(String), attrs)
properties("DataTextField") = prop
End If

End Sub 'PreFilterProperties


Function GetResolvedSelectedDataSource() As IEnumerable Implements
IDataSourceProvider.GetResolvedSelectedDataSource
Dim binding As DataBinding
binding = Me.DataBindings("DataSource")
If Not (binding Is Nothing) Then
Return DesignTimeData.GetSelectedDataSource(Me.Component,
binding.Expression, Me.DataMember)
End If
Return Nothing
End Function 'IDataSourceProvider.GetResolvedSelectedDataSource


Function GetSelectedDataSource() As Object Implements
IDataSourceProvider.GetSelectedDataSource
Dim binding As DataBinding
binding = Me.DataBindings("DataSource")
If Not (binding Is Nothing) Then
Return DesignTimeData.GetSelectedDataSource(Me.Component,
binding.Expression)
End If
Return Nothing
End Function 'IDataSourceProvider.GetSelectedDataSource
 
A

Alessandro Zifiglio

Forgot to mention that _datasource, _datatextField, _dataValueField,
_dataMember are declared as objects in your control class and most
importantly the Datasource property ;P

Private _dataSource As Object = Nothing
Private _dataTextField As String = Nothing
Private _dataValueField As String = Nothing
Private _dataMember As String = Nothing

<Bindable(True), _
DefaultValue(""), _
Category("Data"), _
Description("The data source used to build up the control."), _

DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
Public Property DataSource() As Object
Get
Return _dataSource
End Get
Set(ByVal Value As Object)
If (Value Is Nothing) Or (TypeOf Value Is IListSource) Or
(TypeOf Value Is IEnumerable) Then
_dataSource = Value
Else
Throw New Exception("Invalid datasource.")
End If
End Set
End Property

My apologies for the confusion ;)
Alessandro Zifiglio said:
hi Paul, I know you got his covered Jeffery and hope you dont mind, but I
got a quick answer here coz this is something i uselessly implemented into a
custom control i were building last month and then after all that
work(homework) had to remove it coz the needs of my control changed, lol ;P

Luckily I had come by an article online written by Shaun Wilde
http://www.codeproject.com/aspnet/webcontrolsdatabinding.asp to whom i'm
eternally grateful, and converted his C# implementation to vb.net. So I
really didnt do too much work ;P

Ok so, if i'm not wrong your looking for something like a datasource
property exposed by your control, and when the datasource is selected, you
could have them use the dataMember property to select a particular table
from within the supplied data source and if that is not enough, you can
further expose a DataTextField and DataValueField properties to select a
particular field from the default or selected table(datamember) from the
already selected data source.

Your going to have to use your designer class for this and implement the
IDataSourceProvider interface. IDataSourceProvider defines an interface that
a control designer can implement to provide access to a data source. That
said, when implementing this interface in your designer class you must
override : GetResolvedSelectedDataSource and GetSelectedDataSource. Your
going to have to also override the PreFilterProperties method in your
designer class. This is the method that binds the controls
DataSource,DataTextField,DataValueField,DataMember property and the
designers DataSource, DataTextField, DataValueField, DataMember property
together. This is how you see the dropdowns when selecting the datamember
property for instance showing the available tables.

The IDataSourceProvider interface is also used by DataSourceConverter ,
DataFieldConverter and DataMemberConverter which you will be using in your
designer class.

Here is what you need to do in your control first, depending on what
properties you want to expose, I'm exposing all three properties,
Datasource, DataMember and DataField. Comment out the ones you dont want ;P

in your control class :

<Bindable(True), _
DefaultValue(""), _
Category("Data"), _
Description("The data source.")> _
Public Property DataTextField() As String
Get
Return _dataTextField
End Get
Set(ByVal Value As String)
_dataTextField = Value
End Set
End Property

<Bindable(True), _
DefaultValue(""), _
Category("Data"), _
Description("select a particular field from the default or
selected table")> _
Public Property DataValueField() As String
Get
Return _dataValueField
End Get
Set(ByVal Value As String)
_dataValueField = Value
End Set
End Property

<Bindable(True), _
DefaultValue(""), _
Category("Data"), _
Description("The table from within the selected datasource.")> _
Public Property DataMember() As String
Get
Return _dataMember
End Get
Set(ByVal Value As String)
_dataMember = Value
End Set
End Property




now your designer class :
Implements IDataSourceProvider


'We use the designer to allow us to represent DataSource
'which is an object type property as a string type.
'To this designer class we add a DataSource property that is of type
string
'This property of type string is needed to wire the DataSource
property of the
'control at design-time

Public Property DataSource() As String
Get
Dim binding As DataBinding = DataBindings("DataSource")

If Not (binding Is Nothing) Then
Return binding.Expression
End If
Return [String].Empty
End Get
Set(ByVal Value As String)
If Value Is Nothing Or Value.Length = 0 Then
DataBindings.Remove("DataSource")
Else
Dim binding As DataBinding = DataBindings("DataSource")

If binding Is Nothing Then
binding = New DataBinding("DataSource",
GetType(IEnumerable), Value)
Else
binding.Expression = Value
End If
DataBindings.Add(binding)
End If
'This is the method that actually adds the <%# %>
OnBindingsCollectionChanged("DataSource")
End Set
End Property


'The DataMember is used to select a particular table from within
'a supplied data source such as a DataSet or if it is empty
'then the control should use the first available table or DataView.
'All of the work to implement a DataMember property such that in
'the Properties window it will be represented as a combobox with a
'list of available tables is done in the designer class.
Public Property DataMember() As String
Get
Return CType(Me.Component, MyControl).DataMember
End Get
Set(ByVal Value As String)
CType(Me.Component, MyControl).DataMember = Value
End Set
End Property
'The DataTextField and DataValueField properties are to be
'used to select a particular field from the default or selected
'table from a preselected data source. Again all the work required
to
'allow us to select from a list of available fields is also done in
this class.
'We add a property that is used to attach the required type
converter,
'which is DataFieldConverter, and we also add the type converter to
'the attributes in the PreFilterProperties method.
Public Property DataTextField() As String
Get
Return CType(Me.Component, MyControl).DataTextField
End Get
Set(ByVal Value As String)
CType(Me.Component, MyControl).DataTextField = Value
End Set
End Property


Public Property DataValueField() As String
Get
Return CType(Me.Component, MyControl).DataValueField
End Get
Set(ByVal Value As String)
CType(Me.Component, MyControl).DataValueField = Value
End Set
End Property





' Adding a type converter called
DataSourceConverter,DataFieldConverter,DataMemberConverter to the above
property
'so that it will correctly enumerate the available data
'sources that exist on the WebForm and present them in the
'Properties window as a combobox.
'To add this converter we need to override the PreFilterProperties
method
'and add the TypeConverter attribute dynamically to the
'DataSource,DataTextField,DataValueField,DataMember property at
Design runtime
'This is the glue that binds the controls
'DataSource,DataTextField,DataValueField,DataMember property and
'the designers DataSource,DataTextField,DataValueField,DataMember
property
'together as well as gives us the
'dropdown list of the datasources available to choose from.

Protected Overrides Sub PreFilterProperties(ByVal properties As
IDictionary)
MyBase.PreFilterProperties(properties)
Dim prop As PropertyDescriptor = CType(properties("DataSource"),
PropertyDescriptor)
If Not (prop Is Nothing) Then
Dim runtimeAttributes As AttributeCollection =
prop.Attributes
Dim attrs(runtimeAttributes.Count) As Attribute

runtimeAttributes.CopyTo(attrs, 0)
attrs(runtimeAttributes.Count) = New
TypeConverterAttribute(GetType(DataSourceConverter))
prop = TypeDescriptor.CreateProperty(Me.GetType(),
"DataSource", GetType(String), attrs)
properties("DataSource") = prop
End If

prop = CType(properties("DataMember"), PropertyDescriptor)
If Not (prop Is Nothing) Then
Dim runtimeAttributes As AttributeCollection =
prop.Attributes
Dim attrs(runtimeAttributes.Count) As Attribute

runtimeAttributes.CopyTo(attrs, 0)
attrs(runtimeAttributes.Count) = New
TypeConverterAttribute(GetType(DataMemberConverter))
prop = TypeDescriptor.CreateProperty(Me.GetType(),
"DataMember", GetType(String), attrs)
properties("DataMember") = prop
End If

prop = CType(properties("DataValueField"), PropertyDescriptor)
If Not (prop Is Nothing) Then
Dim runtimeAttributes As AttributeCollection =
prop.Attributes
Dim attrs(runtimeAttributes.Count) As Attribute

runtimeAttributes.CopyTo(attrs, 0)
attrs(runtimeAttributes.Count) = New
TypeConverterAttribute(GetType(DataFieldConverter))
prop = TypeDescriptor.CreateProperty(Me.GetType(),
"DataValueField", GetType(String), attrs)
properties("DataValueField") = prop
End If

prop = CType(properties("DataTextField"), PropertyDescriptor)
If Not (prop Is Nothing) Then
Dim runtimeAttributes As AttributeCollection =
prop.Attributes
Dim attrs(runtimeAttributes.Count) As Attribute

runtimeAttributes.CopyTo(attrs, 0)
attrs(runtimeAttributes.Count) = New
TypeConverterAttribute(GetType(DataFieldConverter))
prop = TypeDescriptor.CreateProperty(Me.GetType(),
"DataTextField", GetType(String), attrs)
properties("DataTextField") = prop
End If

End Sub 'PreFilterProperties


Function GetResolvedSelectedDataSource() As IEnumerable Implements
IDataSourceProvider.GetResolvedSelectedDataSource
Dim binding As DataBinding
binding = Me.DataBindings("DataSource")
If Not (binding Is Nothing) Then
Return DesignTimeData.GetSelectedDataSource(Me.Component,
binding.Expression, Me.DataMember)
End If
Return Nothing
End Function 'IDataSourceProvider.GetResolvedSelectedDataSource


Function GetSelectedDataSource() As Object Implements
IDataSourceProvider.GetSelectedDataSource
Dim binding As DataBinding
binding = Me.DataBindings("DataSource")
If Not (binding Is Nothing) Then
Return DesignTimeData.GetSelectedDataSource(Me.Component,
binding.Expression)
End If
Return Nothing
End Function 'IDataSourceProvider.GetSelectedDataSource
Paul Reed said:
Jeffrey,

Hi....

Let's say my custom control has a property called District. Using the
property designer at design time, they can select properties for the
control and see this property. When they click on the District, I wan
their to be a drop down of the valid Districts they can choose from.
This valid list of disticts needs to come from a database and not be
hard coded because the number of districts can change over time.

In other examples I have seen, the custom control declares an enum
structure with the allowable values. Then the property just returns a
data type of the enum. That is what I meant be enum. However, this
though limits you to only the values specified in the enum...I need mine
to be derived from a database, because the number of districts are
fluid. I hope that clarifies it for you.

Thanks,

Paul Reed
www.jacksonreed.com
 
P

Paul Reed

Allesandro,

Actually, I don't believe this is what I want. Let me briefly
re-explain.

I don't need them to select from a list of tables dynamically...just the
possible values of one column from one table...and I already know the
table.

I believe it is as simple as...I need to load in a dropdown list box the
District_Name from the District table and populate this into a property
that the control exposes. That's all. I already know the table and
everything...I just need to know how to load the drop down list that the
control exposes when they right click on the control and select
properties. The long and short of it is, then at run time, whatever
district they have selected...I use to customize a tree view that I
build (i.e., if it is District A the tree view looks different than if
it is District B).

Does that make sense?

Paul Reed
www.jacksonreed.com
 
A

Alessandro Zifiglio

oh, if thats the case then wont a type Converter work ?
Like the sample below should give you a dropdownList in the property grid


<NotifyParentProperty(True), _
TypeConverter(GetType(Cities)), _
Bindable(False), _
Category("Filter Alpha"), _
DefaultValue("Chicago"), _
Description("Select a city")> _
Property [City]() As String
Get
Dim s As String = CStr(ViewState("Cities"))
If s Is Nothing Then
s = "City"
End If
Return s
End Get

Set(ByVal Value As String)
ViewState("Cities") = Value
End Set
End Property

Public Class Cities : Inherits StringConverter
Private values As ArrayList = New ArrayList(New String() {"Kansas",
"Newyork", "Chicago", "Detroit"})

' Indicates this type converter provides a list of standard values.
Public Overloads Overrides Function GetStandardValuesSupported(ByVal
context As System.ComponentModel.ITypeDescriptorContext) As Boolean
Return True
End Function 'GetStandardValuesSupported

' Returns a StandardValuesCollection of standard value objects.
Public Overloads Overrides Function GetStandardValues(ByVal context
As System.ComponentModel.ITypeDescriptorContext) As
System.ComponentModel.TypeConverter.StandardValuesCollection
' Passes the local integer array.
Dim svc As New StandardValuesCollection(values)
Return svc
End Function 'GetStandardValues
End Class 'Cities
 
A

Alessandro Zifiglio

Also in the sample I posted I'm loading the arrayList with static data, but
you should be able to load this with data from a database easily.
Alessandro Zifiglio said:
oh, if thats the case then wont a type Converter work ?
Like the sample below should give you a dropdownList in the property grid


<NotifyParentProperty(True), _
TypeConverter(GetType(Cities)), _
Bindable(False), _
Category("Filter Alpha"), _
DefaultValue("Chicago"), _
Description("Select a city")> _
Property [City]() As String
Get
Dim s As String = CStr(ViewState("Cities"))
If s Is Nothing Then
s = "City"
End If
Return s
End Get

Set(ByVal Value As String)
ViewState("Cities") = Value
End Set
End Property

Public Class Cities : Inherits StringConverter
Private values As ArrayList = New ArrayList(New String() {"Kansas",
"Newyork", "Chicago", "Detroit"})

' Indicates this type converter provides a list of standard values.
Public Overloads Overrides Function GetStandardValuesSupported(ByVal
context As System.ComponentModel.ITypeDescriptorContext) As Boolean
Return True
End Function 'GetStandardValuesSupported

' Returns a StandardValuesCollection of standard value objects.
Public Overloads Overrides Function GetStandardValues(ByVal context
As System.ComponentModel.ITypeDescriptorContext) As
System.ComponentModel.TypeConverter.StandardValuesCollection
' Passes the local integer array.
Dim svc As New StandardValuesCollection(values)
Return svc
End Function 'GetStandardValues
End Class 'Cities



Paul Reed said:
Allesandro,

Actually, I don't believe this is what I want. Let me briefly
re-explain.

I don't need them to select from a list of tables dynamically...just the
possible values of one column from one table...and I already know the
table.

I believe it is as simple as...I need to load in a dropdown list box the
District_Name from the District table and populate this into a property
that the control exposes. That's all. I already know the table and
everything...I just need to know how to load the drop down list that the
control exposes when they right click on the control and select
properties. The long and short of it is, then at run time, whatever
district they have selected...I use to customize a tree view that I
build (i.e., if it is District A the tree view looks different than if
it is District B).

Does that make sense?

Paul Reed
www.jacksonreed.com
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,481
Members
44,900
Latest member
Nell636132

Latest Threads

Top