简体   繁体   English

区分用户更改 Checkbox.Checked 值,还是以编程方式更改

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

I see that Checkboxes have a CheckedChanged event.我看到复选框有一个 CheckedChanged 事件。 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.然后说他们 go 并通过文本框修改过滤器,我正在检查是否应该以编程方式(取消)检查 CheckBox 控件,以便它反映文本框中的过滤器。

    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.但我担心会出现某种“无限循环”情况,因为在 CheckedChanged 事件发生时调用了 UpdateFilter 方法,因此 ColumnFilterChanged 事件将被触发,这又可能导致 CheckedChange 再次发生,因为 ColumnFilterChanged 操作复选框。

Using flag for this purpose is OK: 为此目的使用flag是可以的:

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 UPDATE

Using flag is the best way (the most concise and convenient) I think. 使用flag是我认为最好的方法(最简洁方便)。 However, after some searching on the internal implementation of CheckBox.cs , I've found that the internal field checkState is used. 但是,在对CheckBox.cs内部实现进行一些搜索之后,我发现使用了内部字段checkState the Checked property is designed just for convenience. Checked酒店旨在为客人提供便利。 The CheckedChanged event is raised in the setter of the CheckState property. CheckedChanged事件在CheckState属性的setter中引发。 So by modifying the checkState field, we bypass the CheckedChanged event raiser. 因此,通过修改checkState字段,我们绕过了CheckedChanged事件提升程序。 Because the field checkState is not public, we have to use Reflection to change its value. 因为字段checkState不是公共的,所以我们必须使用Reflection来更改其值。 That's why this code is a little lengthy compared to using flag . 这就是为什么这个代码与使用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: 这是你的代码,请注意,这只是一个参考,以打开这个问题的知识,正如我所说使用flag更简洁,代码也是连贯的:

//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.有点死线,但我最近不得不解决这个问题,我利用复选框上的Focused属性来收集用户是否按下它或者它是否以编程方式触发。

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:我相信您的用例需要做的就是用一个查看Focused属性的条件来包装UpdateFilter()调用:

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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