0

I have a checkedListBox and a panel. They are both created by code. Now I want to make an event that by checking and Unchecking an item of the checkedListBox items this panel gets enabled or disable. I have the following code but It doesn't work and throws exception on run time.

CheckedListBox chlb = new CheckedListBox();
for (int i = 0; i < dr.Count(); i++)
{
    chlb.Items.Add( dr[i]["Value_Name"].ToString());
    if ((bool)dr[i]["HasText"] == true)
    {
        Panel pnltxt = new Panel();
        pnltxt.Size = new Size(630, 30);
        chlb.SelectedIndexChanged += (s, argx) => pnltxt.Enabled =
           (chlb.GetItemCheckState(i).ToString().Trim() == "Unchecked" ? false : true);
    }
}

Error message is:

Additional information: InvalidArgument=Value of '7' is not valid for 'index'. + chlb.GetItemCheckState(i) 'chlb.GetItemCheckState(i)' threw an exception of type 'System.ArgumentOutOfRangeException' System.Windows.Forms.CheckState {System.ArgumentOutOfRangeException}

Can you please help me how to write the correct code for this event.

3
  • Let me guess, dr.Count() is 7? Commented Apr 3, 2014 at 17:34
  • 4
    This isn't breaking anything, but you really shouldn't be comparing against "Unchecked" as a string. Just compare against CheckedState.Unchecked, and avoid the string problem. Commented Apr 3, 2014 at 17:35
  • 1
    For the love of clarity, change chlb.GetItemCheckState(i).ToString().Trim() == "Unchecked" ? false : true to chlb.GetItemCheckState(i) != CheckState.Unchecked Commented Apr 3, 2014 at 17:49

2 Answers 2

1

Your problem is with lambda expressions and closure.

When you create a lambda expression (as you do with your event handler) and it references an outside variable, the compiler needs to create a closure to include that variable so that you lambda function can use it. So here:

for (int i=0; ......)
{
     //...
     chlb.SelectedIndexChanged += (s, argx) => pnltxt.Enabled =
         (chlb.GetItemCheckState(i).ToString().Trim() == "Unchecked" ? false : true);
     //..........................^
}

The lambda expression uses i which is the loop variable, it has to have a closure that includes it so it can use it when the function is executed. But here's the problem, it doesn't get a copy of the variable at the time the lambda expression was assigned to your event handler, it actually has a reference to the same variable as the one you are looping on. So what's the problem? When you execute the lambda expression (i.e. the event happens) the value of i is the value it had at the end of the loop! Which is, dr.Count() (presumably, 7).

The solution is to copy the variable inside you loop (as in @astander's answer) so that it will close over the copy of the variable that will retain the value it had when you attached the event handler.

So:

for (int i=0; ......)
{
     int copyOfi = i;    // A new variable will get created every iteration!
     //...
     chlb.SelectedIndexChanged += (s, argx) => pnltxt.Enabled =
         (chlb.GetItemCheckState(copyOfi).ToString().Trim() == "Unchecked" ? false : true);
     //..........................^
     // And this will close to include only the `copyOfi` we created in this iteration
}
Sign up to request clarification or add additional context in comments.

Comments

0

Change your code to

if ((bool)dr[i]["HasText"]) {
    Panel pnltxt = new Panel();
    pnltxt.Size = new Size(630, 30);
    int index = i;
    chlb.SelectedIndexChanged += (s, argx) => pnltxt.Enabled =
        chlb.GetItemCheckState(index).ToString().Trim() != "Unchecked";
}

It is something that is called Access to modified closure , and has to do with the scopingrules of closures.

Have a look at Access to modified closure

This message is stating that the variable you are using from your enumeration may potentially change and not be what you would expect it to be at the time it will be used. This will not throw a compiler error .

The recommended approach when using variables like this inside an enumeration (for each loop) is to create a local copy and use that.

So accordingly, I created a local copy of the variable i in index and used that.

5 Comments

You should explain what you changed and why that change is important. This post, without an explanation, is a low quality answer.
@Servy, I am looking for a decent link/article to provide to the OP
It is not considered appropriate on this site to just link to a solution to a problem. Your answer should answer the question not just link to an answer elsewhere.
@Servy, I have updated the answer as to your request.
I have edited the answer and simplifyed the part pnltxt.Enabled = (condition ? false : true) and chaned it to pnltxt.Enabled = !condition and also removed the == true part.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.