简体   繁体   中英

Differentiate between a user changing the Checkbox.Checked value, or it programmatically changing

I see that Checkboxes have a CheckedChanged event. is it possible to tell whether it was changed programmatically, or by the user actually checking the checkbox?

I've got a large grid where the user can type in a filter, or use checkboxes for a sort of "Quick filter" that offers common filtering parameters. Then say they go and modify the filter through the textbox, I was checking whether or not I should programmatically (un)check the CheckBox controls so that it reflects the filter in the textbox.

    private void genericCheckbox_CheckedChanged(object sender, EventArgs e)
    {
        UpdateFilter();
    }

    private void UpdateFilter()
    {
        if (gdcSVNDefaultView.RowCount == 0)
            return;

        gdcSVNDefaultView.ActiveFilterString = BuildTableFilter();
        gdcSVNDefaultView.BestFitColumns();
    }

    private void gdcSVNDefaultView_ColumnFilterChanged(object sender, EventArgs e)
    {
        lblTotalFileCount.Text = gdcSVNDefaultView.RowCount.ToString();

        if (gdcSVNDefaultView.ActiveFilterString.Contains("Normal"))
            cheNormalFiles.Checked = true;
        else
            cheNormalFiles.Checked = false;

        if (gdcSVNDefaultView.ActiveFilterString.Contains("bin") || 
            gdcSVNDefaultView.ActiveFilterString.Contains("obj"))
            cheBinObjFolders.Checked = true;
        else
            cheBinObjFolders.Checked = false;
    }

With some very light testing, this seems to work just as I want it to. But I'm afraid that there's some sort of 'infinite loop' case where the ColumnFilterChanged event will fire because of the UpdateFilter method being called when the CheckedChanged event happens, which could in turn cause CheckedChange to happen again since the ColumnFilterChanged manipulates the Checkboxes.

Using flag for this purpose is OK:

bool suppressCheckedChanged;
private void gdcSVNDefaultView_ColumnFilterChanged(object sender, EventArgs e)
{
   suppressCheckedChanged = true;
   //.... your own code
   //....
   suppressCheckedChanged = false;
}
private void genericCheckbox_CheckedChanged(object sender, EventArgs e)
{
    if(suppressCheckedChanged) return;
    UpdateFilter();
}

UPDATE

Using flag is the best way (the most concise and convenient) I think. However, after some searching on the internal implementation of CheckBox.cs , I've found that the internal field checkState is used. the Checked property is designed just for convenience. The CheckedChanged event is raised in the setter of the CheckState property. So by modifying the checkState field, we bypass the CheckedChanged event raiser. Because the field checkState is not public, we have to use Reflection to change its value. That's why this code is a little lengthy compared to using flag . This is the code for you, note that this is just a reference to open wide the knowledge on this problem, as I said using flag is much more concise and the code is also coherent:

//Use this extension method for convenience
public static class CheckBoxExtension {
  public static void SetChecked(this CheckBox chBox, bool check){
    typeof(CheckBox).GetField("checkState", BindingFlags.NonPublic |
                                            BindingFlags.Instance)
                    .SetValue(chBox, check ? CheckState.Checked : 
                                             CheckState.Unchecked);
    chBox.Invalidate();
  }
}
//then you can use the SetChecked method like this:
checkBox1.SetChecked(true);//instead of checkBox1.Checked = true;
checkBox1.SetChecked(false);//instead of checkBox1.Checked = false;

I'd probably detach and then reattach the handler, eg

private void gdcSVNDefaultView_ColumnFilterChanged(object sender, EventArgs e)
{
    cheNormalFiles.CheckedChanged -= genericCheckbox_CheckedChanged;
    cheBinObjFolders.CheckedChanged -= genericCheckbox_CheckedChanged;

    // do stuff...

    cheNormalFiles.CheckedChanged += genericCheckbox_CheckedChanged;
    cheBinObjFolders.CheckedChanged += genericCheckbox_CheckedChanged;
}

通常,您可以使用Click事件 CheckBoxName_Click,该事件仅在用户单击复选框时调用(而不是在您从代码中设置复选框选中属性时),如事件名称所示。

A bit of a dead thread, but I recently had to solve this and I leveraged the Focused property on the checkbox to glean whether or not a user pressed it or if it was triggered programmatically.

I believe all that would need to happen for your use case is wrapping the UpdateFilter() call with a conditional that looks at the Focused property:

private void genericCheckbox_CheckedChanged(object sender, EventArgs e)
{
    // Only update filter if the checkbox was triggered by the user
    if (((CheckBox)sender).Focused)
    {
        UpdateFilter();
    }
}

private void UpdateFilter()
{
    if (gdcSVNDefaultView.RowCount == 0)
        return;

    gdcSVNDefaultView.ActiveFilterString = BuildTableFilter();
    gdcSVNDefaultView.BestFitColumns();
}

private void gdcSVNDefaultView_ColumnFilterChanged(object sender, EventArgs e)
{
    lblTotalFileCount.Text = gdcSVNDefaultView.RowCount.ToString();

    if (gdcSVNDefaultView.ActiveFilterString.Contains("Normal"))
        cheNormalFiles.Checked = true;
    else
        cheNormalFiles.Checked = false;

    if (gdcSVNDefaultView.ActiveFilterString.Contains("bin") || 
        gdcSVNDefaultView.ActiveFilterString.Contains("obj"))
        cheBinObjFolders.Checked = true;
    else
        cheBinObjFolders.Checked = false;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM