rabejens
rabejens

Reputation: 8132

How to create a UserControl with WinForms based on another UserControl?

I added some screenshots and steps to reproduce below.

My data model has a base class, let's call it CommonThing which has lots of properties. Then there are several specialized versions of this class with additional properties. I now want to create a UI to input data into this model.

I created a UserControl which contains controls for all the common properties like this:

internal partial class CommonThingControl : UserControl {

    public CommonThingControl() {
        InitializeComponent();
    }

    // Controller code
}

This also adds a CommonThingControl.Designer.cs which is populated by the GUI designer.

I now created a SpecialFooThingControl as a UserControl and changed the class header to read:

internal partial class SpecialFooThingControl : CommonThingControl {

    // implementation
}

When I now open the SpecialFooThingControl in the GUI designer, I see the controls of the CommonThingControl but they are all locked. I however have a TableLayoutPanel in the CommonThingControl I want to add stuff to, but I cannot change anything, and when I try to drag a control into the TableLayoutPanel the mouse cursor becomes a "No parking" sign and VS does not let me. This even happens when I set the accessor of the TableLayoutPanel to public.

I can move a control into the TableLayoutPanel via the document outline, but when I then rebuild the project, it disappears from the UI.

Is what I am trying to achieve not possible with the UI designer and do I have to set this up by hand or is there some additional step I forgot?

Here is what I did:

First, created a User Control, this is straightforward. I created a table layout panel which I set to Protected because I want to add to it. Common Control

This is the code for FooControl.cs:

namespace GuiTest {
    public partial class FooControl : UserControl {
        public FooControl() {
            InitializeComponent();
        }
    }
}

And FooControl.Designer.cs:

namespace GuiTest {
    partial class FooControl {
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing) {
            if (disposing && (components != null)) {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Component Designer generated code

        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent() {
            this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
            this.label1 = new System.Windows.Forms.Label();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.tableLayoutPanel1.SuspendLayout();
            this.SuspendLayout();
            // 
            // tableLayoutPanel1
            // 
            this.tableLayoutPanel1.ColumnCount = 2;
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
            this.tableLayoutPanel1.Controls.Add(this.textBox1, 1, 0);
            this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
            this.tableLayoutPanel1.Name = "tableLayoutPanel1";
            this.tableLayoutPanel1.RowCount = 1;
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
            this.tableLayoutPanel1.Size = new System.Drawing.Size(360, 28);
            this.tableLayoutPanel1.TabIndex = 0;
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(3, 7);
            this.label1.Margin = new System.Windows.Forms.Padding(3, 7, 3, 0);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(25, 13);
            this.label1.TabIndex = 0;
            this.label1.Text = "Foo";
            // 
            // textBox1
            // 
            this.textBox1.Dock = System.Windows.Forms.DockStyle.Top;
            this.textBox1.Location = new System.Drawing.Point(34, 3);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(323, 20);
            this.textBox1.TabIndex = 1;
            // 
            // FooControl
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Controls.Add(this.tableLayoutPanel1);
            this.Name = "FooControl";
            this.Size = new System.Drawing.Size(360, 28);
            this.tableLayoutPanel1.ResumeLayout(false);
            this.tableLayoutPanel1.PerformLayout();
            this.ResumeLayout(false);

        }

        #endregion

        protected System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.TextBox textBox1;
    }
}

Now, I added BarControl: Adding UserControl

Now I changed the extend from UserControl to FooControl in BarControl.cs:

namespace GuiTest {
    public partial class BarControl : FooControl {
        public BarControl() {
            InitializeComponent();
        }
    }
}

BarControl.Designer.cs:

namespace GuiTest {
    partial class BarControl {
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing) {
            if (disposing && (components != null)) {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Component Designer generated code

        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent() {
            components = new System.ComponentModel.Container();
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        }

        #endregion
    }
}

And this is how it turns out: Locked Layout Panel

Upvotes: 2

Views: 1864

Answers (2)

Ian
Ian

Reputation: 34489

The reason that the controls in CommonThingControl are locked is because the sub-class SpecialFooThingControl doesn't have access to them (they are privately scoped).

What you can do however in the designer for CommonThingControl is change the modifiers property on them, which will allow you to consume/modify them in your sub-class. You need to do this on each individual control, and can simply change the dropdown in the Properties window. You can probably change them in the commonthingcontrol.designer.cs file however I would also be wary on the basis that this file is auto-generated and changes can easily be over-written if you're not careful.

enter image description here

I'd recommend using something like Protected to ensure they're visible to the sub-controls but prevent them being abused outside of the inheritance chain.

EDIT

So further looking into this reveals that this is a limitation of the TableLayoutPanel. Taken from https://msdn.microsoft.com/en-us/library/ms171689.aspx:

Avoid Visual Inheritance The TableLayoutPanel control does not support visual inheritance in the Windows Forms Designer. A TableLayoutPanel control in a derived class appears as "locked" at design time.

Upvotes: 1

Panda
Panda

Reputation: 458

Due to controls accessibility, you cannot update them. To do so, go into your CommonThingControl.Designer.cs, change for exemple private Button MyButton1 to protected Button MyButton1

Upvotes: 0

Related Questions