michaeln
michaeln

Reputation: 3

.NET Rebuilding User Controls

I have a question regarding User Controls created with Visual Studio 2010.

Specifically, I have created a simple User Control extending the default Label .Net control. Using the Designer, I specified the BackColor of my User Control to "White" (note that my User Control does have an area of its own not occupied by any other control). After building the project, I used my control on a form of a second project under the same solution by dragging it from the toolbox.

After the placement of the control, I modified the very same property of my User Control and turned BackColor to "Red". I recompiled the entire solution (with project dependencies checked - user control project first and the dependent second project last). Subsequently, I created a second instance of my User Control on the form (by dragging it from the toolbox). Although this second instance appears with "Red" background as expected, the first instance persists with the "White" BackColor.

This is highly non-intuitive to me and I am wondering why? I expect ALL controls to adopt any changes made to the base class when the changes affect properties not overriden after their placement on the form. The only conclusion I managed to derive is that it has to do with VS generating the "designer.vb" code for the first instance of the control and NEVER regenerating it afterwards, even though the base user control has been altered and recompiled.

Any ideas ?

UPDATE: Thank you all for your answers! I would like to point out that setting the BackColor property was merely an example for illustrating my point. It appears to me that even if no properties at all are defined for my custom control during design time, VS generates a standard set of properties in the code behind( in addition to any explicit properties defined by the developer). Therefore, omitting the explicit specification of properties does not always tackle the problem (the default VS mechanism of setting properties in the designer.vb file). I believe the only way to address the issue once and for all, is to set property initialization code inside the control's New or Load method; not always a practical solution when a lot of initialization needs to be performed!

Upvotes: 0

Views: 2459

Answers (3)

Hans Passant
Hans Passant

Reputation: 942438

This goes wrong because you haven't told the designer what the default value for the property is. So when you drop your custom label on a form, the designer generates this kind of code in the form's Designer.cs file:

        this.myLabel1.AutoSize = true;
        this.myLabel1.BackColor = System.Drawing.Color.Red;         // <=== Here !
        this.myLabel1.Location = new System.Drawing.Point(19, 22);
        this.myLabel1.Name = "myLabel1";
        this.myLabel1.Size = new System.Drawing.Size(52, 13);
        this.myLabel1.TabIndex = 0;
        this.myLabel1.Text = "myLabel1";

The marked statement ruins it. When you now change the default in the control class, any control that you've already dropped on any form is not going to change to that default color, the designer code overrides it.

You'll need to use the [DefaultValue] attribute to tell the designer about your default. Like this:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

class MyLabel : Label {
    public MyLabel() {
        this.BackColor = Color.Red;
    }
    [DefaultValue(typeof(Color), "Red")]
    public override Color BackColor {
        get { return base.BackColor; }
        set { base.BackColor = value; }
    }
}

Now the designer will omit the assignment and any change you make inside the control will be effective. The attribute syntax is clumsy, unfortunately you need to use a string.

Upvotes: 2

Cody Gray
Cody Gray

Reputation: 244991

The only conclusion I managed to derive is that it has to do with VS generating the "designer.vb" code for the first instance of the control and NEVER regenerating it afterwards, even though the base user control has been altered and recompiled.

This is the correct conclusion. You specifically told the designer to make the BackColor of the first control white, and it obeyed by inserting code into the *.Designer.vb file that sets the BackColor property to white. It does this unconditionally so that it overrides whatever the default BackColor property of the control is.

When you change the default BackColor property of the control to red, this affects all new instances of the controls that are created, because they're still using the default property value. But it doesn't affect instances of the control for which you have explicitly overridden the value of that property.

As unintuitive as this behavior may seem to you, it's actually by design. If you want all instances of the control to inherit the default background color, then don't set the BackColor property for individual instances of it.

To force a particular instance of the control to use the default background color, even if you have set it explicitly, right-click on the property in the Properties Window and select "Reset". In this case, the background of the first instance of the custom control should turn red as it inherits the default value for that particular property.


In response to your update, the problem is that the various control classes set their own default values (by themselves overriding them from their parent class, Control). If you want to override those with new defaults for your custom control, you need to write code inside of the custom control class for that. You can't do it in the designer, because that affects only that particular instance of the custom control, not the entire class.

There are two things in particular you need to do. First, override the relevant property and set a default value using the DefaultValueAttribute. Second, set the default values for the properties in the class's constructor.

Here's an example of a custom control that inherits from the default Label control. I have versions of all the standard controls that basically just force the FlatStyle property to FlatStyle.System by default. The code is arbitrarily in VB.NET, but you should be able to convert it easily if C# is your preferred dialect.

Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Drawing

<ToolboxItem(True)> <ToolboxBitmap(GetType(ResFinder), "LabelEx.bmp")> _
<Description("A standard Windows label control for displaying static text and images.")> _
Public Class LabelEx : Inherits Label

    ''' <summary>
    ''' Gets or sets the flat style appearance of this label control.
    ''' </summary>
    <DefaultValue(GetType(System.Windows.Forms.FlatStyle), "System")> _
    Public Shadows Property FlatStyle As FlatStyle
       'Set the default flat style to System
       Get
          Return MyBase.FlatStyle
       End Get
       Set(ByVal value As FlatStyle)
          MyBase.FlatStyle = value
       End Set
    End Property

    ''' <summary>
    ''' Initializes a new instance of the LabelEx" class with default settings.
    ''' </summary>
    Public Sub New()
       MyBase.New()

       'Set default property values
       Me.FlatStyle = FlatStyle.System
    End Sub

End Class

The only major exceptions to this general rule is if you want to change the defaults for the size or padding of the control. In this case, you should just override the protected DefaultSize and DefaultPadding properties; i.e.

  ''' <summary>
  ''' Gets the default Size of a ButtonEx control.
  ''' </summary>
  Protected Overrides ReadOnly Property DefaultSize As Size
     Get
        Return New Size(88, 26)
     End Get
  End Property

  ''' <summary>
  ''' Gets the spacing, in pixels, around the image that is displayed on this button control.
  ''' </summary>
  Protected Overrides ReadOnly Property DefaultPadding As Padding
     Get
        Return New Padding(7, 0, 0, 0)
     End Get
  End Property

In this case, you should not set the default values for the public properties in the constructor.

For more clues on how to write custom controls (and some useful controls!), you might check out the Windows Forms Aero library.

Upvotes: 2

mathieu
mathieu

Reputation: 31202

You're correct, the backcolor is defined in your form designer generated code.

If you really want to disable setting back color while still inheriting from Label, you can override the BackColor property and remove it from the properties tab of the designer :

[EditorBrowsable(EditorBrowsableState.Never)]
[Browsable(false)]
public override Color BackColor
{
    get
    { 
        return Color.Blue;
    }
    set
    {
    }
}

Upvotes: 0

Related Questions