Click here to Skip to main content
Click here to Skip to main content

C# design-time custom panel (drag and drop into it) web custom control

, 31 Oct 2006 CPOL
Rate this:
Please Sign up or sign in to vote.
Create a custom panel that can expand/contract and that you can nest other controls in.

Introduction

When creating long forms for websites, I often find it is useful to have sections of the form that can be expanded/contracted or just plain closed when done. With this in mind, I created a baseControl that can be inherited by my Web Custom Controls that will allow each to have the functionality to be expanded and contracted, as well as some other functions. This all worked perfectly, but did mean you had to create all your controls in code. I don't really see this as a problem, but some people did point out that it might be nice to be able to drag the baseControl onto a web form and then dump other controls into it. So, this is what I did.

The control is basically my own version of a panel, I'll call it basePanel. This panel has a title bar which holds the title, an Expand or Contract button, and a Close button. There is then a main DIV area that any control can be placed into. Then there is a footer which holds a Cancel button and a Save button. All of these things are controllable via properties, so you can make the resulting control fit a good range of functional uses.

Here are some examples:

Example 1

Sample image

This first example shows three basePanel controls. As you can see, the header and footer sections are in grey. I've chosen not to have a Close button on display, but the Cancel and Save buttons are visible in the open controls footer.

Example 2

Sample image

This example shows the basePanel control without the header or footer sections. This is actually an example of the inherited version that allows us to perform several actions against one business object. In this example, the control acts as a Search, Create, and Display/Select control.

Example 3

Sample image

This example shows the basic control with everything left on. You can see the addition of the Close button. In this example, I had dropped a button control onto my basePanel to check that event handling was working for child controls.

Example 4

Sample image

I've highlighted the control here in red as it's pretty inconspicuous. This is actually the same control as in Example 2, but this time it is in Display/Select mode. This example really shows the versatility of the multimode control; every time I need to do any function on that specific business object, I can use the same custom Web Control.

Now, I will go through how the basePanel works. Next time I will explain the multimode control.

basePanel

Before I begin, let me plead my case for doing this. I'm 100% sure that there is a much easier way of achieving the results, but after three days of looking, I could not find out how to do this. Unfortunately, the new help functionality on VS2005 had just been installed, and it now appears that finding answers is the sole domain of Google. Shame really, help in 2003 was kind of helpful. If anyone knows a simple way to create a custom control that you can drop other controls into at design time, please let me know.

The first problem I had with creating a control that I could drop other controls into was how to tell the IDE that this was allowed for that control. Eventually, I found what I needed.

[ToolboxData("<{0}:basePanel runat="server">")]
[Designer("System.Web.UI.Design.ReadWriteControlDesigner, System.Design")]
[PersistChildren(true)]
[ParseChildren(false)]

The [ToolboxData] attribute just lets it be shown in the toolbox, and sets the default naming when dragged onto a page. The real work happens with the other three attributes. Personally, I find it really difficult to get good information about what attribute tags are out there and what they do.

So the attributes solved the problem of not being able to nest controls inside my existing control. The next problem was that if you rendered the control, all the nested controls appeared outside of the basePanel. This was fixed by some fairly major overriding of the Render method.

protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
    if (HasControls())
    {
        //open a temp stream to render first two
        //controls into (base control and custome control)
        System.IO.MemoryStream memstr = new System.IO.MemoryStream();
        System.IO.StreamWriter stream = new System.IO.StreamWriter(memstr);
        HtmlTextWriter wt = new HtmlTextWriter(stream);
        //render first 2 controls to stream
        for (int i = 0; i < 2; i++)
        {
            this.Controls[i].RenderControl(wt);
            wt.Flush();
        }
        //set to start of stream
        memstr.Seek(0, System.IO.SeekOrigin.Begin);
        //read back the html from memory
        System.IO.StreamReader rd = new System.IO.StreamReader(memstr);
        char[] bytes = new char[memstr.Length];
        rd.ReadBlock(bytes, 0, (int)memstr.Length);
        //create string builder of the correct length
        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        sb.EnsureCapacity((int)memstr.Length);
        //post the bytes to the string builder
        sb.Append(bytes);
        //close everything
        memstr.Close();
        stream.Close();
        rd.Close();
        wt.Close();
        //open a temp stream to render any controls added in the IDE
        memstr = new System.IO.MemoryStream();
        stream = new System.IO.StreamWriter(memstr);
        wt = new HtmlTextWriter(stream);
        //render all other controls added during IDE
        for (int i = 2; i < Controls.Count; i++)
        {
            this.Controls[i].RenderControl(wt);
            wt.Flush();
        }
        //set to start of stream
        memstr.Seek(0, System.IO.SeekOrigin.Begin);
        //read back the html from memory
        rd = new System.IO.StreamReader(memstr);
        bytes = new char[memstr.Length];
        rd.ReadBlock(bytes, 0, (int)memstr.Length);
        //create string builder of the correct length
        System.Text.StringBuilder sbChild = new System.Text.StringBuilder();
        sbChild.EnsureCapacity((int)memstr.Length);
        //post the bytes to the string builder
        sbChild.Append(bytes);
        //close everything
        memstr.Close();
        stream.Close();
        rd.Close();
        wt.Close();
        //find main div and add all IDE time controls into div statement
        sb.Replace("< div class="mainDiv" >", 
                   "< div class="mainDiv" >" + sbChild.ToString());

        //write the html string
        writer.WriteLine(sb.ToString());
    }
}

As you can see, the code above basically renders the first two controls (root control and my custom panel) and puts them in a StringBuilder (sb). It then renders everything after these controls into another StringBuilder (sbChild). Then, at the very end, there is a replace done on the mainDiv class to append all the nested controls into the centre of the panel. It's not elegant, but it works.

I'm hoping the rest of the custom control will make sense to anyone who has created a custom control before. If you haven't and you get stuck, please let me know.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Cheml0ck
Web Developer
United Kingdom United Kingdom
Have been working with computers since the early 80's when our junior school got a BBC Micro. Every since then I've been building, fixing, configuring, installing, networking, coding and designing with them. At present I mainly code web applications in C#/ASP.NET. I'm very interested in Design Patterns and try and use these generic principles in all new projects to create truly n-tier architectures. You can keep up on current affairs on www.steelcurve.com


Ok so the pictures not of me, it's my little girl who makes coding everyday worth while. I just hope she becomes a lawyer not a developer.

Comments and Discussions

 
Generalsimpler render [modified] Pinmemberrich075321412-Nov-07 11:30 
GeneralHint! PinmemberDitrix25-Nov-06 2:24 
GeneralRe: Hint! PinmemberCheml0ck29-Nov-06 21:49 
Question.... Pinmemberchris1755-Nov-06 14:25 
AnswerRe: .... PinmemberCheml0ck29-Nov-06 21:47 
GeneralInteresting PinmemberNinjaCross31-Oct-06 6:20 
GeneralRe: Interesting PinmemberCheml0ck1-Nov-06 0:00 
QuestionSample app ? PinmemberJ.Marc.Piulachs31-Oct-06 4:58 
AnswerRe: Sample app ? PinmemberCheml0ck1-Nov-06 0:01 
AnswerSample app ? PinmemberNarender4uall15-Dec-06 20:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.141022.1 | Last Updated 31 Oct 2006
Article Copyright 2006 by Cheml0ck
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid