Control designer resetting a property to the wrong default value - c#

Since most of my container controls use a transparent background color, I created a simple base class for them:
public partial class BaseControl : UserControl {
public BaseControl() {
this.BackColor = Color.Transparent;
}
[DefaultValue(typeof(Color), "Transparent")]
public override Color BackColor {
get { return base.BackColor; }
set { base.BackColor = value; }
}
}
My problem is when either inheriting or containing one of them, the designer keeps adding a line in InitializeComponent():
control.BackColor = System.Drawing.SystemColors.Control;
When I right-click and choose Reset in the properties window, the property is reset to Transparent, but when I reopen the window the designer immediately sets it back to Control.
Is there another attribute I need to mention for the default to stick?

Related

How to hide inherited property?

I'm trying to make a class inherited from another class that I don't have access to.
public class MyLabel : Label
{
public MyLabel()
{
base.Text = "This text is fixed";
base.BackgroundColor = Color.Green;
}
}
The Text and Visible properties are still available when calling like this:
MyLabel lbl = new MyLabel();
lbl.Text = "Nope.";
lbl.BackgroundColor = Color.Red;
Is there a way to make these two last statements invalid?
You can hide the inherited properties using the new keyword and redefine them as readonly.
public class MyLabel : Label
{
new public string Text { get { return base.Text; } }
new public string BackColor { get { return base.BackColor; } }
public MyLabel()
{
base.Text = "This text is fixed";
base.BackColor= Color.Green;
}
}
Inheritance is inheritance. If your parents passed you the trait for blue eyes, the trait is in your genetic code. That doesn't mean you have blue eyes, though. While you inherit the trait, you might have brown eyes (dominant trait) and therefore you express that trait instead.
Code works similarly. If foo inherits from bar, every foo will have the traits of a bar. What you can do, however, is override the traits with traits unique to the class.
public override string Text
{
get { return "Nope"; }
set { return; /*or throw an exception or whatever you want to do*/ }
}
Now that I've show you how, don't do it if you can avoid it. If you're worried about inheriting a complex object like a Label and you don't want to expose some of what it inherits, your problem probably has nothing to do with with the modifiers on the properties, and everything to do with the scope modifier on your actual instance. You'd be better off using the object in a more narrow scope, then letting it fall out of scope before anything else would access it.
The reason you want to avoid this is code smell. Lets say you make a class library that uses your MyLabel. Because it inherits from Label, I know I can use it just like a label. Then, when I do this:
MyLabel myLanta = new MyLabel();
myLanta.Text = "Oh!";
...I will then proceed to spend an hour trying to find out why myLanta's text is always "Nope!" This is why it's important to throw an exception here, or at least use a summary so when another person is coding, they can see at a glance that no matter what they assign for "Text", it will always be "Nope".
My recommendation is that if you need to narrow the available properties of a class, make the class a component of a new class instead of inheriting it.
public class MyLabel
{
private System.Windows.Forms.Label label
= new System.Windows.Forms.Label { Text = "Nope", BackColor = Color.Green };
//a public accessor and setter
public Font Font { get { return label.Font; } set { label.Font = value; } }
//only this class can set the color, any class can read the color
public Color ForeColor { get { return label.ForeColor; } private set { label.ForeColor = value; } }
public AllMyStuffIWantToDo()....
//fill in your stuff here
}
Then, if you want to return properties of the Label, you can with methods and properties you control without having to worry about inheritance issues. If you don't provide an accessing method to the Label's property, that property never sees the light of day and is effectively private to the class. This also prevents broken code from someone passing your MyLabel in place of a Forms.Label because that inheritance contract will not exist.

Pass properties of parent control to children controls

