Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > ASP .Net > ASP .Net Building Controls > Custom Component and Custom Textbox binding

Reply
Thread Tools

Custom Component and Custom Textbox binding

 
 
Bill Anderson
Guest
Posts: n/a
 
      12-10-2003
Hi,

I am trying to accomplish the following.

Here is what I want to do:
1. Drop a custom business object component onto the
design surface and have it show up in the area below the
designer grid in VisStudio (like a dataadapter or dataset
does).

2. Drag a textbox webcontrol and change a custom
datasource property to the component from step 1. The
property grid will display a drop down of available
datasources (i want the textbox to work like a dropdown
control).

3. Based on the datasource chosen, change a
DataTextField property to a valid property of the
component. The property grid will show a drop down of
available Public Properties (fields) from the component
selected (basically, binding values to the textbox work
like the dropdown box does when binding to a dataset).

4. The Datasource and DataTextField values chosen will
persist as attributes in HTML on the custom component.

5. The componet is a basic business object. It does not
implement IEnumerable or IListsource (it does implement
IComponent). It just exposes public properties.

I have all of this working fine if the custom component
is of type Collection. But I don't want a Collection,
just the ability to bind to Public Properties of a basic
class.

I think have taken care of most of the items as follow,
but I am stuck on #5:
1. Implement IComponent on the business class.
2. Created custom control derived from Textbox webcontrol.
3. Created custom Designer that implements
IDatasourceprovider, and attach to custom textbox control.
4. Put designerserialization attribute of visible on
properties of custom textbox control.
5. Can't get to work unless the component Implements
IEnumerable, and then it only shows up in the datasource
drop down. I can never get the properties to list out
unless i have a stongly typed collection of busines
objects.

here is some sample code(i shortened syntax for brevity):

<Designer(Gettype(MyCustomDesigner)), otherattributes...>
_
Public Class MyTextBox : Inherits from TextBox

Private mDataSource as string = ""
Private mDataTextField as string = ""

<otherattributes, designerserialation...(visible)> _
Public Property DataSource as string
get
return mDataSource
end get
set(Value as string)
mDataSource = Value
end set
end property

'Repeat for the DataTextField Property

End Class

Public Class MyAddressObject : Implements IComponent,
IEnumberable 'IEnumerable causes this to become a valid
datasource in the datasource drop down. Should I use
something else?.

Private mStreet as string
Private mCity as string
Private mSite as ISite
'etc.

'Implement IComponet stuff here
'If IEnumberable is needed, how to implement
GetEnumerator?

Public Property Street as string
get
return mStreet
end get
set(Value as string)
mStreet = Value
end set
end property

'Implement the rest of the properties

End Class

Public Class WebDataBoundDesigner : Inherits
ControlDesigner
Implements IDataSourceProvider

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

Public Property DataSource() As String
Get
Return CType(Me.Component,
MyTextBox).DataSource
End Get
Set(ByVal Value As String)
CType(Me.Component, MyTextBox).DataSource =
Value
OnBindingsCollectionChanged("DataSource")
End Set
End Property

Public Function GetResolvedSelectedDataSource() As
System.Collections.IEnumerable Implements
System.Web.UI.Design.IDataSourceProvider.GetResolv edSelect
edDataSource
If Me.DataSource.Length > 0 Then
Return DesignTimeData.GetSelectedDataSource
(Me.Component, Me.DataSource, Me.DataTextField)
End If
Return Nothing
End Function

Public Function GetSelectedDataSource() As Object
Implements
System.Web.UI.Design.IDataSourceProvider.GetSelect edDataSo
urce
If Me.DataSource.Length > 0 Then
Return DesignTimeData.GetSelectedDataSource
(Me.Component, Me.DataSource)
End If
Return Nothing
End Function

Protected Overrides Sub PreFilterProperties(ByVal
properties As System.Collections.IDictionary)
MyBase.PreFilterProperties(properties)
Dim prop As PropertyDescriptor

