ICaramba

Miguel Castro's blog about .NET and its effect on National Security, the Eco-system, and his daughter's sleeping patterns.


News

My Stats

  • Posts - 120
  • Comments - 69
  • Trackbacks - 145

Twitter












Recent Comments


Recent Posts


Archives


Image Galleries



Blogs I read


Links


Microsoft DCCs


January 2006 Entries

Next User Group talk


I'll be speaking at the Capital Area Users Group in Washington, DC tomorrow the 24th.  It's an INETA event and I've heard it's a great group.  Looking forward to it.

 

posted @ Monday, January 23, 2006 10:20 AM | Feedback (1) |


Remotable collections


Recently I had the need to implements some remoting solutions and I ran into a problem.  After googling around a bit, I confirmed that the .NET Framework does not provide you with a remoting-compatible collection (if someone knows of one, please let me know).  So I went about writing one.  I wanted a strongly typed collection which I used to achieve by inheriting from CollectionBase and adding the strongly typed implementation.  In 2.0, I started inheriting from the List<T> generic collection which was even better.  The problem is that neither of these two objects inherit from MarshalByRefObject, which is required for remoting solutions.

My solution was to create a new class (BusinessCollectionBase), accept a generic for the kind of object it will contain (TBusinessObject), and add a protected property as type List<TBusinessObject>.  This class inherits from MarshalByRefObject which makes it usable in a remoting situation.  Now, the trick was to expose everything I had to and map it to the internal list.  The trick to doing this is to implement all the correct interfaces and perform the mapping there.  This way, any collection object you create that inherits from this class will have all the necessary enumeration and binding compatibility of any standard collection (this is the luxury we got inheriting from CollectionBase or DictionaryBase in the past, or one of the generic collections now).

The result is listed below.  Notice the implementation of both the generic- nterfaces and the non-generic interfaces.  This is very important for the collection to be both strongly typed and bindable to visual controls.  Also notice that the implementations of the non-generic interfaces have been changed to Protected, as opossed to the default Public.  They are only needed for the data binding compatibility where the control doing the binding is casting to the appropriate interface anyway.  The generic-compatible implementations are the only thing I want to expose because they provide the strong typing.

You can of course alter this to accomodate any type of collection you want.  In this case I used a List<T>, but you can contain anything that suits your needs.

Also, notice that I added a couple of methods for the class to support serialization.  Though usually tagging it “Serializable“ is all that is necessary, with these methods, you can actually return a string representation of your collection so you can put it into a cookie, if you're using it on the web.

If anyone further modifies or enhances this, I'd love to hear about it.

Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Xml.Serialization

Namespace InfoTek.Business

    Public MustInherit Class BusinessCollectionBase(Of TBusinessObject As New)

        Inherits MarshalByRefObject
        Implements IList(Of TBusinessObject)
        Implements IList
        Implements ICollection(Of TBusinessObject)
        Implements ICollection
        Implements IEnumerable(Of TBusinessObject)
        Implements IEnumerable

        Protected innerList As List(Of TBusinessObject) = New List(Of TBusinessObject)

        Public Function Add() As TBusinessObject
            Dim o_Item As TBusinessObject = New TBusinessObject()
            innerList.Add(o_Item)
            Return o_Item
        End
Function

        Public Function SerializeObject() As
String
            Dim o_MemStream As MemoryStream = New MemoryStream
            Dim o_Serializer As XmlSerializer = New XmlSerializer(Me.GetType())
            o_Serializer.Serialize(o_MemStream, Me)
            Return ASCII.GetString(o_MemStream.ToArray())
        End
Function

        Public Function DeserializeObject(ByVal xmlString As String) As
Object
            Dim o_Serializer As XmlSerializer = New XmlSerializer(Me.GetType())
            Dim o_MemStream As MemoryStream = New MemoryStream(ASCII.GetBytes(xmlString))
            Return o_Serializer.Deserialize(o_MemStream)
        End
Function

#Region
"ICollection(Of TBusinessObject) implementation"

        Public Sub Add(ByVal item As TBusinessObject) Implements System.Collections.Generic.ICollection(Of TBusinessObject).Add
            CType(innerList, ICollection(Of TBusinessObject)).Add(item)
        End
Sub

        Public Sub Clear() Implements System.Collections.Generic.ICollection(Of TBusinessObject).Clear
            CType(innerList, ICollection(Of TBusinessObject)).Clear()
        End
Sub

        Public Function Contains(ByVal item As TBusinessObject) As Boolean Implements System.Collections.Generic.ICollection(Of TBusinessObject).Contains
            Return CType(innerList, ICollection(Of TBusinessObject)).Contains(item)
        End
