views:

73

answers:

3

A Server based control is not good solution for me, since my panel should by default always contain a asp checkbox which will allow the user to hide and show the panels content.

I created my Panel as a templated user control but now I have the problem that I cannot declare variables in it.

    [ParseChildren(true)]
    public partial class MyPanel: System.Web.UI.UserControl
    {

        private ITemplate messageTemplate = null;

        [TemplateContainer(typeof(MessageContainer))]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public ITemplate Content
        {
            get
            {
                return messageTemplate;
            }
            set
            {
                messageTemplate = value;
            }
        }

        void Page_Init()
        {
            MessageContainer container = new MessageContainer();
            messageTemplate.InstantiateIn(container);
            PlaceHolder1.Controls.Add(container);
        }

        [ParseChildren(true)]
        public class MessageContainer : Control, INamingContainer
        {

            internal MessageContainer()
            {
            }

        }
}

If I do the following in MyPage.aspx then the control definitions are not inserted into MyPage.aspx.designer.cs a they do normally:

<my:MyPanel>
  <Content>
    <asp:TextBox id = "foo" runat="server" />
  </Content>
</my:MyPanel>

Therefore "foo" is not created as control variable by the designer, so I have no access to it.

How can I create my own Panel which allows declaration of controls in it?

EDIT: I now tried with [ParseChildren(false)]. Variables for contained variables are now generated in the designer code of the form. The problem is now that messageTemplate.InstantiateIn(container) throws an exception.

A: 

You haven't given code for the control. In general, it needs to implement INamingContainer and should have properties of type ITemplate to accept templates. Check on MSDN on how to develop one. And here's the sample code from MSDN. Also check this article for data bound templated control.

VinayC
Thats what I did.
codymanix
Ok but then controls within template would never go to designer page because the template instance gets generated at runtime and actual controls ids will be different. You have to use FindControl to get reference to the control. See answer by Citronas.
VinayC
No it is definitely possible to generate variables using the ParseChildren attribute. But this generates runtime errors for me (see my edit)
codymanix
A: 

First of all, you need to use the runat="server" attribute.

<asp:TextBox id = "foo"  runat="server"/>

Afterwards you can try

var textbox = this.MyCustomPanel.FindControl("foo") as TextBox;

Instead of using FindControl I guess it is possible to achieve this behaviour by setting an attribute on the designer settings of the INamingTemplate Container of your Usercontrol

citronas
sorry, forgot the runat tag in my example, I included in in my original source and it didn't work.
codymanix
It is definitely possible to generate variables using the [ParseChildren(false)] attribute. But this generates runtime errors for me (see edit)
codymanix
A: 

You don't need to create a templated control, just create a Composite Web Control. Create a Panel & Checkbox, add them to the control collection of the composite control, adjust the rendering to display it as you want, and run with it.

look here

* EDIT **

Here is a working implementation of what you need. Make to create a reference for the Web.dll.

CustomPanel.cs

using System;
using System.ComponentModel;
using System.Drawing;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;


namespace Web
{
    [    AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal), 
         AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal),
         ToolboxData("<{0}:CustomPanel runat=\"server\"> </{0}:CustomPanel>"),
]

    public class CustomPanel : CompositeControl
{
        private Panel panelContainer;
        private CheckBox chkHideContent;
        private Panel panelInnerContainer;

        [Bindable(true),
        Category("Appearance"),
        DefaultValue(""),
        Description("The text to display with the checkbox.")]
        public string CheckBoxText
        {
            get
            {
                EnsureChildControls();
                return chkHideContent.Text;
            }
            set
            {
                EnsureChildControls();
                chkHideContent.Text = value;
            }
        }

        [Bindable(true)]
        [Category("Data")]
        [DefaultValue("")]
        [Localizable(true)]
        public bool IsCheckBoxChecked
        {
            get
            {
                return chkHideContent.Checked;
            }
        }

        [Bindable(true)]
        [Category("Data")]
        [DefaultValue("")]
        [Localizable(true)]
        public bool HideInnerPanel
        {
            set
            {
                EnsureChildControls();
                panelInnerContainer.Visible = value;
            }
        }

        [Bindable(true)]
        [Category("Data")]
        [DefaultValue("")]
        [Localizable(true)]
        public ControlCollection InnerPanelControls
        {
            get
            {
                EnsureChildControls();
                return panelInnerContainer.Controls;
            }
        }

        protected virtual void OnCheckboxChanged(EventArgs e)
        {
            if (chkHideContent.Checked)
            {
                panelInnerContainer.Visible = false;
            }
            else
            {
                panelInnerContainer.Visible = true;
            }
        }

        private void _checkbox_checkChanged(object sender, EventArgs e)
        {
            OnCheckboxChanged(EventArgs.Empty);
        }

        protected override void RecreateChildControls()
        {
            EnsureChildControls();
        }

        protected override void CreateChildControls()
        {
            Controls.Clear();

            panelContainer = new Panel();
            panelContainer.ID = "panelContainer";

            chkHideContent = new CheckBox();
            chkHideContent.ID = "chkHideContent";
            chkHideContent.CheckedChanged += new EventHandler(_checkbox_checkChanged);
            chkHideContent.AutoPostBack = true;

            panelInnerContainer = new Panel();
            panelInnerContainer.ID = "panelInnerContainer";

            this.Controls.Add(panelContainer);
            this.Controls.Add(chkHideContent);
            this.Controls.Add(panelInnerContainer);
        }

        protected override void Render(HtmlTextWriter writer)
        {
            panelContainer.RenderBeginTag(writer);
            chkHideContent.RenderControl(writer);
            panelInnerContainer.RenderControl(writer);
            panelContainer.RenderEndTag(writer);
        }
    }
}

Default.aspx

<%@ Register assembly="Web" namespace="Web" tagprefix="cc1" %>
<cc1:CustomPanel ID="CustomPanel1" runat="server" />

Default.aspx.cs

 protected void Page_Load(object sender, EventArgs e)
 {
        Label lbl = new Label();
        lbl.Text = "IT WORKS!";

        CustomPanel1.CheckBoxText = "Hide my innards!";
        CustomPanel1.InnerPanelControls.Add(lbl);
 }
TheGeekYouNeed
As I understand this sample, you cannot add child controls into a Composite Web Control other than the controls which are defined in the web control itself. So you cannot use it as a Container control.
codymanix
You can use it as a container control. posting code after this.
TheGeekYouNeed