Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to create a UserControl or Derived Panel to act like a contain

463 views
Skip to first unread message

Dave Leach

unread,
Feb 12, 2007, 6:26:00 PM2/12/07
to
I am using VS 2003 and .NET 1.1 for my Windows Form project, which is being
written with C#. We may upgrade to VS 2005 and .NET 2.0 in the near future.

I am trying to create a control that can be re-used by other developers to
add their own controls like Buttons and TextBoxes using the Visual Studio
2003 Forms Designer. I have tried creating the control by deriving from
Panel and UserControl, without success.

Note that I have read some recent postings by Lee Grissom in this newsgroup
and the windowsform newsgroup. Using his discoveries have not lead to a
completely succuess result.

The control I am trying to create can be visualized as a Panel that contains
a Button and GroupBox. By using appropriate DockPadding settings, the small
square Button sits in the upper left corner of the panel with the GroupBox
occupying the rest of the panel to the right and down. When the user clicks
on the button, the panel either shrinks vertically to the height of the
Button and the GroupBox title text or expands vertically to its nomal full
height. The intent is to contain several other controls that can be hidden
or shown, occupying only as much space as is needed depending on the "state"
of the Button. The Button displays the simple text string of "-" or "+",
indicating either a state of expanded or collapsed.

This "panel" control must support the following:

1. The control must be able to be dropped onto a form from the Toolbox.
2. Once dropped onto a form, a developer must be able to drop other standard
controls onto it (Labels, TextBoxes, ComboBoxes, etc.)
3. The added controls should be contained by the internal GroupBox such that
setting their Location property places them normally within the boundaries of
the GroupBox. Setting their TabIndex is relative to and contained within the
GroupBox.
4. The "panel" control must behave like any other container controls within
the Forms Designer. (Displays a selected border, provides selectable grid
patterns, displays editable properties, etc.)

Thanks for any help.
Dave

--
Dave Leach
Agilent Technologies, Inc.

Linda Liu [MSFT]

unread,
Feb 13, 2007, 4:34:39 AM2/13/07
to
Hi Dave,

Based on my understanding, you'd like to implement a control which can be
re-used by other developers to add their own controls onto your control.
Your control contains a button and a group box. After a developer drag&drop
your control onto a form, he should be able to add some standard controls,
such as TextBox, LisBox, etc into the inner group box within your control
at design-time. If I'm off base, please feel free to let me know.

Panel and UserControl are all containers, but a difference between the two
controls is that when a panel is added on a form, we could drag&drop other
controls into the panel; however, when a user control is added on a form,
we could not drag&drop other controls into the user control.

This is because the designer of a user control does not support such a
'drag&drop' design-time function as the designer for a panel does.

I think the correct direction to solve your problem is to create your
control by deriving from UserControl class, and then implement a custom
designer for your control to enable the 'drag&drop' design-time function.

I have tried adorning a designer of
"System.Windows.Forms.Design.ScrollableControlDesigner" with the derived
UserControl, but unfortunately this only enables the 'drag&drop'
design-time function for the user control, i.e. the controls are dropped
onto the user control, not the inner group box. The following is the code.

[Designer("System.Windows.Forms.Design.ScrollableControlDesigner,
System.Design, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a")]
public partial class UserControl1 : UserControl
{ ....}

I will go on research on this problem and will get the result back to you
ASAP. I appreciate your patience.


Sincerely,
Linda Liu
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.

Dave Leach

unread,
Feb 13, 2007, 12:57:00 PM2/13/07
to
Linda,

I had already tried adding the designer to my UserControl. As you found,
this only enables dropping controls onto the UserControl, even if you change
all of the internal controls to 'public'.

I also found that the UserControl, with the designer added, did not behave
well in the Forms Designer. Clicking on the UserControl in the Forms
Designer would not select it for editing. The only way I could find to get
it to become selected was to find it in the Properties window drop down list.
Even then it did not fully appear to be selected; the border changed but it
did not display a positioning grid.

I look forward to hearing what you find from your research.

Thanks,
Dave

--
Dave Leach
Agilent Technologies, Inc.

Oliver Sturm

unread,
Feb 13, 2007, 1:26:25 PM2/13/07
to
Hello Dave,

>I am trying to create a control that can be re-used by other developers to
>add their own controls like Buttons and TextBoxes using the Visual Studio
>2003 Forms Designer. I have tried creating the control by deriving from
>Panel and UserControl, without success.

Yet you don't mention what your problem is.... I might be speaking for
myself here, but I'm not going to write that control for you, after your
specs :-) With some description of what exactly is not working for you, we
might be able to help you finish the task yourself.


Oliver Sturm
--
http://www.sturmnet.org/blog

Oliver Sturm

unread,
Feb 13, 2007, 1:45:34 PM2/13/07
to

>Yet you don't mention what your problem is.... I might be speaking for
>myself here, but I'm not going to write that control for you, after your
>specs :-) With some description of what exactly is not working for you, we
>might be able to help you finish the task yourself.

