UserControl extending ScrollableControl - disable container functinality - c#

I'm building custom control by extending ScrollableControl.
Problem is that my custom control acts as container - I can drag controls into it:
My question is how can I disable container functionality in class that extends ScrollableControl
Below are two test controls, one extends Control, second ScrollableControl
public class ControlBasedControl : Control
{
protected override Size DefaultSize
{
get { return new Size(100, 100); }
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(Brushes.LightCoral, ClientRectangle);
}
}
public class ScrollableControlBasedControl : ScrollableControl
{
public ScrollableControlBasedControl()
{
AutoScrollMinSize = new Size(200, 200);
}
protected override Size DefaultSize
{
get { return new Size(100, 100); }
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(Brushes.LawnGreen, ClientRectangle);
}
}

You get "acts-like-a-container" behavior at design time from the [Designer] attribute. Copy-pasting from the Reference Source:
[
ComVisible(true),
ClassInterface(ClassInterfaceType.AutoDispatch),
Designer("System.Windows.Forms.Design.ScrollableControlDesigner, " + AssemblyRef.SystemDesign)
]
public class ScrollableControl : Control, IArrangedElement {
// etc...
}
It is ScrollableControlDesigner that gets the job done. Doesn't do much by itself, but derived from ParentControlDesigner, the designer that permits a control to act as a parent for child controls and gives it container-like behavior at design time.
Fix is easy, you just have to use your own [Designer] attribute to select another designer. Add a reference to System.Design and make it look like this:
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms.Design; // Add reference to System.Design
[Designer(typeof(ControlDesigner))]
public class ScrollableControlBasedControl : ScrollableControl {
// etc...
}

There is probably more than one way to accomplish this, but here is what I would do...
First create a read-only version of ControlCollection
public class ReadOnlyControlCollection : Control.ControlCollection
{
public ReadOnlyControlCollection(Control owner)
: base(owner)
{
}
public override bool IsReadOnly
{
get { return true; }
}
public override void Add(Control control)
{
throw new ArgumentException("control");
}
}
Then make your ScrollableControlBasedControl create an instance of ReadOnlyControlCollection in stead of the default ControlCollection
public class ScrollableControlBasedControl : ScrollableControl
{
protected override Control.ControlCollection CreateControlsInstance()
{
return new ReadOnlyControlCollection(this);
}
// The rest of your class goes here...
}
I use Visual Studio 2010 and when I drop a control on an ScrollableControlBasedControl the control is magically moved back to where it came from, as if the action was cancelled.

Related

How to create TextBoxes in a GroupBox using the Collection Editor window?

