User Control as container at design time - c#

I'm designing a simple expander control.
I've derived from UserControl, drawn inner controls, built, run; all ok.
Since an inner Control is a Panel, I'd like to use it as container at design time. Indeed I've used the attributes:
[Designer(typeof(ExpanderControlDesigner))]
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
Great I say. But it isn't...
The result is that I can use it as container at design time but:
The added controls go back the inner controls already embedded in the user control
Even if I push to top a control added at design time, at runtime it is back again on controls embedded to the user control
I cannot restrict the container area at design time into a Panel area
What am I missing? Here is the code for completeness... why this snippet of code is not working?
[Designer(typeof(ExpanderControlDesigner))]
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
public partial class ExpanderControl : UserControl
{
public ExpanderControl()
{
InitializeComponent();
....
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
internal class ExpanderControlDesigner : ControlDesigner
{
private ExpanderControl MyControl;
public override void Initialize(IComponent component)
{
base.Initialize(component);
MyControl = (ExpanderControl)component;
// Hook up events
ISelectionService s = (ISelectionService)GetService(typeof(ISelectionService));
IComponentChangeService c = (IComponentChangeService)GetService(typeof(IComponentChangeService));
s.SelectionChanged += new EventHandler(OnSelectionChanged);
c.ComponentRemoving += new ComponentEventHandler(OnComponentRemoving);
}
private void OnSelectionChanged(object sender, System.EventArgs e)
{
}
private void OnComponentRemoving(object sender, ComponentEventArgs e)
{
}
protected override void Dispose(bool disposing)
{
ISelectionService s = (ISelectionService)GetService(typeof(ISelectionService));
IComponentChangeService c = (IComponentChangeService)GetService(typeof(IComponentChangeService));
// Unhook events
s.SelectionChanged -= new EventHandler(OnSelectionChanged);
c.ComponentRemoving -= new ComponentEventHandler(OnComponentRemoving);
base.Dispose(disposing);
}
public override System.ComponentModel.Design.DesignerVerbCollection Verbs
{
get
{
DesignerVerbCollection v = new DesignerVerbCollection();
v.Add(new DesignerVerb("&asd", new EventHandler(null)));
return v;
}
}
}
I've found many resources (Interaction, designed, limited area), but nothing was usefull for being operative...
Actually there is a trick, since System.Windows.Forms classes can be designed (as usual) and have a correct behavior at runtime (TabControl, for example).

ParentControlDesigner doesn't know what you want do. It only knows you want your UserControl to be a container.
What you need to do is implement your own designer which enables design mode on the panel:
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace MyCtrlLib
{
// specify my custom designer
[Designer(typeof(MyCtrlLib.UserControlDesigner))]
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
// define a property called "DropZone"
[Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Panel DropZone
{
get { return panel1; }
}
}
// my designer
public class UserControlDesigner : ParentControlDesigner
{
public override void Initialize(System.ComponentModel.IComponent component)
{
base.Initialize(component);
if (this.Control is UserControl1)
{
this.EnableDesignMode(
(UserControl1)this.Control).DropZone, "DropZone");
}
}
}
}
I learned this from Henry Minute on CodeProject. See the link for some improvements on the technique.

In addition to the answer above. It is mentioned in the comments, that the user is able to drag the WorkingArea. My fix for that is to include the WorkingArea panel in another panel, setting it to Dock.Fill. To disallow the user to change it back, I have created a class ContentPanel that overrides and hides the Dock property:
class ContentPanel : Panel
{
[Browsable(false)]
public override DockStyle Dock
{
get { return base.Dock; }
set { base.Dock = DockStyle.Fill; }
}
}
For me, this makes it sufficiently safe. We are only using the control internally, so we mainly want to prevent developers from accidently dragging things around. There are certainly ways to mess it up anyway.

To prevent the working area from being moved/resized in the designer you have to create a class for that working area that hides the Location, Height, Width, Size properties from the designer:
public class WorkingArea : Panel
{
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Point Location
{
get
{
return base.Location;
}
set
{
base.Location = value;
}
}
...
}

Related

C# Forms - Custom Button

I have a button with a particular style that I'm reusing within multiple user controls of my project. Whenever I want to change the visual style of this button, I must repeat the property changes everywhere it is being used, which is a pain.
I would like to "extend" the default Button control so that I can reuse it at design-time, within the designer, without having to change its style everywhere. I.e I would like to have my ExtendedButton pickable from the toolbox.
I would also like to be able to open this extended button in the designer so that I can change its design using the designer, causing it to be changed everywhere it is being used.
I tried to cdo the following, but the designer won't show anything and says "To add components to your class, drag them from the Toolbox..."
using System.ComponentModel;
using System.Windows.Forms;
namespace TestForm
{
[Designer(typeof(Button))]
public partial class ExtendedButton : Button
{
public ExtendedButton()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
}
}
How could I acheive this please ? Thank you.
Well I got it to work.
I have to remove the [Designer(typeof(Button))] that I added and call the base constructor that wasn't called by default when creating a new UserControl file.
public partial class ExtendedButton : Button
{
public ExtendedButton() : base()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
}
To make your stile global for all extended buttons you can use static variable:
class ExtendedButton : Button
{
public enum ExtendedStyle
{
Red,
Blue
}
private static ExtendedStyle style;
public ExtendedStyle Style
{
get { return style; }
set { style = value; }
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
if (style == ExtendedStyle.Red)
pe.Graphics.DrawRectangle(Pens.Red, 0, 0, Width - 1, Height - 1);
else
pe.Graphics.DrawRectangle(Pens.Blue, 0, 0, Width - 1, Height - 1);
}
}
If you change Style in designer all buttons will change their style

UserControl to behave like a panel in asp.net

I have several custom user controls (100+) that all have a common formatting involved which basically requires us to wrap the user control in a panel control on the destination page. For simplicity, I wanted to create a base control that handles this, as almost all of the base features are the same for each of the custom controls.
What I have done to accomplish this is to create a base class, inside that base class I create a private panel control, and then I override the Render to generate the panel pre/post tags around the base.Render.
Now this works great as all of the user controls that we care about that are inheriting this and the few formatting items that we have exposed to the inherited controls work as expected (Width, CssClass, etc).
What I would really like is to expose all of the panel control items to the inherited control through the base class, but without having to right a property/method to expose each element.
Any ideas on what the best approach is for this? I just don't want to implement each and every panel property/method manually. We use the design time attributes as well (number one is CssClass and Width) but we have been entending the user of design time attributes...
What we have works, just looking for a easier/better solution.
using System;
using System.Collections;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public class BaseUserControl : System.Web.UI.UserControl
{
private Panel _panel;
private bool _isPanelLoaded;
public Panel Panel
{
get
{
if (_panel == null)
{
_panel = new Panel();
_isPanelLoaded = true;
}
return _panel;
}
}
public BaseUserControl()
{
}
protected override void Render(HtmlTextWriter writer)
{
if (_isPanelLoaded)
{
Panel.RenderBeginTag(writer);
base.Render(writer);
Panel.RenderEndTag(writer);
}
else
{
base.Render(writer);
}
}
public Unit Width
{
get
{
if (Panel.Width.IsEmpty)
{
return 0;
}
return Panel.Width;
}
set
{
Panel.Width = value;
}
}
public string CssClass
{
get
{
return Panel.CssClass;
}
set
{
Panel.CssClass = value;
}
}
}
In order to expose all the properties, you have to inherit from the Panel control. Since you have a panel property, you could set the properties on that property. To define them in markup is the challenge... with most simple objects, if you give the property an attribute of <PersistenceMode(PersistenceMode.Attribute)>, you would be able to access the properties in the sytnax of Panel-CssClass, but I don't know if that will work for you, since Panel is a control. Good to try.
You're essentially talking about writing properties once that wrap the attributes you want, so it may be best just to do that, if the former solution doesn't work.

Visual Inheritance, Design-time support for extended custom controls

I'm extending my own custom control, which extends the UserControl class.
I can see all the elements fine in the extending class' designer, but all the properties of the extended custom control appear disabled and when selecting its element with the mouse a "locked" icon appears.
How can I fix that? I would like to be able to modify these properties from the designer.
EDIT: Definition of the custom control, which extends from UserControl.
namespace Wizard
{
[Designer(typeof(Wizard.StepDesigner))]
[DefaultProperty("TitlePanel, NavigationPanel")]
public partial class Step : UserControl
{
public Step()
{
InitializeComponent();
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Title TitlePanel
{
get
{
return this.title1;
}
set
{
this.title1 = value;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Panel ContentPanel
{
get
{
return this.contentPanel;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Navigation NavigationPanel
{
get
{
return this.navigation1;
}
}
}
internal class StepDesigner : ParentControlDesigner
{
public override void Initialize(System.ComponentModel.IComponent component)
{
base.Initialize(component);
if (this.Control is Step)
{
Step control = (Step)this.Control;
this.EnableDesignMode(control.TitlePanel, "TitlePanel");
this.EnableDesignMode(control.ContentPanel, "ContentPanel");
this.EnableDesignMode(control.NavigationPanel, "NavigationPanel");
}
}
}
}
As I already pointed out in the comments, the modifiers of the properties were all set to private and changing them to protected and rebuilding the solution fixed the "problem".

Popout a user control into a new window

I want to make it so that some of my user controls have the ability to 'pop out' into a new window. How I see it working is that the user control will remain where it currently it, but will send a copy of its current state into a new window. I also want this functionality to be in a base class so that derived classes will have this functionality.
Here is what I have so far:
public class PopoutControl : XtraUserControl
{
public void Popout()
{
XtraForm PopoutForm = new XtraForm();
PopoutForm.Controls.Add(this);
Dock = DockStyle.Fill;
PopoutForm.Show();
}
}
public partial class PopoutControlTest : PopoutControl
{
public PopoutControlTest()
{
InitializeComponent();
}
private void OnPopoutRequest(object sender, EventArgs e)
{
Popout();
}
}
This works except that it removes the user control from the original form where it is located - in order to place it on the new form - how can I solve this?
William
You should make a copy of the control instead of passing a reference, for example, by implementing some "Clone" method:
public class PopoutControl : XtraUserControl
{
public void Popout()
{
XtraForm PopoutForm = new XtraForm();
PopoutForm.Controls.Add(this.Clone());
Dock = DockStyle.Fill;
PopoutForm.Show();
}
public PopoutControl Clone()
{
var p = new PopoutControl();
// implement copying of the current state to p here
// ...
return p;
}
}
EDIT: For a general approach to clone or serialize Windows Forms controls, read this article:
http://www.codeproject.com/Articles/12976/How-to-Clone-Serialize-Copy-Paste-a-Windows-Forms
Your PopOut() has to be changed. Create a clone of the 'this'. Add the cloned object into the new form created. Implement the ICloneable interface in your PopOutControl class. Your clone() method has to be implemented such that it has the same state of your 'PopOutControl' object, ie 'this'.
public void Popout()
{
XtraForm PopoutForm = new XtraForm();
PopoutForm.Controls.Add(this.Clone());
Dock = DockStyle.Fill;
PopoutForm.Show();
}

How do I tell the designer that my custom winforms control has a fixed height?

I've made a custom control and overridden SetBoundsCore such that the height of the control is fixed. I'd like the designer to show the same sort of resize boxes as the NumericUpDown has - just one at each end - so that it's clear that the control has a fixed height. How do I tell the designer that my control has a fixed height?
You have to apply a Designer attribute to your UserControl:
[Designer(typeof(UCDesigner))]
public partial class UserControl1 : UserControl {
public UserControl1() {
InitializeComponent();
}
}
The UCDesigner class is defined as follow:
class UCDesigner : System.Windows.Forms.Design.ControlDesigner {
public override System.Windows.Forms.Design.SelectionRules SelectionRules {
get {
return (base.SelectionRules & ~(SelectionRules.BottomSizeable | SelectionRules.TopSizeable));
}
}
}
Note: You'll have to add a reference to the System.Design namespace.

Categories

Resources