5

Background:

In my winforms form, I have a Checked ListView and a "master" checkbox called checkBoxAll. The behaviour of the master is as follows:

  • If the master is checked or unchecked, all ListViewItems must change accordingly.

  • If the user unchecks a ListViewItem, the master must change accordingly.

  • If the user checks a ListViewItem, and all other ListViewItems are checked aswell, the master must change accordingly.

I have written the following code to mimic this behaviour:

private bool byProgram = false; //Flag to determine the caller of the code. True for program, false for user.

private void checkBoxAll_CheckedChanged(object sender, EventArgs e)
{
    //Check if the user raised this event.
     if (!byProgram)
     {
         //Event was raised by user!

         //If checkBoxAll is checked, all listviewitems must be checked too and vice versa.

         //Check if there are any items to (un)check.
         if (myListView.Items.Count > 0)
         {
             byProgram = true; //Raise flag.

             //(Un)check every item.
             foreach (ListViewItem lvi in myListView.Items)
             {
                 lvi.Checked = checkBoxAll.Checked;
             }

             byProgram = false; //Lower flag.
         }
     }
}

private void myListView_ItemChecked(object sender, ItemCheckedEventArgs e)
{
    //Get the appropiate ListView that raised this event
    var listView = sender as ListView;

    //Check if the user raised this event.
    if (!byProgram)
    {
        //Event was raised by user!

        //If all items are checked, set checkBoxAll checked, else: uncheck him!

        bool allChecked = true; //This boolean will be used to set the value of checkBoxAll

        //This event was raised by an ListViewItem so we don't have to check if any exist. 
        //Check all items untill one is not checked.
        foreach (ListViewItem lvi in listView.Items)
        {
            allChecked = lvi.Checked;
            if (!allChecked) break;
        }

        byProgram = true; //Raise flag.

        //Set the checkBoxAll according to the value determined for allChecked.
        checkBoxAll.Checked = allChecked;

        byProgram = false; //Lower flag.
    }
}

In this example, I use a flag (byProgram) to make sure an event was caused by the user or not, thereby preventing an infinite loop (one event can fire another, which can fire the first one again etc. etc.). IMHO, this is a hacky solution. I searched around but I couldn't find a MSDN documented method to determine if an User Control Event was directly fired thanks to the user. Which strikes me as odd (again, IMHO).

I know that the FormClosingEventArgs has a field which we can use to determine if the user is closing the form or not. But as far as I know, that is the only EventArg that provides this kind of functionality...

So in summary:

Is there a way (other than my example) to determine if an event was fired directly by the user?

Please note: I don't mean the sender of an event! It won't matter if I code someCheckBox.Checked = true; or manually set someCheckBox, the sender of the event will always be someCheckBox. I want to find out if it is possible to determine whether it was through the user (click) or by the program (.Checked = true).

Aaand also: 30% of the time it took to write this question was to formulate the question and the title correctly. Still not sure if it is a 100% clear so please edit if you think you can do better :)

Jordy
  • 1,816
  • 16
  • 29
  • Just a wild guess, but there really should be something in the EventArgs. Have you inspected them at runtime (debugging)? – Janis F Jul 09 '13 at 11:52
  • On click event sets a flag to know that this call is from a click and not by code? – Mahmoud Darwish Jul 09 '13 at 11:52
  • @Romiox, check my question it says I doesn't have that kind of functionality (only FormClosing as for as I know has this). – Jordy Jul 09 '13 at 11:55
  • @MEYWD, I thought about that, but that's still a flag and now also with another event in the mix. Hardly seems like the ideal solution. – Jordy Jul 09 '13 at 11:56
  • I don't see why it is hacky, actually I think that this is the most adequate way to account for that. You cannot use sender because it is every time the same (it does not diferentiate between being checked directly or through the code). – varocarbas Jul 09 '13 at 11:56
  • @DGibbs, Please read the "Please Note" section of my question. – Jordy Jul 09 '13 at 11:57
  • 1
    http://stackoverflow.com/questions/3103912/how-can-i-determine-if-an-event-was-fired-by-a-user-action-or-by-code – Mahmoud Darwish Jul 09 '13 at 11:57
  • @Jordy You claimed that for the sender, not for the event args! – Janis F Jul 09 '13 at 12:04
  • @MEYWD, +1 for finding something in 10 minutes which took me more then a hour before I gave up. Almsot a duplicate, but this question has better answers :) – Jordy Jul 09 '13 at 12:05
  • @Romiox, I'm sorry but I really didn't. I said that "FormClosingEvent**Args** has a field which we can use to determine if...". I never said anything about the sender. – Jordy Jul 09 '13 at 12:10
  • @Jordy Oh, I see... Kinda embarrassing. Sorry! – Janis F Jul 09 '13 at 12:18
  • @Romiox, no worries, it happens to all of us ;) – Jordy Jul 09 '13 at 12:30