I would like to create a customized control that inherits from GroupBox and have a property that is a collection of TextBox. I intent to create in Designer as many TextBoxes as I want, similarly what could be done with TabControl, which one could create pages in TabPages properties through the Collection Editor window.
I created the property TextBoxList that appears in properties window and when I click in the “…” Collection Editor window opens to create the TextBox and set its properties, but when I click the ok button, none TextBox is added to my GroupBox. The TextBox instances were created, but were not added to the GroupBox. Does somebody could help me with the TextBox addition to the GroupBox? Follows the code.
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Forms;
namespace CustomizedControl
{
public partial class GroupBoxWithTextBox : GroupBox
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor(typeof(System.ComponentModel.Design.CollectionEditor),
typeof(System.Drawing.Design.UITypeEditor))]
[Description("The TextBoxes in GroupBox control."), Category("Behavior")]
public Collection<TextBox> TextBoxList
{
get
{
return _textBoxList;
}
}
private Collection<TextBox> _textBoxList = new Collection<TextBox>();
public GroupBoxWithTextBox()
{
InitializeComponent();
}
}
}
Based on my research, we need to override the class CollectionEditor if we want to add the textbox in design.
I write the following code and you can have a look.
Code:
[Serializable]
public partial class GroupBoxWithTextBox : GroupBox
{
public GroupBoxWithTextBox()
{
SetStyle(ControlStyles.DoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
Padding = new Padding(0);
textBox = new TextBox();
textBox.Size = new System.Drawing.Size(60, 30);
Controls1.Add(textBox);
textBox.Dock = DockStyle.Top;
}
[EditorAttribute(typeof(NewCollectionEditor), typeof(System.Drawing.Design.UITypeEditor))]
public ControlCollection Controls1
{
get
{
return base.Controls;
}
set
{
}
}
private TextBox textBox;
}
public partial class NewCollectionEditor : CollectionEditor
{
public NewCollectionEditor(Type t) : base(t)
{
}
// *** Magic happens here: ***
// override the base class to SPECIFY the Type allowed
// rather than letting it derive the Types from the collection type
// which would allow any control to be added
protected override Type[] CreateNewItemTypes()
{
Type[] ValidTypes = new[] { typeof(TextBox) };
return ValidTypes;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
return base.EditValue(context, provider, value);
}
}
Result:(You need to set the textbox location manually)
Hope this could help you.

UserControl with header and content - Allow dropping controls in content panel and Prevent dropping controls in header at design time

I wrote User Control (yay!). But I want it to behave as a container. But wait! I know about
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design",
typeof(IDesigner))]
Trick.
The problem is - I don't want all of my control to behave like container, but only one part. One - de facto - panel ;)
To give wider context: I wrote a control that has Grid, some common buttons, labels and functionalities. But it also has a part where the user is supposed to drop his custom buttons/controls whatever. Only in this particular part of the control, nowhere else.
Anyone had any idea?
You should do the following :
For your user control, you need to create a new designer which enables the inner panel on design-time by calling EnableDesignMode method.
For the inner panel, you need to create a designer which disables moving, resizing and removes some properties from designer.
You should register the designers.
Example
You can read a blog post about this topic here and clone or download a working example:
r-aghaei/ChildContainerControlDesignerSample
Download Zip
Code
Here is the code for different elements of the solution.
Your user control
[Designer(typeof(MyUserControlDesigner))]
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
TypeDescriptor.AddAttributes(this.panel1,
new DesignerAttribute(typeof(MyPanelDesigner)));
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Panel ContentsPanel
{
get { return panel1; }
}
}
Designer for the inner panel
public class MyPanelDesigner : ParentControlDesigner
{
public override SelectionRules SelectionRules
{
get
{
SelectionRules selectionRules = base.SelectionRules;
selectionRules &= ~SelectionRules.AllSizeable;
return selectionRules;
}
}
protected override void PostFilterAttributes(IDictionary attributes)
{
base.PostFilterAttributes(attributes);
attributes[typeof(DockingAttribute)] =
new DockingAttribute(DockingBehavior.Never);
}
protected override void PostFilterProperties(IDictionary properties)
{
base.PostFilterProperties(properties);
var propertiesToRemove = new string[] {
"Dock", "Anchor", "Size", "Location", "Width", "Height",
"MinimumSize", "MaximumSize", "AutoSize", "AutoSizeMode",
"Visible", "Enabled",
};
foreach (var item in propertiesToRemove)
{
if (properties.Contains(item))
properties[item] = TypeDescriptor.CreateProperty(this.Component.GetType(),
(PropertyDescriptor)properties[item],
new BrowsableAttribute(false));
}
}
}
Designer for your user control
public class MyUserControlDesigner : ParentControlDesigner
{
public override void Initialize(IComponent component)
{
base.Initialize(component);
var contentsPanel = ((MyUserControl)this.Control).ContentsPanel;
this.EnableDesignMode(contentsPanel, "ContentsPanel");
}
public override bool CanParent(Control control)
{
return false;
}
protected override void OnDragOver(DragEventArgs de)
{
de.Effect = DragDropEffects.None;
}
protected override IComponent[] CreateToolCore(ToolboxItem tool, int x,
int y, int width, int height, bool hasLocation, bool hasSize)
{
return null;
}
}

ControlCollection items could not be saved