prop = CType(properties("DataSource"),
PropertyDescriptor)
prop = TypeDescriptor.CreateProperty
(Me.GetType, "DataSource", GetType(String), New Attribute
() {New TypeConverterAttribute(GetType
(DataSourceConverter))})
properties("DataSource") = prop

prop = CType(properties("DataTextField"),
PropertyDescriptor)
prop = TypeDescriptor.CreateProperty
(Me.GetType, "DataTextField", GetType(String), New
Attribute() {New TypeConverterAttribute(GetType
(DataFieldConverter))})
properties("DataTextField") = prop
End Sub
End Class
 
Reply With Quote
 
 
 
 
Bill Anderson
Guest
Posts: n/a
 
      12-15-2003
I think I achieved what I wanted to do, so here is a
follow up in case any one wants to know.

I found Rick Strahl's article on two way databinding.
This works great, except you have to type the object name
you want to bind to in the BindingSourceObject property
(the value is a valid instance variable declared in code
behind). Then you have to type the property of that
object you want to bind to in the BindingSourceProperty.
If a designer is not familar with the properties of an
object, they would have to find them out somehow. I
wanted this to be more RAD. I wanted to have these
properties as dropdown boxes showing valid values.

Here is what I did to achieve that.

I created two custom typeconverters.

The first type converter iterates through the code behind
class of the active document that the textbox is sitting
on. It finds all family level fields (protected) using
reflection. Then, by attaching the typeconverter
attribute to the bindingsourceobject property of the
textbox, this creates a drop down at design time that
shows all valid objects from the codebehind this textbox
can bind to.

The second type converter looks at the selected value
from above. Based on the object selected, it uses
reflection to get all public properties of the selected
object and displays them in a drop down box. I attached
this typeconverter to the BindingSourceProperty object.

There are a couple of caveats. The code behind type has
to be the same name as the web form (by default this is
always the case when you create a new web form).

Here is the code of the inherited textbox and the type
converters.....

' *******************************************
' TypeConverter Classes - WebBindingTypeConverters.vb
'********************************************

Imports System.ComponentModel
Imports EnvDTE
Imports VSLangProj
Imports System.Web.UI.Design
Imports System.Web.UI
Imports System.Reflection

Public Class TypeInstanceConverter : Inherits
DataSourceConverter
Public Overloads Overrides Function GetStandardValues
(ByVal context As
System.ComponentModel.ITypeDescriptorContext) As
System.ComponentModel.TypeConverter.StandardValues Collecti
on
Dim list1 As ArrayList
Dim array1() As Object
Dim sAssemblyName As String = ""
Dim sTypeName As String = ""
Dim a As System.Reflection.Assembly
sAssemblyName =
ReflectionHelperFunctions.GetProjectAssemblyName(c ontext)
sTypeName =
ReflectionHelperFunctions.GetActiveDocumentType(co ntext)
list1 = New ArrayList
If Not context Is Nothing Then
Dim ass As System.Reflection.Assembly
Try
ass = System.Reflection.Assembly.Load
(sAssemblyName)
Catch ex As Exception
ass = Nothing
End Try
If Not ass Is Nothing Then
Dim t As System.Type
Try
t = ass.GetType(sAssemblyName & "." &
sTypeName)
Catch ex As Exception
MsgBox(ex.Message)
t = Nothing
End Try
If Not t Is Nothing Then
For Each fi As FieldInfo In
t.GetFields(ReflectionHelperFunctions.MemberAccess )
If fi.IsFamily Then
list1.Add(fi.Name)
End If
Next
End If
End If
End If
array1 = list1.ToArray
Array.Sort(array1, Comparer.Default)
Return New StandardValuesCollection(array1)
End Function
End Class


