How can I prevent an event from causing its own ev

2019-06-22 12:44发布

问题:

I have a treeview with checkboxes and I have the following handler for the "AfterCheck" event:

private void trvAvailableFiles_AfterCheck(object sender, TreeViewEventArgs e)
{
    if (!_isCheckingInProgress)
    {
        trvAvailableFiles.BeginUpdate();

        var nodePath = e.Node.Tag.ToString();
        bool isChecked = e.Node.Checked;
        e.Node.Nodes.Clear();

        try
        {
            _fileTreeLogic.GetChildNodes(e.Node, true);
            e.Node.ExpandAll();

            _isCheckingInProgress = true;
            SetChildrenCheckState(e.Node, isChecked);
            _isCheckingInProgress = false;

        }
        finally
        {
            trvAvailableFiles.EndUpdate();
        }
    }
}

If you look closely you'll see that I'm checking if "_isCheckingInProgress". If it is not, then I proceed and expand all the nodes and call the SetChildrenCheckState() method. The problem I have encountered is that SetChildrenCheckState() will subsequently cause each child node to all fire the AfterCheck event for its own node.

My question is, is there a more clean way to allow the first AfterCheck event to fire but not the subsequent ones? It seems kind of hackish that I have to have an instance bool variable to check and set.

回答1:

One recommendation you'll see occasionally around SO is to not put a lot of code into event handlers themselves. There are a number of reasons for this. First, in your case it would be easier to understand a call like:

private void trvAvailableFiles_AfterCheck(object sender, TreeViewEventArgs e)
{
    if (!_isCheckingInProgress) 
    {
        _isCheckingInProgress = true;
        try { GetAvailableFiles(); } catch {}
        _isCheckingInProgress = false;
    }
}

And to place the rest of your code in GetAvailableFiles(). This creates a separation between event code and action code which most people would agree is a worthwhile distinction to make.

Second, which may or may not be applicable in your case is that multiple events can cause the same action. Such as mnuFileQuit_Click and btnClose_Click as an obvious example. If both make calls to CloseApplication() it removes a lot of redundant code.



回答2:

Use: if(e.Action != TreeViewAction.Unknown) instead of if (!_isCheckingInProgress). See TreeViewAction .

When the user uses the keyboard or mouse to check the checkboxes, e.Action will be TreeViewAction.ByKeyboard or TreeViewAction.ByMouse.

The MSDN provides this as example code for the TreeView.AfterCheck Event.

Edit 1: Obviously, if you're setting the checkbox yourself in code, move the code in the event handler to a new function and have the code that sets the checkbox call it directly. The point of this solution is to let you use event handlers for user input without having those events get triggered when you set the checkboxes yourself via code.

Edit 2: See Spencer's answer for an explanation of my comment in Edit 1



回答3:

Personally, I use a function that removes and then adds the event.

private void trvAvailableFiles_AfterCheck(object sender, TreeViewEventArgs e)
{
    EnableEvents(false);
    trvAvailableFiles.BeginUpdate();

    var nodePath = e.Node.Tag.ToString();
    bool isChecked = e.Node.Checked;
    e.Node.Nodes.Clear();

    try
    {
        _fileTreeLogic.GetChildNodes(e.Node, true);
        e.Node.ExpandAll();

        SetChildrenCheckState(e.Node, isChecked);

    }
    finally
    {
        trvAvailableFiles.EndUpdate();
    }
    EnableEvents(true);
}

private void EnableEvents(bool bEnable)
{
    if(bEnable)
        cbWhatever.OnChecked += EventHandler;
    else
        cbWhatever.OnChecked -= EventHandler;
}


回答4:

No, there's no cleaner way to do what you've shown. I'm not really sure why you feel that variables are a "hack" approach. Setting a flag is a common technique used when writing UI code.

The real hack would be some obscure way to prevent raising the event the first time, but not subsequent times. Future maintenance programmers are guaranteed to understand how setting a flag works; they're not guaranteed to appreciate the "elegance" of your alternative approach.