I have created a control with controlcollection. When I add items from the property window at design time. It added perfectly. Also when I open it back. Added items shows me. But, When I close the form then open it again the items was removed.
Now I have added two Items in the collection.
The items was looking perfectly.
But, When I open the Form.Desigern.cs file the following line is missing.
this.xWizardControl.Window.Controls.Add(this.xWizardPage1);
this.xWizardControl.Window.Controls.Add(this.xWizardPage2);
The code is looks like this.
public class XWizardPageWindow : DevExpress.XtraEditors.XtraUserControl, ISupportInitialize
{
private XWizardPageCollection _pages;
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public XWizardPageCollection Pages
{
get { return _pages; }
set { _pages = value; }
}
public XWizardPageWindow()
{
}
#region Override Methods
protected override ControlCollection CreateControlsInstance()
{
if (_pages == null)
_pages = new XWizardPageCollection(this);
return _pages;
}
#endregion
#region ISupportInitialize Members
public void BeginInit()
{
//DO NOTHING
}
public void EndInit()
{
//DO NOTHING
}
#endregion
}
ControlCollection Class
public class XWizardPageCollection : System.Windows.Forms.Control.ControlCollection
{
public delegate void XWizardPageEventHandler(object sender, XWizardPageEventArgs e);
List<XWizardPage> _pages = new List<XWizardPage>();
#region Constructor
public XWizardPageCollection(System.Windows.Forms.Control owner): base(owner)
{}
#endregion
#region Override Methods
public override void Add(System.Windows.Forms.Control value)
{
base.Add(value);
value.Dock = System.Windows.Forms.DockStyle.Fill;
((XWizardPage)value).BorderStyle = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder;
}
#endregion
#region Destructor
~XWizardPageCollection()
{
GC.SuppressFinalize(this);
}
#endregion
}
First, one should never change the ControlCollection once created and returned by CreateControlsInstance. So the Pages property should be defined as ReadOnly.
Secondly, when using Visible you're telling the code generator to create a new instance of Pages, which we don't want. So change the DesignerSerializationVisibilityAttribute from Visible to Content and the code generator will produce code for the contents of the object (Pages), rather than for the object itself.

Prevent adding controls from toolbox to a UserControl at Design Time [duplicate]

I wrote User Control (yay!). But I want it to behave as a container. But wait! I know about
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design",
typeof(IDesigner))]
Trick.
The problem is - I don't want all of my control to behave like container, but only one part. One - de facto - panel ;)
To give wider context: I wrote a control that has Grid, some common buttons, labels and functionalities. But it also has a part where the user is supposed to drop his custom buttons/controls whatever. Only in this particular part of the control, nowhere else.
Anyone had any idea?
You should do the following :
For your user control, you need to create a new designer which enables the inner panel on design-time by calling EnableDesignMode method.
For the inner panel, you need to create a designer which disables moving, resizing and removes some properties from designer.
You should register the designers.
Example
You can read a blog post about this topic here and clone or download a working example:
r-aghaei/ChildContainerControlDesignerSample
Download Zip
Code
Here is the code for different elements of the solution.
Your user control
[Designer(typeof(MyUserControlDesigner))]
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
TypeDescriptor.AddAttributes(this.panel1,
new DesignerAttribute(typeof(MyPanelDesigner)));
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Panel ContentsPanel
{
get { return panel1; }
}
}
Designer for the inner panel
public class MyPanelDesigner : ParentControlDesigner
{
public override SelectionRules SelectionRules
{
get
{
SelectionRules selectionRules = base.SelectionRules;
selectionRules &= ~SelectionRules.AllSizeable;
return selectionRules;
}
}
protected override void PostFilterAttributes(IDictionary attributes)
{
base.PostFilterAttributes(attributes);
attributes[typeof(DockingAttribute)] =
new DockingAttribute(DockingBehavior.Never);
}
protected override void PostFilterProperties(IDictionary properties)
{
base.PostFilterProperties(properties);
var propertiesToRemove = new string[] {
"Dock", "Anchor", "Size", "Location", "Width", "Height",
"MinimumSize", "MaximumSize", "AutoSize", "AutoSizeMode",
"Visible", "Enabled",
};
foreach (var item in propertiesToRemove)
{
if (properties.Contains(item))
properties[item] = TypeDescriptor.CreateProperty(this.Component.GetType(),
(PropertyDescriptor)properties[item],
new BrowsableAttribute(false));
}
}
}
Designer for your user control
public class MyUserControlDesigner : ParentControlDesigner
{
public override void Initialize(IComponent component)
{
base.Initialize(component);
var contentsPanel = ((MyUserControl)this.Control).ContentsPanel;
this.EnableDesignMode(contentsPanel, "ContentsPanel");
}
public override bool CanParent(Control control)
{
return false;
}
protected override void OnDragOver(DragEventArgs de)
{
de.Effect = DragDropEffects.None;
}
protected override IComponent[] CreateToolCore(ToolboxItem tool, int x,
int y, int width, int height, bool hasLocation, bool hasSize)
{
return null;
}
}