Public Class TypePropertyNameConverter : Inherits
DataSourceConverter
Public Overloads Overrides Function GetStandardValues
(ByVal context As
System.ComponentModel.ITypeDescriptorContext) As
System.ComponentModel.TypeConverter.StandardValues Collecti
on

Dim list1 As ArrayList
Dim array1() As Object
Dim sAssemblyName As String
Dim sTypeName As String

sAssemblyName =
ReflectionHelperFunctions.GetProjectAssemblyName(c ontext)
sTypeName =
ReflectionHelperFunctions.GetActiveDocumentType(co ntext)

list1 = New ArrayList
If Not context Is Nothing Then
If TypeOf context.Instance Is IWebDataControl
Then
Dim c As IWebDataControl = CType
(context.Instance, IWebDataControl)
If Not c Is Nothing Then
If Trim(c.BindingSourceObject).Length
> 0 Then

Dim ass As
System.Reflection.Assembly
ass =
System.Reflection.Assembly.Load(sAssemblyName)
Dim t As Type
Try
t = ass.GetType(sAssemblyName
& "." & sTypeName, True)
Catch ex As Exception
t = Nothing
End Try
If Not t Is Nothing Then
Try
For Each f As FieldInfo
In t.GetFields(ReflectionHelperFunctions.MemberAccess )
If f.IsFamily Then
If f.Name =
c.BindingSourceObject Then
For Each p As
PropertyInfo In f.FieldType.GetProperties
Try

list1.Add(p.Name)
Catch ex
As Exception
End Try

Next
Exit For
End If
End If

Next
Catch ex As Exception
MsgBox(ex.Message)
End Try
End If
End If
End If
End If
End If
array1 = list1.ToArray
Array.Sort(array1, Comparer.Default)
Return New StandardValuesCollection(array1)
End Function
End Class


Public Class ReflectionHelperFunctions
Public Const MemberAccess As BindingFlags =
BindingFlags.Public Or _

BindingFlags.NonPublic Or _

BindingFlags.Static Or _

BindingFlags.Instance Or _

BindingFlags.IgnoreCase

Public Shared Function GetProjectAssemblyName(ByVal
context As ITypeDescriptorContext) As String
Dim item As ProjectItem
Dim sAssemblyName As String

item = CType(context.GetService(GetType
(EnvDTE.ProjectItem)), ProjectItem)

If Not item Is Nothing Then
Dim project As VSProject
project = CType
(item.ContainingProject.Object, VSProject)
If Not project Is Nothing Then
For i As Int32 = 1 To
project.Project.Properties.Count
If project.Project.Properties.Item
(i).Name = "AssemblyName" Then
sAssemblyName =
project.Project.Properties.Item(i).Value
Exit For
End If
Next
End If
Else
sAssemblyName = ""
End If
Return sAssemblyName
End Function

Public Shared Function GetActiveDocumentType(ByVal
context As ITypeDescriptorContext) As String
Dim sTypeName As String = ""
Dim item As EnvDTE._DTE
item = CType(context.GetService(GetType
(EnvDTE._DTE)), EnvDTE._DTE)
sTypeName = item.ActiveDocument.Name
Return Trim(Left(sTypeName, sTypeName.IndexOf
(".aspx")))
End Function
End Class

' **********************************
' TextBox Class - WebTextBox.vb
' ***********************************

Imports System.Web.UI
Imports System.Web.UI.Design
Imports System.Web.I.WebControls
Imports System.ComponentModel
Imports System.Reflection
Imports System.Drawing

