This class exists to provide support for use of library UserControls as WebParts.

The built in personalization provider model will assume that anything that inherits from UserControl should be stored in the WebPart state as a path instead of a type. This causes problems for precomplied UserControls as they should be treated in the same way as custom controls, as their path is not used.

The PrecompiledUserControl class must be used instead of UserControl in your library. This must be used in conjuction with the included PiInternet.Web.UI.WebControls.WebParts.SqlPersonalizationProvider class as your sites personalization provider. Alternatively if you have your own provider you could copy the classes SavePersonalizationState method. The class uses reflection to find PrecompiledUserControls in the state and changes them from being stored as paths to types.

Last edited Jan 16, 2007 at 12:10 PM by grahamdyson, version 1

Comments

cmschick Mar 26, 2007 at 12:58 AM 
Hello Graham,

Absolutely awesome set of libraries you've submitted here. Most of it is pretty straight forward but the PageFactory and the SqlPersonalizationProvider classes are a bit over my head. What I am trying to do is use your classes to automatically import precompiled user controls as web parts into a web part catalog. I have the custom catalog working for reading in actual webpart types but was wondering how I would go about querying the type and loading it into the WebPartDescription collection without having the WebPartManager complain about a user control needing a path... Any help you could provide would be greatly appreciated. You can reach me at Chris@ViaSoftCS.com.

By the way, here is the custom catalog code that is still in the works...

<Drawing.ToolboxBitmap(GetType(CatalogPart)), ToolboxData("<{Iatric}:CatalogPartUpdates runat='server'></{Iatric}:CatalogPartUpdates>")> _
Public Class CatalogPartUpdates
Inherits CatalogPart

Public Sub New()
End Sub

'Property to store the path to pull the dll's from.
'If the property is left blank, it uses the /bin folder.
Private _fileLocation As String

Public Property FileLocation() As String
Get
Dim webPartMgr As WebPartManager = Page.FindControl("WebPartManager1")
If Not webPartMgr Is Nothing Then
If _fileLocation Is Nothing Then
Return HttpContext.Current.Server.MapPath("~/bin")
Else
Return _fileLocation
End If
Else
Return String.Empty
End If
End Get
Set(ByVal value As String)
_fileLocation = value
End Set
End Property

'Property to determine if the control is in runtime or designtime

Private ReadOnly Property IsWebApp() As Boolean
Get
'HttpContext.Current will be null at design time
If HttpContext.Current Is Nothing Then
Return False
Else
Return True
End If
End Get
End Property



'Override the GetAvailableWebPartDescriptions method.
'This method loads the descriptions that display in the catalog part at run time
Public Overrides Function GetAvailableWebPartDescriptions() As WebPartDescriptionCollection
'If this is at runtime, call CacheParts which caches the results
If IsWebApp Then
Return CacheParts(True)
Else
Return DesignerParts()
End If
End Function


'Caches the results of the ReflectParts() function to avoid accessing the file system and parsing the dll's on each request
Private Function CacheParts(ByVal _cache As Boolean) As WebPartDescriptionCollection
'If both objects are not null, pull them from cache
If Not (HttpContext.Current.Cache("ReflectionPartsDescriptions") Is Nothing) And Not (HttpContext.Current.Cache("ReflectionParts") Is Nothing) Then
webparts = CType(HttpContext.Current.Cache("ReflectionParts"), Hashtable)
Return CType(HttpContext.Current.Cache("ReflectionPartsDescriptions"), WebPartDescriptionCollection)

'If either are missing, run ReflectParts and cache the results
Else
Dim wpd As WebPartDescriptionCollection = ReflectParts()
HttpContext.Current.Cache("ReflectionPartsDescriptions") = wpd
HttpContext.Current.Cache("ReflectionParts") = webparts
Return wpd
End If
End Function



Private Function DesignerParts() As WebPartDescriptionCollection
'If they are in the designer, call ReflectParts without caching
'Probably be better to build a designer for the control and do that work there

Return ReflectParts()

End Function


'hashtable to store the web part descriptions.
Private webparts As New Hashtable()

Private Function ReflectParts() As WebPartDescriptionCollection
'Clear out the hashtable
webparts.Clear()
Dim coll As Collection(Of WebPartDescription) = New Collection(Of WebPartDescription)