Function

        Public Sub CopyTo(ByVal array() As TBusinessObject, ByVal arrayIndex As Integer) Implements System.Collections.Generic.ICollection(Of TBusinessObject).CopyTo
            CType(innerList, ICollection(Of TBusinessObject)).CopyTo(array, arrayIndex)
        End
Sub

        Public ReadOnly Property Count() As Integer Implements System.Collections.Generic.ICollection(Of TBusinessObject).Count
            
Get
                Return CType(innerList, ICollection(Of TBusinessObject)).Count
            End
Get
        End
Property

        Public ReadOnly Property IsReadOnly() As Boolean Implements System.Collections.Generic.ICollection(Of TBusinessObject).IsReadOnly
            
Get
                Return CType(innerList, ICollection(Of TBusinessObject)).IsReadOnly
            End
Get
        End
Property

        Public Function Remove(ByVal item As TBusinessObject) As Boolean Implements System.Collections.Generic.ICollection(Of TBusinessObject).Remove
            Return CType(innerList, ICollection(Of TBusinessObject)).Remove(item)
        End
Function

#End Region

#Region
"IEnumerable(Of TBusinessObject) implementation"

        Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of TBusinessObject) Implements System.Collections.Generic.IEnumerable(Of TBusinessObject).GetEnumerator
            Return CType(innerList, IEnumerable(Of TBusinessObject)).GetEnumerator()
        End
Function

#End Region

#Region
"IList(Of TBusinessObject) implementation"

        Public Function IndexOf(ByVal item As TBusinessObject) As Integer Implements System.Collections.Generic.IList(Of TBusinessObject).IndexOf
            Return CType(innerList, IList(Of TBusinessObject)).IndexOf(item)
        End
Function

        Public Sub Insert(ByVal index As Integer, ByVal item As TBusinessObject) Implements System.Collections.Generic.IList(Of TBusinessObject).Insert
            CType(innerList, IList(Of TBusinessObject)).Insert(index, item)
        End
Sub

        Default Public Property Item(ByVal index As Integer) As TBusinessObject Implements System.Collections.Generic.IList(Of TBusinessObject).Item
            
Get
                Return CType(innerList, IList(Of TBusinessObject)).Item(index)
            End
Get
            Set(ByVal value As TBusinessObject)
                CType(innerList, IList(Of TBusinessObject)).Item(index) = value
            End
Set
        End
Property

        Public Sub RemoveAt(ByVal index As Integer) Implements System.Collections.Generic.IList(Of TBusinessObject).RemoveAt
            CType(innerList, IList(Of TBusinessObject)).RemoveAt(index)
        End
Sub

#End Region

#Region
"IEnumerable implementation"

        Protected Function GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
            Return CType(innerList, IEnumerable).GetEnumerator()
        End
Function

#End Region

#Region
"ICollection implementation"

        Protected Sub CopyTo1(ByVal array As System.Array, ByVal index As Integer) Implements System.Collections.ICollection.CopyTo
            CType(innerList, ICollection).CopyTo(array, index)
        End
Sub

        Protected ReadOnly Property Count1() As Integer Implements System.Collections.ICollection.Count
            
Get
                Return CType(innerList, ICollection).Count
            End
Get
        End
Property

        Protected ReadOnly Property IsSynchronized() As Boolean Implements System.Collections.ICollection.IsSynchronized
            
Get
                Return CType(innerList, ICollection).IsSynchronized
            End
Get
        End
Property

        Protected ReadOnly Property SyncRoot() As Object Implements System.Collections.ICollection.SyncRoot
            
Get
                Return CType(innerList, ICollection).SyncRoot
            End
Get
        End
Property

#End Region

#Region
"IList implementation"

        Protected Function Add1(ByVal value As Object) As Integer Implements System.Collections.IList.Add
            Return CType(innerList, IList).Add(value)
        End
Function

        Protected Sub Clear1() Implements System.Collections.IList.Clear
            CType(innerList, IList).Clear()
        End
Sub

        Protected Function Contains1(ByVal value As Object) As Boolean Implements System.Collections.IList.Contains
            Return CType(innerList, IList).Contains(value)
        End
Function

        Protected Function IndexOf1(ByVal value As Object) As Integer Implements System.Collections.IList.IndexOf
            Return CType(innerList, IList).IndexOf(value)
        End
Function

        Protected Sub Insert1(ByVal index As Integer, ByVal value As Object) Implements System.Collections.IList.Insert
            CType(innerList, IList).Insert(index, value)
        End
Sub

        Protected ReadOnly Property IsFixedSize() As Boolean Implements System.Collections.IList.IsFixedSize
            
Get
                Return CType(innerList, IList).IsFixedSize
            End
Get
        End
