|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionIf you have ever wanted to use the same or two BackgroundIf you add an The SolutionIntroducing the SharedImageLists ComponentThe
The I'll start by explaining how to use the Now when you click the Add NewItem or Add Component from the context menu in the Solution Explorer, simply select the
When the
Now comes the fun part. When you add the
This SmartTag panel contains a dropdown list with all the An In the property browser window, you will also notice that all the
You can now bind this shared As you can see from the
So how does it work?The SharedImageLists componentWell, on the one level, it's really quite simple. The Listing 1 - The code the Me.LargeImageList = Me.SharedImageLists11.NewImageList( _
Me.components, CType(Me.SharedImageLists11.GetSharedImageLists, _
WindowsApplication2.SharedImageLists1).LargeImageList)
To understand what's going on here, we need to look at what the Listing 2 - Shows what the Public Function NewImageList(ByVal component As IContainer, _
ByVal sharedImageList As ImageList) As ImageList
If SharedImageListCodeDomSerializer.InDesignerMode Then
Dim imageList As New ImageList
'Add our imageList to the ImageLists collection
'These ImageLists are then managed
'by the SharedImageListDesigner.
Me.ImageLists.Add(imageList, sharedImageList)
If component IsNot Nothing _
AndAlso component.Components IsNot Nothing Then
component.Add(imageList)
End If
Return imageList
Else
'Just return a reference to our Shared ImageList
Return sharedImageList
End If
End Function
When in designer mode (running in the IDE), we create a new We cannot simply pass the SharedImageListsDesignerFirst, I'll show what happens to the The Listing 2 - Private Sub OnComponentAdded(ByVal sender As Object, _
ByVal e As ComponentEventArgs)
If TypeOf e.Component Is ImageList _
AndAlso Me.ImageLists.ContainsKey(CType(e.Component, ImageList)) Then
Dim targetImageList As ImageList = CType(e.Component, ImageList)
Dim sharedImageList As ImageList = Me.ImageLists(targetImageList)
If sharedImageList IsNot Nothing Then
Me.InitializeImageList(targetImageList, sharedImageList)
End If
End If
End Sub
The reason for using the Listing 3 - Faking the shared Public Shared Sub CopyImageList(ByVal target As ImageList, _
ByVal source As ImageList)
If Not source.HandleCreated Then
target.ImageStream = Nothing
target.ColorDepth = source.ColorDepth
target.ImageSize = source.ImageSize
target.TransparentColor = source.TransparentColor
Return
End If
If source.ImageStream Is Nothing Then Return
Dim bf As New Binary Formatter
Dim stream As New IO.MemoryStream
bf.Serialize(stream, source.ImageStream)
stream.Position = 0
Dim ils As ImageListStreamer = _
CType(bf.Deserialize(stream), ImageListStreamer)
stream.Dispose()
target.ImageStream = Nothing
target.ImageStream = ils
End Sub
Since a component can only be sited in one place, in design mode, we need to fake that the In the Most of the other code inside the Listing 4 - Filtering Private Function FilterProperties(ByVal component As _
System.ComponentModel.IComponent, _
ByVal properties As System.Collections.IDictionary) _
As Boolean Implements _
ITypeDescriptorFilterService.FilterProperties
Dim cache As Boolean = True
If (Not Me._imageListFilterService Is Nothing) Then
cache = _
Me._imageListFilterService.FilterProperties(component, _
properties)
End If
If TypeOf component Is ImageList Then
If Utility.IsSharedImageList(Me.SharedImageLists, _
component) Then
Dim propsCopy() As PropertyDescriptor = _
Utility.ToArray(Of PropertyDescriptor)(properties.Values)
Dim pd As PropertyDescriptor
Dim keys() As String = _
Utility.ToArray(Of String)(properties.Keys)
For i As Int32 = 0 To keys.Length - 1
pd = DirectCast(properties(keys(i)), PropertyDescriptor)
Select Case keys(i)
Case "Modifiers"
properties(keys(i)) = _
TypeDescriptor.CreateProperty(pd.ComponentType, pd, _
New Attribute() {ReadOnlyAttribute.Yes})
Case "Images"
properties(keys(i)) = _
TypeDescriptor.CreateProperty(pd.ComponentType, pd, _
New Attribute() {BrowsableAttribute.No, _
ReadOnlyAttribute.Yes})
Case "ImageStream"
properties(keys(i)) = _
TypeDescriptor.CreateProperty(pd.ComponentType, pd, _
New Attribute() _
{DesignerSerializationVisibilityAttribute.Hidden, _
ReadOnlyAttribute.Yes})
Case Else
If pd.IsBrowsable AndAlso Not pd.DesignTimeOnly Then
properties(keys(i)) = _
TypeDescriptor.CreateProperty(pd.ComponentType, pd, _
New Attribute() _
{DesignerSerializationVisibilityAttribute.Hidden, _
ReadOnlyAttribute.Yes})
End If
End Select
Next
End If
cache = False
Else
cache = True
End If
Return cache
End Function
CodeDomSerializersOne of the most challenging parts of this project was writing the one simple line of code below, into the Listing 5 - The CodeDom statement that needs to be outputted by the Me.LargeImageList = Me.SharedImageLists11.NewImageList( _
Me.components, CType(Me.SharedImageLists11.GetSharedImageLists, _
WindowsApplication2.SharedImageLists1).LargeImageList)
The reason this caused the most problems is that ideally I wanted the But how do you override the default Listing 5 - The Private Function SerializeImageList( _
ByVal manager As IDesignerSerializationManager, _
ByVal sharedImageLists As SharedImageLists, _
ByVal targetImageList As ImageList, _
ByVal sharedImageList As ImageList) As CodeExpression
Dim designer As SharedImageListsDesigner = _
Utility.GetDesigner(sharedImageLists)
If designer Is Nothing Then Return Nothing
Dim sharedImageListName As String = _
designer.GetSharedImageListName(sharedImageList)
If String.IsNullOrEmpty(sharedImageListName) Then
Return Nothing
'The statement we need to construct
'Me.SmallImageList1 = _
' SharedImageLists1.NewImageList(components, _
' CType(SharedImageLists1.GetSharedImageLists, _
' MySharedImageLists).ImageList1)
Dim containerExp As CodeExpression = _
MyBase.GetExpression(manager, sharedImageLists.Container)
Dim sharedImageListsExp As CodeExpression = _
MyBase.GetExpression(manager, sharedImageLists)
Dim targetImageListExp As CodeExpression = _
MyBase.GetExpression(manager, targetImageList)
Dim sharedImageListExp As _
New CodePropertyReferenceExpression(Nothing, _
sharedImageListName)
If sharedImageListsExp Is Nothing Then
sharedImageListsExp = _
MyBase.SerializeToExpression(manager, _
sharedImageLists)
End If
If containerExp Is Nothing Then
containerExp = _
MyBase.SerializeToExpression(manager, _
sharedImageLists.Container)
End If
Dim getSharedImageListsMthd As _
New CodeMethodInvokeExpression(sharedImageListsExp, _
"GetSharedImageLists")
Dim castTo As New CodeCastExpression(sharedImageLists.GetType, _
getSharedImageListsMthd)
sharedImageListExp.TargetObject = castTo
Return New _
CodeMethodInvokeExpression(sharedImageListsExp, _
"NewImageList", containerExp, sharedImageListExp)
End Function
The next problem I found was that forms and controls started getting the newly named WSOD (White Screen of Darn). Even worse was, the IDE just crashed/disappeared when pasting a shared
After much investigation, it turned out that if the To fix this problem, the Listing 6 - The routine which deserializes a shared Public Overrides Function Deserialize(ByVal manager As _
System.ComponentModel.Design.Serialization.
IDesignerSerializationManager, _
ByVal codeObject As Object) As Object
Dim instance As Object = Nothing
For Each cc As CodeObject In CType(codeObject, _
CodeStatementCollection)
Dim codeAssign As CodeAssignStatement = _
TryCast(cc, CodeAssignStatement)
If codeAssign Is Nothing Then Continue For
Dim methodI As CodeMethodInvokeExpression = _
TryCast(codeAssign.Right, CodeMethodInvokeExpression)
If methodI Is Nothing OrElse _
methodI.Parameters.Count <> 2 Then
Continue For
Dim sharedImageLists As Object = _
MyBase.DeserializeExpression(manager, _
MyBase.GetTargetComponentName(Nothing, _
methodI.Method.TargetObject, Nothing), _
methodI.Method.TargetObject)
If sharedImageLists Is Nothing Then Continue For
If Not GetType(SharedImageLists).IsAssignableFrom(
sharedImageLists.GetType) Then
Continue For
' If we are here we can be pretty sure
' we have the correct statement.
Dim name As String = Nothing
Dim propRef As CodePropertyReferenceExpression = _
TryCast(methodI.Parameters(1), _
CodePropertyReferenceExpression)
Dim fieldRef As CodeFieldReferenceExpression = Nothing
Dim propInfo As PropertyInfo
Dim fieldInfo As FieldInfo
If propRef Is Nothing Then
fieldRef = TryCast(methodI.Parameters(1), _
CodeFieldReferenceExpression)
End If
If propRef IsNot Nothing Then
name = propRef.PropertyName
' Check that we actualy do have a property and not a field
propInfo = sharedImageLists.GetType.GetProperty(name, _
BindingFlags.Instance Or BindingFlags.Public)
If propInfo Is Nothing Then
fieldInfo = sharedImageLists.GetType.GetField(name, _
BindingFlags.Instance Or BindingFlags.Public)
If fieldInfo IsNot Nothing Then
' Change the property expression to a field expression
methodI.Parameters(1) = _
New CodeFieldReferenceExpression(propRef.TargetObject, name)
End If
End If
ElseIf fieldRef IsNot Nothing Then
' Check that we actualy do have a field and not a property
name = fieldRef.FieldName
fieldInfo = sharedImageLists.GetType.GetField(name, _
BindingFlags.Instance Or BindingFlags.Public)
If fieldInfo Is Nothing Then
propInfo = sharedImageLists.GetType.GetProperty(name, _
BindingFlags.Instance Or BindingFlags.Public)
If propInfo IsNot Nothing Then
'change the field expression to a field expression
methodI.Parameters(1) = New _
CodePropertyReferenceExpression(fieldRef.TargetObject, name)
End If
End If
End If
If propRef IsNot Nothing OrElse fieldRef IsNot Nothing Then
instance = CType(MyBase.Deserialize(manager, _
codeObject), ImageList)
' After the instance has been created we
' now need to make sure it's named correctly.
If TypeOf instance Is ImageList Then
If Not CType(instance, ImageList).Site.Name = name Then
Dim obj As Object = manager.GetInstance(name)
If obj Is Nothing Then
CType(instance, ImageList).Site.Name = name
manager.SetName(instance, name)
End If
End If
End If
End If
Next
If instance Is Nothing Then
Dim ilSerializer As New ImageListCodeDomSerializer
instance = ilSerializer.Deserialize(manager, codeObject)
End If
Return instance
End Function
The bulk of the code simply loops through the passed CodeDom statements to determine if the statements passed are for normal The final piece of the puzzleThe above code was a success, and I now have working code that both serializes and deserializes the shared Listing 7 - The Public Sub New()
TypeDescriptor.AddAttributes(GetType(ImageList), New _
Serialization.DesignerSerializerAttribute(_
GetType(SharedImageListCodeDomSerializer), _
GetType(Serialization.CodeDomSerializer)))
End Sub
All the code does is add an extra attribute to the History
| ||||||||||||||||||||