3 Answers3

5

No, there's no practical way to determine whether the change came from GUI or was done by program (in fact, you could analyze the callstack - but that's not recommended because it's very slow and error-prone).

BTW, there's one other thing you could do instead of setting byProgram. You could remove and add the event handler prior or after, respectively, change your controls:

checkBoxAll.CheckedChanged -= checkBoxAll_CheckedChanged;
// do something
checkBoxAll.CheckedChanged += checkBoxAll_CheckedChanged;
JeffRSon
  • 10,404
  • 4
  • 26
  • 51
  • This is what I regularly do. – dotNET Jul 09 '13 at 12:02
  • +1 for giving a solution that doesn't involve extra events or flags! – Jordy Jul 09 '13 at 12:02
  • 1
    I'd argue that it's not a great idea for detaching/reattaching event handlers in this manner. Apart from the *slight* overhead, what if you have 20 event handlers? I feel the blocking code is better because you are reacting to "state" rather than whether or not an event is hooked up or not because there is always the chance you forget to reattach or detach. – James Jul 09 '13 at 12:16
4

Instead of using the changed event, you could use the clicked event to cascade the change through to the relevant controls. This would be in response to a user click, and not the value being changed programatically.

Tevo D
  • 3,351
  • 21
  • 28
  • +1 for giving a solution that doesn't involve extra flags. But hwo do you know the property actually changed? That would involve storing old values right? – Jordy Jul 09 '13 at 12:03
  • 1
    If I get a clicked event on the select all box, then I could set the values of all checkboxes. If I get a clicked event on one of the option items, I would only be updating the select all checkbox based on the state of the options. – Tevo D Jul 09 '13 at 12:06
  • You're right! I don't care wheter or not the value did or didn't change. I can just look at the value it currently has. – Jordy Jul 09 '13 at 12:12
  • One caveat is timing. I believe that the click event may occur before the item changes, so you would have to account for that. – Tevo D Jul 09 '13 at 12:27
2

This is something I come across quite a lot and what I tend to try do is not split it between user interaction vs program interaction - I use more generic code i.e. the UI is being updated and doesn't require any events to be handled. I usually package this up through BeginUpdate/EndUpdate methods e.g.

private int updates = 0;

public bool Updating { get { return updates > 0; } }

public void BeginUpdate()
{
    updates++;
}

public void EndUpdate()
{
    updates--;
}

public void IndividualCheckBoxChanged(...)
{
    if (!Updating)
    {
        // run code
    }
}

public void CheckAllChanged(...)
{
    BeginUpdate();
    try
    {
        // run code
    }
    finally
    {
        EndUpdate();
    }
}
James
  • 80,725
  • 18
  • 167
  • 237
  • 1
    +1, still a flag, but I am going to use this construction the next time I need to raise a flag. – Jordy Jul 09 '13 at 12:14
  • @Jordy it's not *another* flag, it's an improvement on your existing flag by adding more structure & meaning. The `BeginUpdate`/`EndUpdate` pattern is a well used & adopted approach to this sort of problem, throughout various different platforms (.NET included). The idea is you are reacting to "state" regardless of whether an event is attached or not, this makes for much more readable code. – James Jul 09 '13 at 12:19
  • I see... I probably shouldn't have described it as "still a flag". But for some reason I can't edit my comment when it's too old or something :\ – Jordy Jul 09 '13 at 12:28
  • No worries, I got what you meant. However, the point I am trying to make is don't think about it as if "oh no, another flag" - think about it from an application architecture point of view i.e. my app has to deal with scenarios where UI updates can result in events being triggered which I want to ignore under certain circumstances, how can I account for that? What you are basically doing here is taking a common pattern & applying it to your own code therefore keeping your code consistent. – James Jul 09 '13 at 12:36