Property

        Protected ReadOnly Property IsReadOnly1() As Boolean Implements System.Collections.IList.IsReadOnly
            
Get
                Return CType(innerList, IList).IsReadOnly
            End
Get
        End
Property

        Protected Property Item1(ByVal index As Integer) As Object Implements System.Collections.IList.Item
            
Get
                Return CType(innerList, IList).Item(index)
            End
Get
            Set(ByVal value As Object)
                CType(innerList, IList).Item(index) = value
            End
Set
        End
Property

        Protected Sub Remove1(ByVal value As Object) Implements System.Collections.IList.Remove
            CType(innerList, IList).Remove(value)
        End
Sub

        Protected Sub RemoveAt1(ByVal index As Integer) Implements System.Collections.IList.RemoveAt
            CType(innerList, IList).RemoveAt(index)
        End
Sub

#End Region

    End Class

End Namespace

 

posted @ Monday, January 23, 2006 10:16 AM | Feedback (1) |


Project built on .NET Rocks TV


The project that was built in the first two episodes of .NET Rocks TV can be downloaded from the download section of www.dotnetdude.com.

 

posted @ Saturday, January 21, 2006 3:00 PM | Feedback (0) |


.NET Rocks TV is here


Check out www.dnrtv.com - yours truly has episodes 1 and 2 on Developing Custom Webcontrols (of course)

This is a hybrid between a webcast and a standard .NET Rocks interview, but much more technical. 

It's a flash presentations where you get to witness code being developed, and at the same time there is a lot of interaction between Carl and I.  He can see my screen from his monitor so he interviews me and asks me questions specifically about what I am doing at the time.  I think the results made for a very informative format.

Hope to do another one soon.

 

posted @ Thursday, January 19, 2006 7:50 AM | Feedback (2) |


A detail the Webcontrol books don't teach you


And unfortunately, it's a detail I always seem to forget whenever I teach Webcontrol development.

Anyone who has ever taken my webcontrol class or has heard my user group presentations about them, knows that I start by telling you the simplest thing: webcontrols render HTML to the browser.  Now, while this has become pretty much common knowledge in the ASP.NET community, here's one detail that I haven't even seen the books tell.  A standard composite control inherits from the System.Web.UI.WebControl.  A very common practice is to build an HTML table inside your composite control to surround any child controls you may have.  The problem is that the WebControl class by default, renders the control container as a Span tag in HTML.  In fact if you create an empty WebControl with no children and run the page, you'll just see an empty Span tag.  The Span tag is an inline element, while the Table tag is a block element.  The HTML specification states that a block element cannot be contained inside an inline element - only the other way around.  For those that don't understand the difference:  the main characteristic of these two types of HTML elements is that inline elements can go next to each other, while block elements cannot.  Ever try putting two Tables next to each other?  Doesn't work.  You have to put them in side-by-side cells of another table.  So if you develop a composite control and build a table surrounding its child controls, you end up with illegal HTML.  The problem is that IE is so forgiving and lets you get away with it without screaming at you.  Well, the fix is easy.

If you inherit from the WebControl class, you need to override the default constructor of your control and call its base constructor with a certain argument:

Public Sub New()
     MyBase.New(HtmlTextWriterTag.Div)
End Sub

or

public WebControl1() : base(HtmlTextWriterTag.Div) { }

In the C# example, I am assuming the webcontrol class is called WebControl1.

Notice the argument I am sending into the base constructor.  It's an enum that Microsoft gives us for almost every HTML tag value.  In this case, we are changing the default tag that is rendered as the control container to Div.  Div is a block element and allows the legal containment of other block elements (or inline elements) such as the Table tag.

In ASP.NET 2.0, we have a new class from which to inherit composite controls; it's called CompositeControl.  This class does a few things automatically for you, such as calling EnsureChildControls before rendering and implementing INamingContainer.  But it does not correct the default container tag.  I say “the correct” one because my opinion is that the default tag for composite controls should be Div, not Span.  Overriding the default constructor on a webcontrol that inherits from the CompositeControl class does not work because it is not able to acces the overload with the argument we need.  In this case, we have a property we can override, called TagKey:

Protected Overrides ReadOnly Property TagKey() As System.Web.UI.HtmlTextWriterTag
     Get
         
Return
HtmlTextWriterTag.Div
     End
Get
End Property

or

protected override System.Web.UI.HtmlTextWriterTag TagKey
{
     get { return HtmlTextWriterTag.Div;
  }
}

In this case, we're achieving the same thing just in a different manner.

So the important thing to remember is to do this if you are going to start your Controls collection with a block element like Table.

Don't forget this !!!

 

posted @ Thursday, January 19, 2006 7:47 AM | Feedback (18) |