My newsgroup content wasn't updated when I posted this comment - I see
you're already discussing things in another part of the thread.

Dave Leach

unread,
Feb 13, 2007, 4:46:01 PM2/13/07
to
Oliver,

The only reason I listed "specs" was to give any reader a picture of what I
am trying to do and that the simple solution you provided to Lee Grissom does
not provide a complete solution for my requirements. While adding the
Designer attribute to the UserControl allows you to add a contol, it does not
allow you to choose the parent of the control within the UserControl. In my
case the GroupBox.

Also, I found that simply addiing the Designer attribute does not provide a
well behaved control in the Forms Designer. The UserControl did not display
with the expected decorations (like positioning grids) and could not be
selected for editing by simply clicking on it. Is your experience different
than this?

My hope is to create a control that is well behaved in the Forms Designer
and allows a nested usage pattern. Whatever control I drop onto the
UserControl, including another UserControl, is placed within the GroupBox.
This seems like a useful capability that I have not found described elsewhere.

Regards,


Dave
--
Dave Leach
Agilent Technologies, Inc.

Oliver Sturm

unread,
Feb 14, 2007, 5:15:10 AM2/14/07
to
Hello Dave,

>The only reason I listed "specs" was to give any reader a picture of what I
>am trying to do

Right. I was only commenting on the fact that you didn't say what exactly
in the process of creating that control was the problem.

>and that the simple solution you provided to Lee Grissom does
>not provide a complete solution for my requirements.

I'm not sure which post of Lee Grissom's you mean. There's one thread that
I suspect you mean, where I pointed Lee at the possibility of using the
OnControlAdded event to reorganize nested controls. As this is what you
want to do - change the way the designer parents dropped controls by
default - I think it'll be the way you'll have to go. I did some simple
tests and I see that there's something the designer complains about when a
dropped child control is reparented during that event handler - I guess
that's the problem you're referring to here (again, you don't really state
what your problem is, apart from saying that it "does not provide a
complete solution").

I'm sure there's a workaround that can be found for that part of the
issue, but I don't have extensive time right at this moment to look into
it. My advice is not to drop this idea, as I'm pretty sure it is the right
point at which to look - as long as we're not restructuring the whole
concept.

>While adding the
>Designer attribute to the UserControl allows you to add a contol, it does
>not
>allow you to choose the parent of the control within the UserControl.

No, that's true. That designer attribute doesn't have anything to do with
letting you choose the parent - in fact I believe that the drag&drop model
of the VS designer doesn't include the concept of "letting you choose the
parent". It assumes that the parent is the control onto which another
control is dropped - not an altogether unreasonable assumption :-)

>Also, I found that simply addiing the Designer attribute does not provide a
>well behaved control in the Forms Designer. The UserControl did not
>display
>with the expected decorations (like positioning grids) and could not be
>selected for editing by simply clicking on it. Is your experience
>different
>than this?

In part I admit I don't know what you mean - positioning grids? What are
those? I don't see any grids in my VS form designer, whether in
conjunction with that user control or without it.

Other than that, your problem seems to be one of defining "well behaved
control in the Forms Designer". The behaviour you are seeing with that
attribute applied is the standard behaviour of container controls in VS
2005 - any boring standard Panel control you drop on your form behaves the
same way. It was changed from VS 2003 because this allows the designer to
let you select controls *inside* the container using the rubber band, for
instance, without accidentally dragging the parent container around. To do
this, there's the special element with the four-point arrow in the top
left corner of such containers, and using that designer attribute we've
been referring to, my test user control starts behaving exactly like I
would expect it to, from looking at other controls like the Panel.

