How to prevent firing CheckedChanged event when checking a control programmatically?

I think your way is fine.

The other way to do it is remove the EventHandler before the check, and then add it back again after the check. This way eliminates the need for the isFrozen variable.

private void btn1_CheckedChanged(object sender, EventArgs e)
{
  btn2.CheckedChanged -= btn2_CheckedChanged;
  btn2.Checked = false;
  btn2.CheckedChanged += btn2_CheckedChanged;

    // Do some staff
}

private void btn2_CheckedChanged(object sender, EventArgs e)
{
  btn1.CheckedChanged -= btn1_CheckedChanged;
  btn1.Checked = false;
  btn1.CheckedChanged += btn1_CheckedChanged;

    // Do another staff
}

In VB:

RemoveHandler btn2.CheckedChanged, AddressOf btn2_CheckedChanged
btn2.Checked = false
AddHandler btn2.CheckedChanged, AddressOf btn2_CheckedChanged

I came across this post after wanting to implement something like this for a while. I regularly use Measurement Studio from National Instruments, and their WinForms controls that have the event StateChanging or StateChanged pass a parameter of type ActionEventArgs, which has a property Action which can take three values: ByKeyboard, ByMouse and Programatic. This is very useful in determining what has caused the state of the control to change. I wanted to replicate this in a standard WinForms checkbox.

Here is my code:

public enum ControlSource
{
    Programatic,
    ByKeyboard,
    ByMouse
}

public class AwareCheckBox : Checkbox
{
    public AwareCheckBox()
            : base()
    {
        this.MouseDown += AwareCheckbox_MouseDown;
        this.KeyDown += AwareCheckbox_KeyDown;
    }

    private ControlSource controlSource = ControlSource.Programatic;

    void AwareCheckbox_KeyDown(object sender, KeyEventArgs e)
    {
        controlSource = ControlSource.ByKeyboard;
    }

    void AwareCheckbox_MouseDown(object sender, MouseEventArgs e)
    {
        controlSource = ControlSource.ByMouse;
    }

    public new event AwareControlEventHandler CheckedChanged;
    protected override void OnCheckedChanged(EventArgs e)
    {
        var handler = CheckedChanged;
        if (handler != null)
            handler(this, new AwareControlEventArgs(controlSource));

        controlSource = ControlSource.Programatic;
    }
}

public delegate void AwareControlEventHandler(object source, AwareControlEventArgs e);

public class AwareControlEventArgs : EventArgs
{
    public ControlSource Source { get; private set; }

    public AwareControlEventArgs(ControlSource s)
    {
        Source = s;
    }
}

I'm sure there are improvements to make, but my rudimentary testing has demonstrated that it works. I have posted here simply in case others stumble across this issue and want a clearer way of distinguishing where the change was initiated. Any comments welcome.