<DefaultProperty("BindingSourceObject"), _
ToolboxBitmap(GetType(TextBox)), _
ToolboxData("<{0}:WebTextBox runat=server></
{0}:WebTextBox>")> _
Public Class WebTextBox : Inherits TextBox
Implements IWebDataControl

Private mBindingSourceObject As String = ""
Private mBindingSourceProperty As String = ""
Private mBindingProperty As String = "Text"
Private mDisplayFormat As String = ""
Private mUserFieldName As String = ""
Private mBindingErrorMessage As String = ""

<TypeConverter(GetType(TypeInstanceConverter))> _
Public Property BindingSourceObject() As String
Implements IWebDataControl.BindingSourceObject
Get
Return mBindingSourceObject
End Get
Set(ByVal Value As String)
mBindingSourceObject = Value
End Set
End Property

<TypeConverter(GetType(TypePropertyNameConverter)) > _
Public Property BindingSourceProperty() As String
Implements IWebDataControl.BindingSourceProperty
Get
Return mBindingSourceProperty
End Get
Set(ByVal Value As String)
mBindingSourceProperty = Value
End Set
End Property

Public Property BindingErrorMessage() As String
Implements IWebDataControl.BindingErrorMessage
Get
Return mBindingErrorMessage
End Get
Set(ByVal Value As String)
mBindingErrorMessage = Value
End Set
End Property

<DefaultValue("Text")> _
Public Property BindingProperty() As String
Implements IWebDataControl.BindingProperty
Get
Return mBindingProperty
End Get
Set(ByVal Value As String)
mBindingProperty = Value
End Set
End Property

Public Property DisplayFormat() As String Implements
IWebDataControl.DisplayFormat
Get
Return mDisplayFormat
End Get
Set(ByVal Value As String)
mDisplayFormat = Value
End Set
End Property

Public Overrides Property Text() As String Implements
IWebDataControl.Text
Get
Return MyBase.Text
End Get
Set(ByVal Value As String)
MyBase.Text = Value
End Set
End Property

Public Property UserFieldName() As String Implements
IWebDataControl.UserFieldName
Get
Return mUserFieldName
End Get
Set(ByVal Value As String)
mUserFieldName = Value
End Set
End Property

Public Sub UnbindData(ByVal WebForm As
System.Web.UI.Page) Implements IWebDataControl.UnbindData
WebDataHelper.ControlUnbindData(WebForm, Me)
End Sub

Public Sub BindData(ByVal WebForm As
System.Web.UI.Page) Implements IWebDataControl.BindData
WebDataHelper.ControlBindData(WebForm, Me)
End Sub
End Class


>-----Original Message-----
>Hi,
>
>I am trying to accomplish the following.
>
>Here is what I want to do:
>1. Drop a custom business object component onto the
>design surface and have it show up in the area below the
>designer grid in VisStudio (like a dataadapter or

dataset
>does).
>
>2. Drag a textbox webcontrol and change a custom
>datasource property to the component from step 1. The
>property grid will display a drop down of available
>datasources (i want the textbox to work like a dropdown
>control).
>
>3. Based on the datasource chosen, change a
>DataTextField property to a valid property of the
>component. The property grid will show a drop down of
>available Public Properties (fields) from the component
>selected (basically, binding values to the textbox work
>like the dropdown box does when binding to a dataset).
>
>4. The Datasource and DataTextField values chosen will
>persist as attributes in HTML on the custom component.
>
>5. The componet is a basic business object. It does

not
>implement IEnumerable or IListsource (it does implement
>IComponent). It just exposes public properties.
>
>I have all of this working fine if the custom component
>is of type Collection. But I don't want a Collection,
>just the ability to bind to Public Properties of a basic
>class.
>
>I think have taken care of most of the items as follow,
>but I am stuck on #5:
>1. Implement IComponent on the business class.
>2. Created custom control derived from Textbox

webcontrol.
>3. Created custom Designer that implements
>IDatasourceprovider, and attach to custom textbox

control.
>4. Put designerserialization attribute of visible on
>properties of custom textbox control.
>5. Can't get to work unless the component Implements
>IEnumerable, and then it only shows up in the datasource
>drop down. I can never get the properties to list out
>unless i have a stongly typed collection of busines
>objects.
>
>here is some sample code(i shortened syntax for brevity):
>
><Designer(Gettype(MyCustomDesigner)),

otherattributes...>
>_
>Public Class MyTextBox : Inherits from TextBox
>
>Private mDataSource as string = ""
>Private mDataTextField as string = ""
>
><otherattributes, designerserialation...(visible)> _
>Public Property DataSource as string
>get
>return mDataSource
>end get
>set(Value as string)
>mDataSource = Value
>end set
>end property
>
>'Repeat for the DataTextField Property
>
>End Class
>
>Public Class MyAddressObject : Implements IComponent,
>IEnumberable 'IEnumerable causes this to become a valid
>datasource in the datasource drop down. Should I use
>something else?.
>
>Private mStreet as string
>Private mCity as string
>Private mSite as ISite
>'etc.
>
>'Implement IComponet stuff here
>'If IEnumberable is needed, how to implement
>GetEnumerator?
>
>Public Property Street as string
>get
>return mStreet
>end get
>set(Value as string)
>mStreet = Value
>end set
>end property
>
>'Implement the rest of the properties
>
>End Class
>
>Public Class WebDataBoundDesigner : Inherits
>ControlDesigner
> Implements IDataSourceProvider
>
>Public Property DataTextField() As String
> Get
> Return CType(Me.Component,
>MyTextBox).DataTextField
> End Get
> Set(ByVal Value As String)
> CType(Me.Component, MyTextBox).DataTextField
>= Value
> End Set
> End Property
>
>Public Property DataSource() As String
> Get
> Return CType(Me.Component,
>MyTextBox).DataSource
> End Get
> Set(ByVal Value As String)
> CType(Me.Component, MyTextBox).DataSource =
>Value
> OnBindingsCollectionChanged("DataSource")
> End Set
> End Property
>
>Public Function GetResolvedSelectedDataSource() As
>System.Collections.IEnumerable Implements
>System.Web.UI.Design.IDataSourceProvider.GetResol vedSelec

t
>edDataSource
> If Me.DataSource.Length > 0 Then
> Return DesignTimeData.GetSelectedDataSource
>(Me.Component, Me.DataSource, Me.DataTextField)
> End If
> Return Nothing
> End Function
>
>Public Function GetSelectedDataSource() As Object
>Implements
>System.Web.UI.Design.IDataSourceProvider.GetSelec tedDataS

o
>urce
> If Me.DataSource.Length > 0 Then
> Return DesignTimeData.GetSelectedDataSource
>(Me.Component, Me.DataSource)
> End If
> Return Nothing
> End Function
>
>Protected Overrides Sub PreFilterProperties(ByVal
>properties As System.Collections.IDictionary)
> MyBase.PreFilterProperties(properties)
> Dim prop As PropertyDescriptor
>
> prop = CType(properties("DataSource"),
>PropertyDescriptor)
> prop = TypeDescriptor.CreateProperty
>(Me.GetType, "DataSource", GetType(String), New Attribute
>() {New TypeConverterAttribute(GetType
>(DataSourceConverter))})
> properties("DataSource") = prop
>
> prop = CType(properties("DataTextField"),
>PropertyDescriptor)
> prop = TypeDescriptor.CreateProperty
>(Me.GetType, "DataTextField", GetType(String), New
>Attribute() {New TypeConverterAttribute(GetType
>(DataFieldConverter))})
> properties("DataTextField") = prop
>End Sub
>End Class
>.
>

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Complex data binding question, binding child objects of a custom collection. JcFx ASP .Net Datagrid Control 0 06-01-2005 04:01 PM
Is there any POP3 and NNTP component I can use as a ASP Component? Miguel Minora ASP General 1 01-11-2005 10:16 PM
com+ component and Component Service Karuppasamy ASP .Net 1 01-14-2004 11:30 PM
com+ component and Component Service Karuppasamy ASP .Net 0 01-13-2004 06:09 PM
event binding and component.config Scott Holmes Python 1 08-27-2003 01:03 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57