>My hope is to create a control that is well behaved in the Forms Designer

In my book you've managed to do that with the designer attribute - if you
have a different opinion, I'm either not understanding it yet or you're
trying to do something that is different from the VS designer's standard
handling.

>and allows a nested usage pattern. Whatever control I drop onto the
>UserControl, including another UserControl, is placed within the GroupBox.

Right, I understand that part. I think there are two solutions:

1. Follow up on the OnControlAdded approach and figure out how to make it work correctly. I'm pretty sure this will be possible if done right.

2. Restructure the whole idea - it is probably also possible to create some kind of a derived Panel that has a very wide margin.... the nested controls would not have to be reparented in this case, and the margin could be used to display the "outer" UI element (that +/- button you want). This might be difficult to get just right, but it may work better with the standard mechanisms of the VS designer.

>This seems like a useful capability that I have not found described
>elsewhere.

Sounds useful, yes - not described elsewhere? I haven't looked, but I
would be kind of surprised if there wasn't anything around on CodeProject
or similar sites that could give some more hints and ideas.

Linda Liu [MSFT]

unread,
Feb 15, 2007, 7:51:12 AM2/15/07
to
Hi Dave,

After doing some research, I could drag&drop a control from Toolbox into
the inner group box in the UserControl now. I extend the design-time
behavior of the UserControl by implementing a custom Behavior.

The following is the code of the custom designer for the UserControl.

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Design;
using System.ComponentModel.Design;
using System.Windows.Forms.Design.Behavior;
using System.IO;
using System.Collections;

[Designer (typeof(MyDesigner))]


public partial class UserControl1 : UserControl
{

public UserControl1()
{
InitializeComponent();
}
public GroupBox InnerGB
{
get { return this.groupBox1; }
}
}

public class MyGlyph : Glyph
{
private IDesignerHost host = null;
private BehaviorService behaviorSvc = null;
private IToolboxService toolboxSvc = null;
private ISelectionService selectionSvc = null;
private Control control = null;

public MyGlyph(IDesignerHost h, IToolboxService tbs,
ISelectionService ss, BehaviorService bs, Control ctrl)
: base(new MyBehavior())
{
host = h;
behaviorSvc = bs;
toolboxSvc = tbs;
selectionSvc = ss;
control = ctrl;
}
public override Rectangle Bounds
{
get
{
Rectangle rect =
behaviorSvc.ControlRectInAdornerWindow((control as UserControl1).InnerGB);
return rect;
}
}

public IDesignerHost DesignerHost
{
get { return this.host; }
}
public Control InnerControl
{
get { return control; }
}
public BehaviorService BahaviorService
{
get { return this.behaviorSvc; }
}
public IToolboxService ToolboxService
{
get { return this.toolboxSvc; }
}
public ISelectionService SelectionService
{
get { return this.selectionSvc; }
}
public override Cursor GetHitTest(Point p)
{
if (this.Bounds.Contains(p))
{
return Cursors.Default;
}
return null;
}
public override void Paint(PaintEventArgs pe)
{
using (Pen mypen = new Pen(Color.Blue))
{
mypen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
pe.Graphics.DrawRectangle(mypen, this.Bounds.X + 4,
this.Bounds.Y + 10, this.Bounds.Width - 8, this.Bounds.Height - 14);
}
}
}