I am developing a set of custom controls for a specific application. I want to define properties which is universal over the set of controls for appearance purposes, for argument's sake let's make it CustomCtrl.AccentColor
I want to define that same property for my Windows form i.e. Form1.AccentColor and when I change it, all the custom controls' AccentColor should change, exactly like when I change the ForeColor of my form, all labels' and buttons' etc ForeColor changes with it.
Is it at all possible to do this or do I have to settle for the effort of looping through all custom controls and changing it one-by-one?
Short Answer
Since you can have a common base class for all your controls as you mentioned in comments, as an option you can create a base class and then add some properties with behavior like ambient properties (like Font) to the base control class.
Detailed Answer
An ambient property is a property on a control that, if not set, is retrieved from the parent control.
In our implementation, we get the value from parent Form using FindForm method. So in the implementation, when getting the property value, we check if the value equals to default value and if the parent from has the same property, we return the property value of the parent form, otherwise we return the property value of the control itself.
After adding XXXX property, in this scenario we also should implement ShouldSerializeXXXX and ResetXXXX methods to let the designer when serialize the property and how to reset value when you right click on property and choose reset.
MyBaseControl
using System.Drawing;
using System.Windows.Forms;
public class MyBaseControl : Control
{
public MyBaseControl()
{
accentColor = Color.Empty;
}
private Color accentColor;
public Color AccentColor
{
get
{
if (accentColor == Color.Empty && ParentFormHasAccentColor())
return GetParentFormAccentColor();
return accentColor;
}
set
{
if (accentColor != value)
accentColor = value;
}
}
private bool ParentFormHasAccentColor()
{
return this.FindForm() != null &&
this.FindForm().GetType().GetProperty("AccentColor") != null;
}
private Color GetParentFormAccentColor()
{
if (ParentFormHasAccentColor())
return (Color)this.FindForm().GetType()
.GetProperty("AccentColor").GetValue(this.FindForm());
else
return Color.Red;
}
private bool ShouldSerializeAccentColor()
{
return this.AccentColor != GetParentFormAccentColor();
}
private void ResetAccentColor()
{
this.AccentColor = GetParentFormAccentColor();
}
}
MyBaseForm
public class BaseForm : Form
{
[DefaultValue("Red")]
public Color AccentColor { get; set; }
public BaseForm()
{
this.AccentColor = Color.Red;
}
}
Form1
public partial class Form1 : BaseForm
{
public Form1()
{
InitializeComponent();
}
}
i think you can create inherited class from Control class and define your common properties on there then inheriting your custom controls from that class and use parent property to access container (like Form) and get property value from it

UserControl and make Dock property ReadOnly - is this a correct way?

I have created a own control derived from UserControl and I wanted to make the Dock property a read only, and by trials & errors I came with something like this:
public partial class Header : UserControl
{
public Header()
{
InitializeComponent();
base.Dock = DockStyle.Top;
}
/// <summary>
/// Gets the DockStyle of the control
/// </summary>
[Browsable(false)]
[ReadOnly(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new DockStyle Dock
{
get
{
return base.Dock;
}
private set
{
base.Dock = DockStyle.Top;
}
}
}
is this a correct way to do this ? Simply: I want the control to always be docked to top (since it's a header :))
The private set you have is not well implemented, as it sets base.Dock to a hard-coded value instead of the given value. Either remove it completely or make it
private set
{
base.Dock = value;
}
Note, however, that users of your Header class could still cast it to UserControl and thus set the Dock property.
There is no 100% way to prevent this.
remove the set block and everything should work as desired:
public new DockStyle Dock
{
get
{
return base.Dock;
}
}

Create ToolTip for Custom UserControl

I need to understand how to utilize a ToolTip with a custom UserControl. Just creating the ToolTip on a form and assigning the specific control a ToolTip (via SetToolTip) obviously will not work.
What properties do I need to give the custom UserControl in order to assign ToolTip text to it? Do I need to add a ToolTip on the usercontrol form? How can I go about doing this?
Please provide a code sample or something for me to visualize.
Thank you!
Put a ToolTip on your UserControl (use the designer, just like you would put one on a form), and add a public property to your UserControl like:
public string TextBoxHint
{
get
{
return toolTip1.GetToolTip(textBox1);
}
set
{
toolTip1.SetToolTip(textBox1, value);
}
}
Create SetToolTip method in user control and set tooltip for each user control's subcontrol:
public partial class SomeUserControl : UserControl
{
public void SetToolTip(ToolTip toolTip)
{
string text = toolTip.GetToolTip(this);
toolTip.SetToolTip(subControl1, text);
toolTip.SetToolTip(subControl2, text);
// ...
}
}
Set tooltip text for user control instance in parent control designer. This adds in .designer file:
this.toolTip1.SetToolTip(this.someUserControl1, "Some text.");
Call SetToolTip method of user control instance with ToolTip parent control instance from parent control's constructor:
public partial class ParentForm : Form
{
public ParentForm()
{
InitializeComponent();
someUserControl1.SetToolTip(toolTip1);
}
}
This is the correct way to implement a serialized ToolTip property:
public partial class YourControlClass : UserControl
{
// Serialized property.
private ToolTip toolTip = new System.Windows.Forms.ToolTip();
// Public and designer access to the property.
public string ToolTip
{
get
{
return toolTip.GetToolTip(this);
}
set
{
toolTip.SetToolTip(this, value);
}
}

Add Form to a UserControl - is this possible?

Normally, controls are being added to forms. But I need to do an opposite thing - add a Form instance to container user control.
The reason behind this is that I need to embed a third-party application into my own. Converting the form to a user control is not feasible due to complexity.
This is possible by setting the form's TopLevel property to false. Which turns it into a child window, almost indistinguishable from a UserControl. Here's a sample user control with the required code:
public partial class UserControl1 : UserControl {
public UserControl1() {
InitializeComponent();
}
public void EmbedForm(Form frm) {
frm.TopLevel = false;
frm.FormBorderStyle = FormBorderStyle.None;
frm.Visible = true;
frm.Dock = DockStyle.Fill; // optional
this.Controls.Add(frm);
}
}

Categories