To accomplish this I created an abstract control that takes a generic parameter of type Control and has a default constructor. You might notice that in my override Font I check whether the control is in design time or run time. If in design time, we need to create a new Font object for the setter using the values passed. If we directly use the value passed the designer will crash, and at times Visual Studio will crash.
Here's how it can look like:
And here's the code for the base control:
[EditorBrowsable(EditorBrowsableState.Never)]
public abstract class BorderedControlBase<T> : Control
where T : Control, new()
{ protected T innerControl; protected BorderedControlBase() { innerControl = new T();innerControl.GotFocus += delegate { OnGotFocus(EventArgs.Empty); };
innerControl.LostFocus += delegate { OnLostFocus(EventArgs.Empty); };
innerControl.TextChanged += delegate { OnTextChanged(EventArgs.Empty); };
Controls.Add(innerControl);
}
protected override void OnResize(EventArgs e)
{ base.OnResize(e);innerControl.Bounds = new Rectangle(1, 1, ClientSize.Width - 2, ClientSize.Height - 2);
Height = innerControl.Height + 2;
}
protected override void OnParentChanged(EventArgs e)
{ base.OnParentChanged(e);Invalidate();
}
protected override void OnGotFocus(EventArgs e)
{ base.OnGotFocus(e);Invalidate();
innerControl.Focus();
}
protected override void OnLostFocus(EventArgs e)
{ base.OnLostFocus(e);Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{ e.Graphics.Clear(innerControl.Focused ? SystemColors.Highlight : BackColor);}
protected override void OnPaintBackground(PaintEventArgs e)
{if (Environment.OSVersion.Platform != PlatformID.WinCE)
base.OnPaint(e);}
public override Font Font
{get { return base.Font; }
set {if (Environment.OSVersion.Platform != PlatformID.WinCE)
{var font = new Font(value.Name, value.Size, value.Style);
base.Font = innerControl.Font = font;}
else base.Font = innerControl.Font = value;
}
}
public override string Text
{get { return innerControl.Text; }
set { innerControl.Text = value; }
}
public override bool Focused
{get { return innerControl.Focused; }
}
}
Now that we have this base control we can easily add borders to any control. Here's an example of how to use the the bordered control base:
public class BorderedTextBox : BorderedControlBase<TextBox>
{........
}
public class BorderedComboBox : BorderedControlBase<ComboBox>
{........
}
Of course you will still have to wrap all the members of the wrapped control you wish to expose to access them. Hope you find this useful. If you need the Visual Studio solution then you can grab it here.
