I have a specific requirement to create a user control with specific common functions. To that control I also have the requirement to allow other developers to add controls in designer mode to make specific UI's. To do this I created a user control, adding (sample) label, and button. I also added a panel to allow adding of addition controls in a specific area of the control.
I then made the made the class visible in designer mode by adding [Designer] markup and a [ControlDesigner]. This gives the desired effect to add a User control with some fixed content, and add more controls to the page. The problem is that the panel can be moved by the user in design mode, and VisualStudio gets confused, creating a circular reference.. I must be missing something? Can I turn off the resizing/positioning of the panel, even though I need design mode enabled?
NOTE: I also tried to just use a user control in design mode, but added controls keep disappearing behind the fixed controls on the User Control.
Code and examples are below.. Any suggestion/fixes welcomed..
Above is the visual of the user control with the panel
Above is a form including the User control, and adding a custom button to the panel.. Note the panel drag is enable, if touched, a circular reference gets created in the form.designer.cs file, and the project becomes unstable.
Finally below is the class for User Control
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
using System.Windows.Forms.Design;
namespace wfcLib
{
[DesignerAttribute(typeof(MyControlDesigner))]
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
public partial class ucInput : UserControl
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Panel InternalPanel
{
get { return pnlContent; }
set { pnlContent = value; }
}
public ucInput()
{
InitializeComponent();
}
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
public class MyControlDesigner : System.Windows.Forms.Design.ControlDesigner
{
public override void Initialize(IComponent c)
{
base.Initialize(c);
ucInput ctl = (ucInput)c;
EnableDesignMode(ctl.InternalPanel, "InternalPanel");
}
}
}
In addition to my comment concerning using a derived Panel with its own designer that overrides the SelectionRules property, another method would be to tap into the designer's ISelectionService to detect a change in selected components and remove the panel if it was selected.
This is accomplished by overriding the control's Site property to set the hook. Also note that I changed the InternalPanel property to be read-only as you really do not want that writable.
[DesignerAttribute(typeof(MyControlDesigner))]
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
public partial class ucInput : UserControl
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Panel InternalPanel
{
get { return pnlContent; }
}
public ucInput()
{
InitializeComponent();
}
private ISelectionService selectionService;
private IDesignerHost host;
public override ISite Site
{
get
{
return base.Site;
}
set
{
host = null;
UnSubscribeFromSelectionService();
base.Site = value;
if (value != null)
{
host = (IDesignerHost)this.Site.GetService(typeof(IDesignerHost));
if (host != null)
{
if (host.Loading)
{
// defer subscription to selection service until fully loaded
host.Activated += Host_Activated;
}
else
{
SubscribeToSelectionService();
}
}
}
}
}
private void Host_Activated(object sender, EventArgs e)
{
host.Activated -= Host_Activated;
SubscribeToSelectionService();
}
private void SubscribeToSelectionService()
{
selectionService = (ISelectionService)this.Site.GetService(typeof(ISelectionService));
if (selectionService != null)
{
selectionService.SelectionChanging += OnSelectionChanging;
}
}
private void UnSubscribeFromSelectionService()
{
if (selectionService != null)
{
selectionService.SelectionChanging -= OnSelectionChanging;
}
}
private void OnSelectionChanging(object sender, EventArgs e)
{
if (selectionService.GetComponentSelected(pnlContent))
{
selectionService.SelectionChanging -= OnSelectionChanging;
selectionService.SetSelectedComponents(new[] { pnlContent }, SelectionTypes.Remove);
selectionService.SelectionChanging += OnSelectionChanging;
}
}
}
Edit: The original code neglected to account for SelectionService not being available while the IDesignerHost is loading. Added code to defer subscription until the IDesignerHost is activated.
Related
I am adding a checkbox to a comboBox in a windowsform. The checkbox add and I can select them as Items but I can see the text of the box to tick. In the combobox drop down the items are list empty and clicking on them specific the correct details when incepting the selected item.
How do I make them visable to see the box and name?
You need to create your own user control. First step is to create subclass the System.Windows.Forms.ComboBox class:
using System;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class CheckComboBox : ComboBox
{
public CheckComboBox()
{
this.DrawMode = DrawMode.OwnerDrawFixed;
}
}
}
You should set the DrawMode property to tell the ComboBox that we intend to render the drop-down list items ourselves. The next step was to define a class to contain our drop-down list item data and maintain the state. This is a simple class:
namespace WindowsFormsApp1
{
public class CheckComboBoxItem
{
public CheckComboBoxItem(string text, bool initialCheckState)
{
_checkState = initialCheckState;
_text = text;
}
private bool _checkState = false;
public bool CheckState
{
get { return _checkState; }
set { _checkState = value; }
}
private string _text = "";
public string Text
{
get { return _text; }
set { _text = value; }
}
public override string ToString()
{
return "Select Options";
}
}
}
After that go back to your CheckComboBox.cs and add delegates DrawItem and SelectedIndexChanged event.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
namespace WindowsFormsApp1
{
public partial class CheckComboBox : ComboBox
{
public event EventHandler CheckStateChanged;
public CheckComboBox()
{
this.DrawMode = DrawMode.OwnerDrawFixed;
this.DrawItem += new DrawItemEventHandler(CheckComboBox_DrawItem);
this.SelectedIndexChanged += new EventHandler(CheckComboBox_SelectedIndexChanged);
}
void CheckComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index == -1)
{
return;
}
if (!(Items[e.Index] is CheckComboBoxItem))
{
e.Graphics.DrawString(
Items[e.Index].ToString(),
this.Font,
Brushes.Black,
new Point(e.Bounds.X, e.Bounds.Y));
return;
}
CheckComboBoxItem box = (CheckComboBoxItem)Items[e.Index];
CheckBoxRenderer.RenderMatchingApplicationState = true;
CheckBoxRenderer.DrawCheckBox(
e.Graphics,
new Point(e.Bounds.X, e.Bounds.Y),
e.Bounds,
box.Text,
this.Font,
(e.State & DrawItemState.Focus) == 0,
box.CheckState ? CheckBoxState.CheckedNormal :
CheckBoxState.UncheckedNormal);
}
void CheckComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
CheckComboBoxItem item = (CheckComboBoxItem)SelectedItem;
item.CheckState = !item.CheckState;
CheckStateChanged?.Invoke(item, e);
}
}
}
In DrawItems delegate, the first thing we do is to verify that the item we are rendering was added as a CheckComboBoxItem. If it is not, we render it as a simple string. Otherwise, we get the appropriate CheckComboBoxItem from the Items collection (using the DrawItemEventArgs.Index property). Then we call the CheckBoxRenderer.DrawCheckBox() method, passing in the Graphics object, into which we want to render the CheckBox, and the location, size, text, font, focus and check states.
The second one allows us to toggle the check box in the drop-downs, but doesn't allow the user of this control to know that anything has happened. So we also add a public event to notify the control's users of a change to the check state of an item in the drop-down list:
public event EventHandler CheckStateChanged;
Finally, if you want to use this control, in the default Form1 of your application type this code:
using System;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
checkComboBox1.Items.Add(new CheckComboBoxItem("One", true));
checkComboBox1.Items.Add(new CheckComboBoxItem("Two", true));
checkComboBox1.Items.Add(new CheckComboBoxItem("Three", true));
this.checkComboBox1.CheckStateChanged += new EventHandler(this.checkComboBox1_CheckStateChanged);
}
private void checkComboBox1_CheckStateChanged(object sender, EventArgs e)
{
if (sender is CheckComboBoxItem)
{
CheckComboBoxItem item = (CheckComboBoxItem)sender;
}
}
}
}
You have so many links that can be useful to you. You do not put any code so we don't know what exactly you need...
https://www.codeproject.com/Articles/31105/A-ComboBox-with-a-CheckedListBox-as-a-Dropdown
https://www.codeproject.com/Articles/21085/CheckBox-ComboBox-Extending-the-ComboBox-Class-and
https://www.codeproject.com/Articles/18929/An-OwnerDraw-ComboBox-with-CheckBoxes-in-the-Drop
Thank you #Rob and #Mamun for correcting me.
See the picture above. This is a screenshot from Visual Studio's options form.
The left side is essentially a TreeView. The right side is various controls that change program options.
When nodes in the TreeView are selected, the right side changes, showing different options.
How do you program something like this? Does the right side just have 50 overlapping panels, and the selecting of nodes just changes which panel is visible? If this is the case, how would you go about managing such? It would be a mess in the designer.
No you don't make 50 overlapping panels. Just create several usercontrols and, for example, link the types on the tag of a node. You can use the Activator to create the controls.
Create 1 treeview and 1 panel: (PSEUDO CODE)
// create nodes:
TreeNode item = new TreeNode();
item.Tag = typeof(UserControl1);
TreeView.Nodes.Add( item );
// field currentControl
UserControl _currentControl;
// on selection:
TreeViewItem item = (TreeViewItem)sender;
if(_currentControl != null)
{
_currentControl.Controls.Remove(_currentControl);
_currentControl.Dispose();
}
// if no type is bound to the node, just leave the panel empty
if (item.Tag == null)
return;
_currentControl = (UserControl)Activator.Create((Type)item.Tag);
Panel1.Controls.Add(_currentControl);
The next question would be, "I'd like to call a save method, or RequestClose method in the controls". For this, you should implement an Interface on the controls, and when you switch nodes, just try to cast the _currentusercontrol to IRequestClose interface and call, for example, bool RequestClose(); method.
// on selection:
TreeViewItem item = (TreeViewItem)sender;
if(_currentControl != null)
{
// if the _currentControl supports the IRequestClose interface:
if(_currentControl is IRequestClose)
// cast the _currentControl to IRequestCode and call the RequestClose method.
if(!((IRequestClose)_currentControl).RequestClose())
// now the usercontrol decides whether the control is closed/disposed or not.
return;
_currentControl.Controls.Remove(_currentControl);
_currentControl.Dispose();
}
if (item.Tag == null)
return;
_currentControl = (UserControl)Activator.Create(item.Tag);
Panel1.Controls.Add(_currentControl);
But this will be the next step.
For me, the common design of that is, a classical treeview on the left side and a "content zone" on the right side. When the user pick something in the treeview you load the related view in the content zone. After there's a lot of different way to implement the stuff, for example automaticaly generate the treeview based on a list of object which contain the type of view to be instanciated and create a generic instantiator called when an item is picked to create the related view, anyway, the background is still the same. To resume, a treeview and just create the view in the content zone based on the selected item. (I've seen several screen like that in my work and most of the time it was like that)
My approach, after checking several options, was to inherit the TabControl component in such a way the pages of the control can be used as paged panels, and adding functionality so that the tabs do not show at run time. Then, by creating a property called Pages which depends on TabPages, I can refer to each page in a semantically correct way, giving the advantage of being able to manage every page as part of the Pages collection, and also hierarchically through the document explorer.
The code also hides design-time properties that pertain to a regular TabControl, but that would be irrelevant in a paged panel. Below is the code if anyone is interested.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
namespace MyCustomControls
{
public class PagedPanel : TabControl
{
//------------------------------------------------------------------------------------------------
public PagedPanel()
{
base.Multiline = true;
base.Appearance = TabAppearance.Buttons;
base.ItemSize = new Size(0, 1);
base.SizeMode = TabSizeMode.Fixed;
base.TabStop = false;
}
//------------------------------------------------------------------------------------------------
protected override void WndProc(ref Message m)
{
// Hide tabs by trapping the TCM_ADJUSTRECT message
if (m.Msg == 0x1328 && !DesignMode) m.Result = (IntPtr)1;
else base.WndProc(ref m);
}
//------------------------------------------------------------------------------------------------
protected override void OnKeyDown(KeyEventArgs ke)
{
// Block Ctrl+Tab and Ctrl+Shift+Tab hotkeys
if (ke.Control && ke.KeyCode == Keys.Tab)
return;
base.OnKeyDown(ke);
}
//------------------------------------------------------------------------------------------------
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[DefaultValue(true)]
public new bool Multiline
{
get { return base.Multiline; }
set { base.Multiline = value; Invalidate(); }
}
//------------------------------------------------------------------------------------------------
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)
, DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[DefaultValue(TabAppearance.Buttons)]
public new TabAppearance Appearance
{
get { return base.Appearance; }
set { base.Appearance = value; Invalidate(); }
}
//------------------------------------------------------------------------------------------------
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)
, DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[DefaultValue(typeof(Size), "0, 1")]
public new Size ItemSize
{
get { return base.ItemSize; }
set { base.ItemSize = value; Invalidate(); }
}
//------------------------------------------------------------------------------------------------
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)
, DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[DefaultValue(TabSizeMode.Fixed)]
public new TabSizeMode SizeMode
{
get { return base.SizeMode; }
set { base.SizeMode = value; Invalidate(); }
}
//------------------------------------------------------------------------------------------------
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)
, DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new TabPageCollection TabPages
{
get { return base.TabPages; }
}
//------------------------------------------------------------------------------------------------
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[DefaultValue(false)]
public new bool TabStop
{
get { return base.TabStop; }
set { base.TabStop = value; Invalidate(); }
}
//------------------------------------------------------------------------------------------------
public TabPageCollection Pages
{
get { return base.TabPages; }
}
//------------------------------------------------------------------------------------------------
}
}
The treeview would handle calling each tab either by key or index, a relatively trivial task. I do this by naming the nodes in my tree with a prefix such as "tvn", and then naming the pages in the PagedPanel the same but with prefix "pg". So on the AfterSelect event of the treeview, all I need is the name of the current node and I know what page to show.
I am writing a custom control derived from Component.
In this control I need to be able somehow to get OS messages WM_DEVICEDCHANGED to create some events from.
Usually I would just override WndProc directly in the applications Form, but it is really important that this functionallity lays directly in the control instead.
Even though the control will always be used on a Form it is important that the OS messages are received on the control that is derived from Component so when dropping the control on a form there is no need to add functionality for it manually IN the form.
I have seen some examples mentioning NativeWindow and other solutions, but I have not been able to find head or tail in any of it, so I hope someone here can help me out.
Thanks...
i want to receive the WM_DEVICECHANGED message
Okay, that just requires subclassing the window of the form on which you drop the component. Any top-level window gets that message. Add a new class to your project and paste the code shown below. Build. Drop the new component from the top of the toolbox onto a form. Add an event handler for the DeviceChange event and add any code that's relevant to the kind of device change notification that you are interested in. You could also put that code in the OnDeviceChange() method to further specialize the notification and raise more specific events. It is up to you to take it from here.
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms;
public class DeviceChangeNotifier : Component {
public delegate void DeviceChangeDelegate(Message msg);
public event DeviceChangeDelegate DeviceChange;
public DeviceChangeNotifier() {
// Add initialization here
}
public DeviceChangeNotifier(IContainer container) : this() {
// In case you need automatic disposal
container.Add(this);
}
public DeviceChangeNotifier(ContainerControl parentControl) : this() {
// In case you want to use it without the designer
this.ContainerControl = parentControl;
}
public ContainerControl ContainerControl {
// References the parent form
get { return this.parentControl; }
set {
this.parentControl = value;
this.parentControl.HandleCreated += parentControl_HandleCreated;
}
}
private void parentControl_HandleCreated(object sender, EventArgs e) {
// Subclass the form when its handle is created
snooper = new MessageSnooper(this, parentControl.Handle);
}
protected void OnDeviceChange(Message msg) {
// Raise the DeviceChange message
var handler = DeviceChange;
if (handler != null) handler(msg);
}
public override ISite Site {
// Runs at design time, ensures designer initializes ContainerControl
// so we'll have a reference to the parent form without it having to do any work
set {
base.Site = value;
if (value == null) return;
IDesignerHost service = value.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (service == null) return;
IComponent rootComponent = service.RootComponent;
this.ContainerControl = rootComponent as ContainerControl;
}
}
private ContainerControl parentControl;
private MessageSnooper snooper;
private const int WM_DESTROY = 0x0002;
private const int WM_DEVICECHANGE = 0x0219;
private class MessageSnooper : NativeWindow {
// Subclasses the parent window
public MessageSnooper(DeviceChangeNotifier owner, IntPtr handle) {
this.owner = owner;
this.AssignHandle(handle);
}
protected override void WndProc(ref Message m) {
if (m.Msg == WM_DESTROY) this.ReleaseHandle();
if (m.Msg == WM_DEVICECHANGE) owner.OnDeviceChange(m);
base.WndProc(ref m);
}
private DeviceChangeNotifier owner;
}
}
How can I disable the navigation shortcuts in a frame (for example the "Backspace" for navigation backward and "Alt+Right arrow" for navigation forward).
I want to use other keyboard functions, so I want to disable the navigation shortcuts of the frame.
Who can help me?
there is a more elegant solution where Attached behaviours can be used to disable navigation without actually extending a frame.
create an attached-behaviour :
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
namespace A
{
public static class DisableNavigation
{
public static bool GetDisable(DependencyObject o)
{
return (bool)o.GetValue(DisableProperty);
}
public static void SetDisable(DependencyObject o, bool value)
{
o.SetValue(DisableProperty, value);
}
public static readonly DependencyProperty DisableProperty =
DependencyProperty.RegisterAttached("Disable", typeof(bool), typeof(DisableNavigation),
new PropertyMetadata(false, DisableChanged));
public static void DisableChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var frame = (Frame)sender;
frame.Navigated += DontNavigate;
frame.NavigationUIVisibility = NavigationUIVisibility.Hidden;
}
public static void DontNavigate(object sender, NavigationEventArgs e)
{
((Frame)sender).NavigationService.RemoveBackEntry();
}
}
}
And in the xaml add this whenever you use a frame :
<Frame beha:DisableNavigation.Disable="True" />
and at the top of the xaml add the import :
xmlns:beha="clr-namespace:A"
See this answer for how to disable the keyboard shortcuts:
Disable backspace in wpf
That doesn't work for the back and forward navigation mouse buttons. To prevent that, it seems you need to put a handler on the Navigating event and cancel it if you don't want it.
For example, to totally disable forward navigation:
In .xaml:
<Frame Navigating="HandleNavigating" />
In code behind:
void HandleNavigating(Object sender, NavigatingCancelEventArgs e)
{
if (e.NavigationMode == NavigationMode.Forward)
{
e.Cancel = true;
}
}
What I do is host the content in ContentControl.
The real answer to disable all shortcuts in WPF Frame is:
foreach (var vNavigationCommand in new RoutedUICommand[]
{ NavigationCommands.BrowseBack,
NavigationCommands.BrowseForward,
NavigationCommands.BrowseHome,
NavigationCommands.BrowseStop,
NavigationCommands.Refresh,
NavigationCommands.Favorites,
NavigationCommands.Search,
NavigationCommands.IncreaseZoom,
NavigationCommands.DecreaseZoom,
NavigationCommands.Zoom,
NavigationCommands.NextPage,
NavigationCommands.PreviousPage,
NavigationCommands.FirstPage,
NavigationCommands.LastPage,
NavigationCommands.GoToPage,
NavigationCommands.NavigateJournal })
{
ctlFrame.CommandBindings.Add(new CommandBinding(vNavigationCommand, (sender, args) => { }));
}
The frame it's self provides no method of disabling navigation. It only provides a means to hide the navigation controls. You can however inherit from the Frame class and make some modifications to it yourself. The following example removes the last page from the BackStack every time the page navigates. Thus ensuring the frame can never navigate backwards as it does not know which page was last.
class NoNavFrame : Frame
{
public NoNavFrame()
{
this.Navigated += new System.Windows.Navigation.NavigatedEventHandler(NoNavFrame_Navigated);
}
void NoNavFrame_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
this.NavigationService.RemoveBackEntry();
}
}
Then you can include this in XAML as follows...
<myControls:NoNavFrame x:Name="myFrame" NavigationUIVisibility="Hidden" />
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.
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.
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
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);
}