|
-
Aug 20th, 2011, 12:17 PM
#1
[Ideas wanted] Options view control - child options
Hi,
I am in need of a form that shows various options, exactly like the Options in Visual Studio. Since there are so many options I too want them categorized, with a TreeView to the left taking care of showing the right category.
The usual 'easy' approach here would be to just place a TreeView control on the form, add some nodes, and give those nodes a tag or key that corresponds to a panel or UserControl with the options for that category.
Since there will be a lot of options however, this is not really feasible design-wise; the form would be cluttered with possibly 50 panels, all of which I would need to select and bring to front from time to time to add controls to them that represent the options.
So I decided to create a custom control that does exactly that. The control is very similar to my Wizard usercontrol, users can add OptionsPanels at design time, which inherit Panel and simply represent one panel of options. When they do, the panel is added to a container panel, and at the same time a TreeNode is added to a TreeView. The control uses a custom ControlDesigner to handle design-time clicks in the Treeview, selecting a different node would select and bring to front the corresponding panel, allowing the user to add the controls he wants.
Due to the design time support the problem of having 50 panels is no longer present, only one panel will be visible at a time and selecting the right panel is as simple as selecting the corresponding TreeNode, just as during run-time.
Anyway, I got all this working, but only for a single 'level' of categories. As you can see in the Visual Studio options, there can be multiple levels of categories. For example, the Environment node has a bunch of children, where each child represents one 'options panel'. There can even be deeper nesting, see the Text Editor node for example.
I am having trouble designing this feature and would appreciate some help or ideas.
Let me begin by drawing out the basics of how my control works so far.
The main control is an OptionsView control, which contains a SplitContainer with a TreeView to the left and a OptionsPanelContainer to the right. The OptionsPanelContainer is merely a Panel to which only OptionsPanel controls can be added, and which raises events when this happens, as well as when OptionsPanels are removed from it.
An OptionsPanel also inherits Panel, and these are the actual panels the users will see in the control, one for each option category.
For now, each OptionsPanel has exactly one corresponding TreeNode (and vice versa). In the Visual Studio options, each 'parent' category usually has a 'General' node as the first child, and the parent and this General node show the same option panel, but I am ignoring that for the moment.
The OptionsView control has a property Panels that returns the ControlCollection (Controls property) of the OptionsPanelContainer (in other words: it returns a collection of OptionsPanels that are in this container panel).
vb.net Code:
<Editor(GetType(Designers.OptionsPanelCollectionEditor), GetType(UITypeEditor))> _ <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _ Public ReadOnly Property Panels As Control.ControlCollection Get Return Me.PanelContainer.Controls End Get End Property
A custom CollectionEditor for this property takes care of the designer: even though the property type is ControlCollection, the CollectionEditor knows it should create instances of the OptionsPanel control instead of just Controls.
When it does this, a corresponding TreeNode is also created and its Tag property is set to the OptionsPanel. Vice versa, the Node property of the OptionsPanel is set to the node. Hence the node and panel both know their corresponding object.
vb.net Code:
Public Class OptionsPanelCollectionEditor Inherits System.ComponentModel.Design.CollectionEditor Public Sub New(type As Type) MyBase.New(type) End Sub Protected Overrides Function CreateCollectionItemType() As System.Type Return GetType(OptionsPanel) End Function 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 Dim node As New TreeNode(panel.Name) node.Tag = panel panel.SetNode(node) Return panel End If Return MyBase.CreateInstance(itemType) End Function End Class
Finally, when an OptionsPanel is added to the OptionsPanelContainer, it raises an event and its TreeNode is added to the TreeView. The same goes for removing. When a TreeNode is selected, its Panel is set as the SelectedPanel. Setting the SelectedPanel merely hides all panels except the selected one:
vb.net Code:
Private Sub container_PanelAdded(sender As System.Object, e As OptionsViewLibrary.OptionsPanelContainer.OptionsPanelEventArgs) Handles container.PanelAdded Me.TreeView.Nodes.Add(e.Panel.Node) End Sub Private Sub container_PanelRemoved(sender As System.Object, e As OptionsViewLibrary.OptionsPanelContainer.OptionsPanelEventArgs) Handles container.PanelRemoved Me.TreeView.Nodes.Remove(e.Panel.Node) End Sub Private Sub tvw_AfterSelect(sender As System.Object, e As System.Windows.Forms.TreeViewEventArgs) Handles tvw.AfterSelect If e.Node IsNot Nothing Then Dim panel = TryCast(e.Node.Tag, OptionsPanel) If panel IsNot Nothing Then Me.SelectedPanel = panel End If End If End Sub
So far so good, this all works fine. I can add Panels via the designer and when I do a new TreeNode appears in the TreeView. I can select this node and the panel becomes visible (comes to the front).
Now, I am a little stuck. How do I implement child option panels? And more importantly: how do I let the user add child panels?
The most logical choice I think is to let each OptionsPanel have a property (ChildPanels or something) that returns the child OptionsPanels for that panel. Once the user selects an OptionsPanel then, he can look in the property grid to find its ChildPanels property and add child panels to that.
There is a problem though: what would this property return? It must return a ControlCollection of some container (this is, I think, a requirement for the designer features to work, otherwise panels are not added to the Form.Designer.vb file).
But there is no container. I cannot add them to the OptionsPanel itself, that would make no sense because the parent OptionsPanel has its own set of controls (the options itself...), there cannot be another (fully docked) Panel on top of those obviously.
The container of the main OptionsView then? That is not an option either, its Controls collection holds ALL OptionsPanels, not just the children of the selected panel. I cannot 'select' only the right panels either, that would require me to return a new instance of ControlCollection, it would be impossible to return the actual ControlsCollection that holds merely a small selection of its controls.
I realize this post might be hard to understand, but if you have any idea at all just let me know, perhaps I can work with it.
Thanks!
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
|