implementing IPostbackHandler .net

I have a server control, inheriting from PlaceHolder.
Basically, all it is, is a placeholder, with the top part having a "<div class etc...", and the bottom closing it off.
So, typical usage would be
<control:control runat="server" id="phControl">
<asp:TextBox runat="server" id="txtControl">
<asp:DropDownList runat="server"id="ddlControl">
</control:control>
or something similar.
It has struck me that if I postback to the control, it loses all the items in the ddlControl (or whatever), and that implementing IPostBackHandler apparently would solve all my woes.
I had a quick glance through the documentation, but am still not really sure what I am implementing (obviously I have the method names, but I don't really get what is expected in here)
Any pointers in the right direction would be greatly appreciated.
Thanks,
Tim
Its looks like you just want a server control that can contains other controls or a "template", I have just done this using the example at: http://msdn.microsoft.com/en-us/library/ms178657.aspx
This should handle all the work done on postback.
A basic example adapted from the above link:
using System;
using System.ComponentModel;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.WebControls;
namespace Made4Print.Web.UI
{
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal), Designer(typeof(VacationHomeDesigner)), DefaultProperty("Title"), ToolboxData("<{0}:TemplateContainer runat=\"server\"> "),]
public class TemplateContainer : CompositeControl
{
private ITemplate templateValue;
private TemplateOwner ownerValue;
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public TemplateOwner Owner
{
get
{
return ownerValue;
}
}
[Browsable(false), PersistenceMode(PersistenceMode.InnerProperty), DefaultValue(typeof(ITemplate), ""), Description("Control template"), TemplateContainer(typeof(TemplateContainer))]
public virtual ITemplate Template
{
get
{
return templateValue;
}
set
{
templateValue = value;
}
}
protected override void CreateChildControls()
{
Controls.Clear();
ownerValue = new TemplateOwner();
ITemplate temp = templateValue;
if (temp == null)
{
temp = new DefaultTemplate();
}
temp.InstantiateIn(ownerValue);
this.Controls.Add(ownerValue);
}
public override void DataBind()
{
CreateChildControls();
ChildControlsCreated = true;
base.DataBind();
}
}
[ToolboxItem(false)]
public class TemplateOwner : WebControl
{
}
#region DefaultTemplate
sealed class DefaultTemplate : ITemplate
{
void ITemplate.InstantiateIn(Control owner)
{
// Create Controls Here
//Label title = new Label();
//title.DataBinding += new EventHandler(title_DataBinding);
//owner.Controls.Add(title);
}
//void title_DataBinding(object sender, EventArgs e)
//{
// Label source = (Label)sender;
// TemplateContainer container = (TemplateContainer)(source.NamingContainer);
// source.Text = container.Title;
//}
}
#endregion
public class VacationHomeDesigner : ControlDesigner
{
public override void Initialize(IComponent Component)
{
base.Initialize(Component);
SetViewFlags(ViewFlags.TemplateEditing, true);
}
public override string GetDesignTimeHtml()
{
return "<span>[Template Container Control]</span>";
}
public override TemplateGroupCollection TemplateGroups
{
get
{
TemplateGroupCollection collection = new TemplateGroupCollection();
TemplateGroup group;
TemplateDefinition template;
TemplateContainer control;
control = (TemplateContainer)Component;
group = new TemplateGroup("Item");
template = new TemplateDefinition(this, "Template", control, "Template", true);
group.AddTemplateDefinition(template);
collection.Add(group);
return collection;
}
}
}
}

Categories