2

I have created a custom control which extends GroupBox. This control supports collapsing and expanding and I use the GroupBoxRenderer and ButtonRenderer to make it look like a typical GroupBox that has a Button in the corner. I have handled all the appropriate mouse events which effectively make the "button" behave and look like a regular Button. Now I have hit a problem where the GroupBox does not receive focus using TabStop. Is there anyway that I can get my Collapsable GroupBox to receive focus from a TabStop?

I was hoping that I could use the trick from How to set focus to a control after validation in .NET to set the focus in the Enter event but I haven't come up with a good way of determining when it should actually get the focus. I could probably devise a way of finding the siblings with the next highest and lowest TabIndex (or ChildIndex if the same TabIndex) and then determine if they lost focus but this seems a bit hacky and a high chance of breaking if I don't get it exactly right.

Note: I did initially create user control but this was not what I wanted for various reasons including:

  1. It is not a control that contains a button and a groupbox (it just so happens to sort of look that way), it is a groupbox
  2. Flexibility
  3. Coupling between backend code and UI
  4. Dynamic layout
  5. Shared across many projects which require Toolbox support and customising the UI and layout of the entire control

Here is what it looks like when expanded: enter image description here

And now when it has been collapsed (and has focus): enter image description here

Community
  • 1
  • 1
steinybot
  • 5,491
  • 6
  • 37
  • 55
  • I don't really get this, you said you `extends GroupBox` which I think is some kind of inheritance but if so why you have to use `GroupBoxRenderer`? What is the actual base class of your custom `GroupBox`? BTW, a `GroupBox` doesn't support focusability. I don't think a `GroupBox` needs such a feature, could you explain more on this? Or you want it to be able to receive focus to handle some focus related events like `Enter` and `GotFocus`? – King King Jul 11 '13 at 08:13
  • @KingKing I am inheriting GroupBox but overriding the OnPaint. I need to render the whole thing myself otherwise the GroupBox Text ends up behind the collapse/expand button (see the images I added). Just to be clear, a GroupBox can receive focus programmatically but since it is a static control it will not receive focus through the TabStop mechanism. – steinybot Jul 14 '13 at 20:33

1 Answers1

0

CheckGroup => Here is an approach for a Custom Control that inherits from Control and uses the GroupBoxRenderer and CheckBoxRenderer in the OnPaint method. It is a container that mimics having focus by changing how the CheckBoxRenderer draws. This code collapses the CheckGroup and disables any child controls, but you could remove either or both as desired.

using System;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
namespace CoolControls
{

[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(System.ComponentModel.Design.IDesigner))] 
public partial class CheckGroup : Control
{
    public event EventHandler CheckBoxGotFocus;
    public event EventHandler CheckBoxLostFocus;

    private int _CheckBoxSideLength;
    private Rectangle _CheckBoxBorderRectangle;
    private bool _Focused = false;
    private bool _Checked;
    private CheckBoxState _CheckedState = CheckBoxState.UncheckedNormal;
    private int _ExpandedHeight;

    [Category("Behavior")]
    [Description("Get or set whether the checkbox is checked.")]
    public bool Checked
    {
        get { return _Checked; }
        set
        {
            SetCheckedState(value);
        }
    }

    public CheckGroup()
    {
        InitializeComponent();
        InitControl();
    }

    private void InitControl()
    {
        _CheckBoxSideLength = 15;
        _Checked = true;
        _Focused = false;
        _CheckBoxBorderRectangle = new Rectangle(0, 0, _CheckBoxSideLength - 1, _CheckBoxSideLength - 1);
    }

    private void SetCheckedState(bool pToChecked)
    {
        _Checked = pToChecked;
        if (_Checked)
        {
            _CheckedState = CheckBoxState.CheckedNormal;
            this.Height = _ExpandedHeight;
        }
        else
        {
            _CheckedState = CheckBoxState.UncheckedNormal;
            this.Height = _CheckBoxSideLength;
        }
        foreach (Control c in this.Controls)
        {
            c.Enabled = _Checked;
        }
        this.Invalidate();
    }

    protected override void OnPaint(PaintEventArgs pe)
    {
        base.OnPaint(pe);
        Graphics g = pe.Graphics;
        GroupBoxRenderer.DrawGroupBox(g, ClientRectangle, "   " + this.Text, this.Font, this.ForeColor, TextFormatFlags.Left, GroupBoxState.Normal);
        CheckBoxRenderer.DrawCheckBox(g, ClientRectangle.Location, _CheckBoxBorderRectangle, "", null, TextFormatFlags.Left, _Focused, _CheckedState);
    }

    protected override void OnSizeChanged(EventArgs e)
    {
        base.OnSizeChanged(e);
        if (_Checked)
        {
            _ExpandedHeight = this.Size.Height;
        }
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);
        if (e.Location.Y <= _CheckBoxSideLength)
        {
            SetCheckedState(!_Checked);
        }
    }

    protected override void OnGotFocus(EventArgs e)
    {
        base.OnGotFocus(e);
        _Focused = true;
        Invalidate();
        CheckBoxGotFocus.Invoke(this, new EventArgs());
    }

    protected override void OnLostFocus(EventArgs e)
    {
        base.OnLostFocus(e);
        _Focused = false;
        Invalidate();
        CheckBoxLostFocus.Invoke(this, new EventArgs());
    }

    protected override void OnKeyDown(KeyEventArgs e)
    {
        base.OnKeyDown(e);
        if (e.KeyCode == Keys.Space)
        {
            SetCheckedState(!_Checked);
        }
    }

    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);
        Invalidate();
    }
}
}
John Kurtz
  • 775
  • 8
  • 16