If Not DesignMode Then
If Not (FileLocation Is Nothing) Then
Dim partDirectory As New DirectoryInfo(FileLocation)

If partDirectory.Exists Then

'Loop through the dll's in the directory
Dim dllFileInfo As FileInfo
For Each dllFileInfo In partDirectory.GetFiles("*.dll")

Dim privateAssembly As Assembly = Assembly.LoadFrom(dllFileInfo.FullName)

'Loop through the types in the assembly to see if the dll has WebParts
Dim privateType As Type
For Each privateType In privateAssembly.GetTypes()
Dim wp As WebPart = Nothing

If Not (privateType.BaseType Is Nothing) Then
If privateType.BaseType.Name.ToLower() = "webpart" Or privateType.Name.ToLower() = "webpartbase" Then
If privateType.BaseType.Name.ToLower() = "webpart" Then

'Do this check to keep the Designer from throwing an exception when rendering the catalog.
Dim webPrtMgr As WebPartManager = Page.FindControl("WebPartManager1")
If Not (webPrtMgr Is Nothing) Then
'The framwork has to ensure that the type will be loadable on future requests.
'The following BuildManager call would check for that.
'BuildManager.GetType(mytype.ToString(), true, false);
wp = CType(Activator.CreateInstance(privateType), WebPart)
wp.ID = privateType.ToString() + "1" 'have to give an ID to the WebPart.
'This is for the designer.
Else
Dim genericWebPart As GenericWebPart = CType(Activator.CreateInstance(privateType), GenericWebPart)
wp = CType(genericWebPart, WebPart)
wp.ID = privateType.ToString() + "1"
End If
Else ' it derives from our webpartbase (webpartbase is old, it will now derive from precompiledusercontrol)

'*****************************************************************************************************
' Here we will add our own methods for importing user controls as web parts from an assembly. The
' challenge is that there is no way to do this without creating a custom Virtual Path Provider which
' will fail on IIS if the User does not have Administrative priveleges on the machine itself. So we
' need to implement that check here as well and skip over this section if they do not have those priveleges.
' Essentially, this will be an administrator right only...

' This code works up to a point but the WebPartManager needs the virtual path to the user control which,
' as discussed above, is not working yet...

'Dim myAssemblyName As String = Assembly.LoadFrom(dllFileInfo.FullName).ManifestModule.ToString()
'Dim myClassName As String = "MyWatchList"
'Dim myNamespace As String = "Iatric.Web.UI.WebControls.UserControls" ' IIf(privateType.Namespace != null, privateType.Namespace,"")
'Dim eUCL As New EmbeddedUserControlLoader()
'eUCL.AssemblyName = myAssemblyName
'eUCL.ControlClassName = myClassName
'eUCL.ControlNamespace = myNamespace
'eUCL.ID = privateType.Name + "1"

'Dim genericWebPart2 As GenericWebPart = WebPartManager.CreateWebPart(eUCL)
'wp = CType(genericWebPart2, WebPart)
'wp.ID = genericWebPart2.ID ' privateType2.ToString() + counter + "wp";
End If
End If
End If

'If the WebPart was set,
'Populate the WebPartDescription collection and the WebPart hashtable
If Not (wp Is Nothing) Then
Dim desc As New WebPartDescription(wp)
coll.Add(desc)
webparts(desc) = wp
End If
Next privateType
Next dllFileInfo
End If
End If
Else
' If design mode just return a dummy webpart
Dim c As Label = New Label()
c.Text = "Web Part 1"
c.ID = "IatricDummy_WebPart1"
Dim webPartMgr As WebPartManager = Page.FindControl("WebPartManager1")
Dim myWebPart As WebPart = webPartMgr.CreateWebPart(c)
myWebPart.Description = "WebPart 1"
myWebPart.Title = "WebPart 1"
Dim desc As New WebPartDescription(myWebPart.ID, "Sample WebPart", "WebPart 1", "")
coll.Add(desc)
webparts(desc) = myWebPart
End If
Return New WebPartDescriptionCollection(coll)
End Function


'Return the custom webparts hashtable.
Public Overrides Function GetWebPart(ByVal description As WebPartDescription) As WebPart
Return CType(webparts(description), WebPart)
End Function

End Class