I wrote a custom collection editor for a WinForms control. Its core code looks like this:
internal class MyCollectionEditor : CollectionEditor
{
public MyCollectionEditor(Type type) : base(type) { }
protected override System.ComponentModel.Design.CollectionEditor.CollectionForm CreateCollectionForm()
{
System.ComponentModel.Design.CollectionEditor.CollectionForm myForm = base.CreateCollectionForm();
#region Adjust the property grid
PropertyGrid myPropGrid = GetPropertyGrid(myForm);
if (myPropGrid != null)
{
myPropGrid.CommandsVisibleIfAvailable = true;
myPropGrid.HelpVisible = true;
myPropGrid.PropertySort = PropertySort.CategorizedAlphabetical;
}
#endregion
return myForm;
}
}
I need to set a custom size and location for the collection editor form, but I could not find a way to do that. It seems the collection editor form is always positioned by VS to its default location. Is there a way to do what I need?
It respects to the StartPosition, DesktopLocation and Size which you set for the form:
public class MyCollectionEditor : CollectionEditor
{
public MyCollectionEditor() : base(typeof(Collection<Point>)) { }
protected override CollectionForm CreateCollectionForm()
{
var form = base.CreateCollectionForm();
// Other Settings
// ...
form.StartPosition = FormStartPosition.Manual;
form.Size = new Size(900, 600);
form.DesktopLocation = new Point(10, 10);
return form;
}
}
Then decorate your property this way:
[Editor(typeof(MyCollectionEditor), typeof(UITypeEditor))]
public Collection<Point> MyPoints { get; set; }
Related
I am creating a custom control in my C# application in order to add a new property (MyProperty below). It is inheriting from Label. One thing I would like it to do, is display at a particular size when I drag it on to my form (200x132). I'd also like it to display no text. However, no matter how I try to do this, it doesn't seem to work. I am able to set BackColor and BorderStyle with no problem, however. I'm fairly new to C#, so maybe I'm missing something obvious.
Here is my code:
using System.Drawing;
using System.Windows.Forms;
namespace MyProgram
{
public enum MyEnum
{
Value1, Value2, Value3
}
public partial class MyControl : Label
{
public MyControl()
{
BackColor = Color.LightCoral;
BorderStyle = BorderStyle.FixedSingle;
AutoSize = false;
Size = new Size(200, 132);
Text = "";
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
private MyEnum myProperty;
public MyEnum MyProperty
{
get { return myProperty; }
set { myPropery = value; }
}
}
}
The answer provided via Dispersia's link has a bug, in my opinion. The text reset should happen once and then whatever a user does after that shouldn't matter. In Dispersia's link you can't actually set the text back to the control name because it will keep blanking it out.
The answer provided by cramopy doesn't technically answer your question, it is a way to do it by using the defaults on a UserControl though. You'll also need to bind the Text property of the UserControl to the label's.
The following should work while inheriting from a Label and will only reset the Text property once.
public partial class MyControl : Label
{
#region fields
private IComponentChangeService _changeService;
private bool canResetText = false;
#endregion
#region properties
protected override Size DefaultSize
{
get { return new Size(200, 132); }
}
[Browsable(false)]
public override bool AutoSize
{
get { return false; }
set { base.AutoSize = false; }
}
public override ISite Site
{
get { return base.Site; }
set
{
base.Site = value;
if (!base.DesignMode)
return;
this._changeService = (IComponentChangeService)base.GetService(typeof(IComponentChangeService));
if (this._changeService != null)
this._changeService.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
#endregion
#region constructors
public MyControl()
{
base.BackColor = Color.LightCoral;
base.BorderStyle = BorderStyle.FixedSingle;
}
#endregion
#region methods
protected override void InitLayout()
{
base.InitLayout();
this.canResetText = true;
}
private void OnComponentChanged(object sender, ComponentChangedEventArgs ce)
{
if (ce.Component != null &&
ce.Component == this &&
ce.Member.Name == "Text" &&
base.DesignMode &&
this.canResetText)
{
((MyControl)ce.Component).Text = string.Empty;
this.canResetText = false;
if (this._changeService != null)
this._changeService.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
#endregion
}
#Dispersia reply only answers the myControl1 thing. (deleted meanwhile)
Here comes a full guide for solving your problem:
Add a new UserControl named MyLabel
Change the following within Designer Mode:
BorderStyle:= FixedSingle
Size:= 200; 132
Now Drag&Drop a new Label onto the control
Edit those Label values (also within Designer Mode):
AutoSize:= false
BackColor:= LightCoral
Dock:= Fill
Text:= clear/empty this box!! (don't write this inside the box, you really have to clear it!)
TextAlign:= MiddleCenter
Just recompile your project && add a MyLabel control from the Toolbar.
Now it show up as you wanted!!
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;
}
}
I'm using two classes as Toolbox derived from ItemsControl and ToolboxItem derived from ContnentControl
// Implements ItemsControl for ToolboxItems
public class Toolbox : ItemsControl
{
// Defines the ItemHeight and ItemWidth properties of
// the WrapPanel used for this Toolbox
public Size ItemSize
{
get { return itemSize; }
set { itemSize = value; }
}
private Size itemSize = new Size(50, 50);
// Creates or identifies the element that is used to display the given item.
protected override DependencyObject GetContainerForItemOverride()
{
return new ToolboxItem();
}
// Determines if the specified item is (or is eligible to be) its own container.
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is ToolboxItem);
}
}
// Represents a selectable item in the Toolbox/>.
public class ToolboxItem : ContentControl
{
// caches the start point of the drag operation
private Point? dragStartPoint = null;
static ToolboxItem()
{
// set the key to reference the style for this control
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
typeof(ToolboxItem), new FrameworkPropertyMetadata(typeof(ToolboxItem)));
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
base.OnPreviewMouseDown(e);
this.dragStartPoint = new Point?(e.GetPosition(this));
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.LeftButton != MouseButtonState.Pressed)
this.dragStartPoint = null;
if (this.dragStartPoint.HasValue)
{
// XamlWriter.Save() has limitations in exactly what is serialized,
// see SDK documentation; short term solution only;
string xamlString = XamlWriter.Save(this.Content);
DragObject dataObject = new DragObject();
dataObject.Xaml = xamlString;
WrapPanel panel = VisualTreeHelper.GetParent(this) as WrapPanel;
if (panel != null)
{
// desired size for DesignerCanvas is the stretched Toolbox item size
double scale = 1.3;
dataObject.DesiredSize = new Size(panel.ItemWidth * scale, panel.ItemHeight * scale);
}
DragDrop.DoDragDrop(this, dataObject, DragDropEffects.Copy);
e.Handled = true;
}
}
}
// Wraps info of the dragged object into a class
public class DragObject
{
// Xaml string that represents the serialized content
public String Xaml { get; set; }
// Defines width and height of the DesignerItem
// when this DragObject is dropped on the DesignerCanvas
public Size? DesiredSize { get; set; }
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
DesignerItem ni = new DesignerItem();
ni.AllowDrop = true;
ni.ToolTip = "tooltip goes here";
ni.Width = 100;
ni.Height = 200;
ni.Background = Brushes.Red;
//somehow code should introduce the object shape as object. in the sample on codeproject it is reading shape information from xaml but I need to add it from code behind.
Toolbox tb = new Toolbox();
tb.Items.Add(ni);
AddChild() method from ItemsControl must be the method to add a new object on a toolbox. However, when I instantiate an object from this class it does not give me this method. For simplicity I only want to add a rectangle shape on it this would allow me to to drag/drop it on canvas. So question is how I can add a Rectangle on toobox.
Any help is appreciated.
Thanks.
Amit
Solution:
var TheToolbar = ToolboxContainer.Content as Toolbox;
// Instantiate a ToolboxItem
ToolboxItem TheToolboxItem = new ToolboxItem();
Rectangle myRect = new Rectangle();
myRect.StrokeThickness = 1;
myRect.Stroke = some value for stroke;
myRect.Fill = some value for filling the object;
myRect.IsHitTestVisible = false;
//add to Toolbar
TheToolboxItem.Content= myRect;
TheToolbar.Items.Add(TheToolboxItem);
AddChild() is a protected method ( = accessible only from itself or from an inherited class). Try to use a code like this instead:
Toolbox tb = new Toolbox();
tb.Items.Add(myNewItem);
After you instantiate your objects, you need to add them as children to your canvas/parent element.
myParentElement.Children.Add(tb);
If you're doing it, include it in your example code.
Hey guys, I'm trying to set up a custom Panel called FormPanel from Panel
class FormPanel : Panel
{
bool previous;
FormPanel l;
public FormPanel()
{
previous = false;
l.Parent = this;
l.Dock = DockStyle.Fill;
}
}
This is pretty much where I am right now. I want the FormPanel to have a bool var and want to set it's default properties of Parent and Dock. How does this work? How can I set those?
In case you want your panel to have DockStyle.Fill as default for the Dock property, do this:
public class FormPanel : Panel
{
public FormPanel()
{
this.Dock = DockStyle.Fill;
}
[System.ComponentModel.DefaultValue(typeof(DockStyle), "Fill")]
public override DockStyle Dock
{
get { return base.Dock; }
set { base.Dock = value; }
}
}
This makes the Dock property default to Fill within the property window.
You shouldn't use an internal variable of your type, instead set the properties (that you inherit from the baseclass) directly:
class FormPanel : Panel
{
bool previous;
public FormPanel()
{
previous = false;
base.Parent = this;
base.Dock = DockStyle.Fill;
}
}
although I don't think that "base.Parent=this" will work ...
You need to add more info about what you're trying to achieve.
As it stands your FormPanel has a private field (l) which is itself a FormPanel:
FormPanel l;
You never instantiate this field, so it will always be null, and the assignments to properties in the constructor will fail with a NullReferenceException:
l.Parent = this;
l.Dock = DockStyle.Fill;
If you did instantiate this private field, you would have recursion, since your FormPanel contains a private FormPanel, which itself contains a private FormPanel, ...
l = new FormPanel();
l.Parent = this;
l.Dock = DockStyle.Fill;
You say you want to set a default Parent, but I don't see how a FormPanel can know what it's parent is in the constructor, unless you pass the parent as a parameter to the constructor, e.g. maybe you're looking for something like:
public FormPanel() : this(null)
{
}
public FormPanel(Control parent)
{
if (parent != null)
{
this.Parent = parent;
}
this.Dock = DockStyle.Fill;
...
}
I am writing an application which is going to allows users to change the properties of a text box or label and these controls are user controls. Would it be easiest to create a separate class for each user control which implements the properties I want them to be able to change and then bind those back to the user control? Or is there another method I am overlooking?
Create a custom Attribute, and tag the properties you want the user to edit with this attribute. Then set the BrowsableAttribute property on the property grid to a collection containing only your custom attribute:
public class MyForm : Form
{
private PropertyGrid _grid = new PropertyGrid();
public MyForm()
{
this._grid.BrowsableAttributes = new AttributeCollection(new UserEditableAttribute());
this._grid.SelectedObject = new MyControl();
}
}
public class UserEditableAttribute : Attribute
{
}
public class MyControl : UserControl
{
private Label _label = new Label();
private TextBox _textBox = new TextBox();
[UserEditable]
public string Label
{
get
{
return this._label.Text;
}
set
{
this._label.Text = value;
}
}
[UserEditable]
public string Value
{
get
{
return this._textBox.Text;
}
set
{
this._textBox.Text = value;
}
}
}