public class MyBehavior : Behavior
{
public override bool OnMouseDown(Glyph g, MouseButtons button,
Point mouseLoc)
{
BehaviorService bs = (g as MyGlyph).BahaviorService;
ISelectionService ss = (g as MyGlyph).SelectionService;

GroupBox gb = ((g as MyGlyph).InnerControl as
UserControl1).InnerGB;

Point screenpt = bs.AdornerWindowPointToScreen(mouseLoc);
Point pt = gb.PointToClient(screenpt);
Control ctrl = gb.GetChildAtPoint(pt);
if (ctrl != null)
{
ArrayList array = new ArrayList();
array.Add(ctrl);
ss.SetSelectedComponents(array);
}
return true;

}

public override void OnDragEnter(Glyph g, DragEventArgs e)
{
if (e.Data.GetDataPresent("CF_NDP_TYPENAME"))
{
e.Effect = DragDropEffects.Copy;
}
else
{
e.Effect = DragDropEffects.None;
}
}
public override void OnDragDrop(Glyph g, DragEventArgs e)
{
MemoryStream ms = e.Data.GetData("CF_NDP_TYPENAME") as
MemoryStream;
if (ms != null)
{
byte[] bytes = new byte[256];
ms.Read(bytes, 0, (int)ms.Length);
System.Text.Encoding encoding =
System.Text.Encoding.Unicode;
string typename = encoding.GetString(bytes);

char[] separater = new char[] { ',' };
string[] parts = typename.Split(separater);

string componentTypeName = parts[0].Trim();

IDesignerHost host = (g as MyGlyph).DesignerHost;
if (host != null)
{
Type componentType = Type.GetType(componentTypeName);
if (componentType.IsSubclassOf(typeof(Control)))
{
IComponent component =
host.CreateComponent(Type.GetType(componentTypeName));

Point pt = ((g as MyGlyph).InnerControl as
UserControl1).InnerGB.PointToClient(new Point(e.X, e.Y));
MessageBox.Show("drop location:" + e.X.ToString() +
" " + e.Y.ToString());
(component as Control).Location = pt;
// (component as Control).Text = (component as
Control).Name;
((g as MyGlyph).InnerControl as
UserControl1).InnerGB.Controls.Add(component as Control);
(g as
MyGlyph).ToolboxService.SelectedToolboxItemUsed();
}
else
{
e.Effect = DragDropEffects.None;
}
}
}
}
}

public class MyDesigner : System.Windows.Forms.Design.ControlDesigner
{
private Adorner myAdorner;

private IToolboxService toolboxSvc = null;
private ISelectionService selectionSvc = null;
private IDesignerHost host = null;

protected override void Dispose(bool disposing)
{
if (disposing && myAdorner != null)
{
BehaviorService b = BehaviorService;
if (b != null)
{
b.Adorners.Remove(myAdorner);
}
}
}

public override void Initialize(IComponent component)
{
base.Initialize(component);

toolboxSvc = this.GetService(typeof(IToolboxService)) as
IToolboxService;
host = this.GetService(typeof(IDesignerHost)) as IDesignerHost;
selectionSvc = this.GetService(typeof(ISelectionService)) as
ISelectionService;

myAdorner = new Adorner();
BehaviorService.Adorners.Add(myAdorner);
myAdorner.Glyphs.Add(new MyGlyph(host,toolboxSvc,selectionSvc,
BehaviorService,Control));

}

}

The problem of the custom designer is that the code of adding the
dragged&dropped control into the Controls property of the inner group box
is not generated in the InitializeComponent method. I will go on research

on this problem and will get the result back to you ASAP.

Dave Leach

unread,
Feb 15, 2007, 12:32:33 PM2/15/07
to
Linda,

The code from your previous message will not build in my environment. The
types Adorner, Behavior, BehaviorService and Glyph are not recognized. Are
they .NET 2.0 types that are not available in .NET 1.1?

Please refer to my original message where I describe my environment. Will I
need to upgrade to get the desired designer behavior?

Regards,


Dave
--
Dave Leach
Agilent Technologies, Inc.

Oliver Sturm

unread,
Feb 15, 2007, 3:18:35 PM2/15/07
to
Hello Linda,

>The following is the code of the custom designer for the UserControl.

Hehe... good job on that :-) You must be enormously bored... the solution
I was looking for was on a slightly easier level.

Linda Liu [MSFT]

unread,
Feb 15, 2007, 8:56:21 PM2/15/07
to
Hi Dave,

Thank you for your prompt feedback.

Yes, the Adorner, Behavior, BehaviorSerive and Glyph are all new classes in
.NET 2.0, which are within System.Windows.Forms.Design.Behavior namespace
and System.Design assembly. I am sorry I didn't mention it in my previous
reply.

.NET 2.0 provides more extensible model for design-time programming. I
suggest that you upgrade to .NET 2.0 and VS2005 to get the desired designer
behavior.

I have implemented a custom CodeDOMSerializer for the UserControl to get

the code of adding the dragged&dropped control into the Controls property

of the inner group box serialized in the InitialComponent method.

