|
-
Aug 22nd, 2011, 08:40 AM
#1
Properties set during design-time are not serialized to Designer file
Hi,
I am creating a UserControl with rich design-time support that should eventually look like the Options window in Visual Studio (or many other applications). Basically a split container, to the left is a TreeView with 'option categories', and each node in the treeview corresponds to a 'panel' to the right with certain options.
Just for terminology, the nodes in the TreeView are OptionsNode objects, the panels (containing the controls that determine the options) are OptionsPanel controls. My UserControl itself is called OptionsView and is the control that contains the treeview as well as a panel that contains the OptionsPanels.
I have some experience in design-time coding, and I have gotten pretty far. The OptionsView control contains a property Panels that returns the ControlCollection of the right side of the split container. The user can add/remove OptionsPanels via this property (and automatically an OptionsNode is created). The property uses a custom CollectionEditor that tells the designer to create instances of type OptionsPanel (instead of just Control which is the usual collection type of ControlCollection). Furthermore, in the CreateInstance method I use the DesignerHost object and its CreateComponent method to create the panels, instead of just creating New OptionsPanel objects. This way the OptionsPanel created is selectable and editable (via property grid) during design-time:
vb.net Code:
Protected Overrides Function CreateInstance(ByVal itemType As System.Type) As Object
If itemType Is GetType(OptionsPanel) Then
' Use the IDesignerHost service to create a new component
' This way it's editable during design-time
Dim designerHost = DirectCast(Me.GetService(GetType(IDesignerHost)), IDesignerHost)
Dim panel = DirectCast(designerHost.CreateComponent(GetType(OptionsPanel)), OptionsPanel)
Dim view = DirectCast(Me.Context.Instance, OptionsView)
' Also set some properties
panel.Owner = view
panel.Dock = DockStyle.Fill
'Create node
Dim node As New OptionsNode()
node.Panel = panel
panel.Node = node
Return panel
End If
Return MyBase.CreateInstance(itemType)
End Function
As you can see, this is also where I create the OptionsNode, and most importantly: the connection between an OptionsNode and its OptionsPanel. The panel has a Node property and the node has a Panel property, which are set here so that a Node knows which Panel belongs to it and vice versa.
Finally, when a control is added to the right side of the split container (ControlAdded event), I cast it to an OptionsPanel and add its Node to the TreeView.
Now, during design-time, this works perfectly. I can add OptionsPanels via the Panels property, they appear fully selectable on the right side of my control, and nodes appear in the TreeView. I even wrote a custom designer to make the nodes selectable during design-time (basically just return True in the GetHitTest method when the mouse is over the TreeView), so I can select nodes (and thus panels) also during design-time (when the treeview selection changes, I hide all panels except the panel associated with that OptionsNode).
The problem is when I try to run the application. The form Designer file and the InitializeComponent method is simply wrong. The error is quite simple: the OptionsPanels as well as the OptionsNodes are created just fine, but the connection between them is nowhere to be found! Furthermore, even IF that connection was made, it would be made in the wrong place. Here's a bit of the InitializeComponent method:
vb.net Code:
Private Sub InitializeComponent()
Dim OptionsNode1 As OptionsViewLibrary.OptionsNode = New OptionsViewLibrary.OptionsNode()
Dim OptionsNode2 As OptionsViewLibrary.OptionsNode = New OptionsViewLibrary.OptionsNode()
Dim OptionsNode3 As OptionsViewLibrary.OptionsNode = New OptionsViewLibrary.OptionsNode()
Me.OptionsView1 = New OptionsViewLibrary.OptionsView()
Me.OptionsPanel1 = New OptionsViewLibrary.OptionsPanel()
Me.OptionsPanel2 = New OptionsViewLibrary.OptionsPanel()
Me.OptionsPanel3 = New OptionsViewLibrary.OptionsPanel()
Me.SuspendLayout()
'
'OptionsView1
'
Me.OptionsView1.Location = New System.Drawing.Point(360, 84)
Me.OptionsView1.Name = "OptionsView1"
Me.OptionsView1.Panels.Add(Me.OptionsPanel1)
Me.OptionsView1.Panels.Add(Me.OptionsPanel2)
Me.OptionsView1.Panels.Add(Me.OptionsPanel3)
Me.OptionsView1.SelectedPanel = Nothing
Me.OptionsView1.Size = New System.Drawing.Size(462, 373)
Me.OptionsView1.TabIndex = 0
'...
End Sub
(I forgot to mention I am also using a custom TypeConverter for the OptionsNode class which ensures the designer creates New OptionsNode() objects; otherwise it would create TreeNode objects and try to cast them to OptionsNode, not going to work obviously (cast from base to derived type)).
As you can see, nodes and panels are created just fine, but they are never told to whom they belong. Then, when the properties for the OptionsView itself are being set, the Panels are added to the Panels property. This is when my ControlAdded event is handled and where I try to add the Node property of each panel to the TreeView. Obviously, this Node property is Nothing at this moment because it has never been set. Even if it WOULD be set in code, that would (probably) only be when the properties of each OptionsPanel are set, which is AFTER the properties of the OptionsView is set (this is just the usual order for InitializeComponent I think?), so it is also AFTER panels are added...
So two questions:
1. How can I tell the designer to serialize the connection between nodes and panels. At the moment this is simply done by setting the Node and Panel properties, that works during design-time but the properties don't persist to the designer file. I tried using the DesignerSerializationVisibility on both the Node and Panel property (or both) but that resulted in the Nodes not being serialized at all (they are still created during design-time, but they no longer exist during run-time, so I get the same problem).
2. Once problem 1 is solved, how can I make sure the connection is made BEFORE the panels are added to the Panels property? This is a requirement because at that point I need to know what Node belongs to each Panel, this is where I add the nodes to the TreeView...
Thanks for any help!
-------
Just for completion, here's the relevant code for the OptionsView, OptionsNode and OptionsPanel classes. Nothing special really:
vb.net Code:
Public Class OptionsPanel
Inherits Panel
Private _Owner As OptionsView
Public Property Owner() As OptionsView
Get
Return _Owner
End Get
Friend Set(ByVal value As OptionsView)
_Owner = value
End Set
End Property
Private _Node As OptionsNode
Public Property Node() As OptionsNode
Get
Return _Node
End Get
Friend Set(value As OptionsNode)
_Node = value
End Set
End Property
End Class
<TypeConverter(GetType(Converters.OptionsNodeConverter))> _
Public Class OptionsNode
Inherits TreeNode
Public Sub New()
End Sub
Private _Panel As OptionsPanel
Public Property Panel() As OptionsPanel
Get
Return _Panel
End Get
Friend Set(value As OptionsPanel)
_Panel = value
Me.Text = value.Name
End Set
End Property
End Class
<Designer(GetType(Designers.OptionsViewDesigner))> _
Public Class OptionsView
Public Event SelectedPanelChanged As EventHandler
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
AddHandler Me.TreeView.AfterSelect, AddressOf OnNodeSelected
AddHandler Me.PanelContainer.ControlAdded, AddressOf OnPanelAdded
AddHandler Me.PanelContainer.ControlRemoved, AddressOf OnPanelRemoved
End Sub
#Region " Properties "
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
Public ReadOnly Property TreeView As TreeView
Get
Return Me.tvw
End Get
End Property
Public ReadOnly Property PanelContainer As OptionsPanelContainer
Get
Return Me.pnl
End Get
End Property
<Editor(GetType(Editors.OptionsPanelCollectionEditor), GetType(UITypeEditor))> _
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
Public ReadOnly Property Panels() As Control.ControlCollection
Get
Return Me.PanelContainer.Controls
End Get
End Property
Private _SelectedPanel As OptionsPanel
Public Property SelectedPanel() As OptionsPanel
Get
Return _SelectedPanel
End Get
Set(ByVal value As OptionsPanel)
_SelectedPanel = value
Me.OnSelectedPanelChanged()
End Set
End Property
#End Region
'...
End Class
-
Aug 22nd, 2011, 04:36 PM
#2
Re: Properties set during design-time are not serialized to Designer file
News:
I have managed to get a LITTLE bit further. I found out I can declare functions called 'ShouldSerialize<property>' where <property> is replaced by the name of a property. When I add ShouldSerializeNode to the OptionsPanel class for example, the Node property is set to the right panel.
However, as I feared, not at the right time. It is set after the panels are already added to the OptionsView control, too late.
I also tried having the constructor of OptionsNode accept a Panel, so I could create their connection in that constructor, but I did not manage to let my TypeConverter return the correct InstanceDescriptor; I tried getting the constructor with a single OptionsPanel as a parameter and tried passing the Panel, but it keps generating a parameterless constructor, which no longer exists and thus creates errors.
I found out about two more very promising methods just now, which I will try tomorrow. Perhaps before that time someone else can comment on my ideas:
1. I can let my control implement ISupportInitialize which should cause the designer to call BeginInit and EndInit methods before and after setting its properties. I could then add the nodes in the EndInit method, instead of when the panels are added to the control. At that point, hopefully, the nodes would be set and I can use them.
2. I found out one can control the InitializeComponent code serialization using a custom CodeDomSerializer. I haven't looked into this in detail, it seems quite complicated, but it also seems like it might be just what I need.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|