The following is the code. It requires that you add a reference to the
System.Design assembly in your project.

using System.ComponentModel.Design.Serialization;
using System.CodeDom;

public class MyCodeDomSerializer : CodeDomSerializer
{

public override object Deserialize(IDesignerSerializationManager
manager, object codeObject)
{
CodeDomSerializer baseClassSerializer =
(CodeDomSerializer)manager.GetSerializer(typeof(UserControl1).BaseType,
typeof(CodeDomSerializer));

return baseClassSerializer.Deserialize(manager, codeObject);

}
public override object Serialize(IDesignerSerializationManager
manager, object value)
{
CodeDomSerializer baseClassSerializer =
(CodeDomSerializer)manager.GetSerializer(typeof(UserControl1).BaseType,
typeof(CodeDomSerializer));
// serialize the UserControl
object codeObject = baseClassSerializer.Serialize(manager,
value);

if (codeObject is CodeStatementCollection)
{
CodeStatementCollection statements =
(CodeStatementCollection)codeObject;

// The code statement collection is valid, so add a comment.
string commentText = "This comment was added to this object
by a custom serializer.";
CodeCommentStatement comment = new
CodeCommentStatement(commentText);

statements.Insert(0, comment);
// serialize the inner DataGridView's columns

if (value is UserControl1)
{
Control.ControlCollection ctrls = (value as
UserControl1).InnerGB.Controls;

CodeFieldReferenceExpression target =
base.SerializeToExpression(manager, value) as CodeFieldReferenceExpression;
CodeMethodInvokeExpression methodcall = null;


if (target != null)
{
for (int i = 0; i < ctrls.Count; i++)
{
CodeFieldReferenceExpression
ctrl_FieldReference = base.SerializeToExpression(manager, ctrls[i]) as
CodeFieldReferenceExpression;
if (ctrl_FieldReference != null)
{
methodcall = new
CodeMethodInvokeExpression(new
CodeVariableReferenceExpression(target.FieldName + ".InnerGB.Controls"),
"Add", new CodeExpression[] { ctrl_FieldReference });
statements.Add(methodcall);
}
}
}

}
}
return codeObject;
}
}

Then we adorn the DesignerSerializerAttribute to the UserControl.

[Designer(typeof(MyDesigner))]
[DesignerSerializer(typeof(MyCodeDomSerializer),typeof(CodeDomSerializer))]


public partial class UserControl1 : UserControl

{ .....}

Build the project. Open the form in the designer and drag&drop a control,
e.g.a TextBox from Toolbox onto the inner group box of the UserControl. You
should see the TextBox is added in the group box in the designer. Swith to
the designer.cs file of the form, and you'll see the text box is added to
the group box in the InitializeComponent method.

I have tested my custom designer and CodeDOMSerializer and they work on my
side.

If you have any question, please feel free to let me know.

BTW, I will be on a long vacation from the next Monday to Friday. During
this period, my team mates will follow up to you and it may not in time.
Sorry for the inconvenience it may brings to you!

Dave Leach

unread,
Feb 16, 2007, 2:27:08 PM2/16/07
to
Linda,

Since the solution to this issue involves VS2005 and .NET 2.0, I will have
to put a hold on further development of my user control as we are currently
in the middle of an SQ phase of our project leading up to a customer release
and I am therefore unable to upgrade my workstation to VS 2005 at this time
(involves source control integration issues).

We do plan to upgrade soon after the release, so I will be able to turn my
attention back to this issue at that time. I have captured all of my work on
the new user control and added your designer solutions to the project code
pool, but with the designer extensions commented out.

Thank you for all of your help on this and I look forward to learning more
about the VS 2005 designer customizations. I hope you enjoy your time off:-)

Best regards,


Dave
--
Dave Leach
Agilent Technologies, Inc.

Jeffrey Tan[MSFT]

unread,
Feb 18, 2007, 7:48:59 AM2/18/07
to
Hi Dave,

Thanks for your feedback!

Since Linda is on vacation in next week, I will backup her posts in this
period.

Yes, I understand that the project development lifecycle may not allow you
to upgrade to .Net2.0.

Ok, if you have time to check this issue again after the release, please
feel free to post or reopen, I and Linda will continue to work with you.
Thanks.

Best regards,
Jeffrey Tan

0 new messages