TaW
TaW

Reputation: 54453

ContextMenu and Lambda - is it possible?

I'd like to use a context menu and get the user's choice in a local variable.

I tried to use a simple lambda like this:

void showMenu(Point pos)
{
    ContextMenu m = new ContextMenu();

    int choice = -1;
    var menueChoices = new string[] {"Stretch Mode", "Brightness",  "Saturation", 
                                     "Hues", "Flip Color Stops", "Invert Colors" };
    for (int i = 0; i < menueChoices.Length; i++ )
    {
        MenuItem mi = new MenuItem(menueChoices[i]);
        mi.Index = i;
        mi.Click += (sender, e) =>  { choice = mi.Index; };
        m.MenuItems.Add(i, mi);
    }

    m.Show(flowLayoutPanel1, pos);
    // work with the choice..

}

The choice is assigned alright in the lambda (I can print it to the Console) but is reset afterwards.

I have found in this post that according to the C# Language Specification:

5.3.3.29 Anonymous functions

For a lambda-expression or anonymous-method-expression expr with a body (either block or expression) body:

  • The definite assignment state of an outer variable v before body is the same as the state of v before expr. That is, definite assignment state of outer variables is inherited from the context of the anonymous function.

  • The definite assignment state of an outer variable v after expr is the same as the state of v before expr

OK. Now, I realize that I could create a non-anonymous function but I'd really love to use this concise syntax for a job as trivial as assigning one integer. I guess I missed something obvious? About Lambdas? Or about ContextMenus??

I tried a Property and also changing the MenueItem but didn't find how persist anything from that lambda..

Update:

Now that I have learned the menu.Show doesn't block as a ShowDialog does, I can change my code simply to

mi.Click += (sender, e) =>  {  menuAction( mi) ;  };

and

void menuAction(MenuItem choice)
{
    //..do my stuff
}

So the Property change I had tried as well was persisted after all but I had used the Property too soon and read the 'outer variable' part of the specs a bit too pessimistically..

Upvotes: 0

Views: 525

Answers (1)

Rotem
Rotem

Reputation: 21947

You seem to be under the impression that calling Show on your ContextMenu will block until the menu is closed.

This is in fact not the case, the showMenu method will exit right after showing the menu, before the user selects an item, and your choice local will be thin air at that point.

You must use an instance or otherwise referenced variable to store the selection in.

Typically, you would define a field on the class which contains showMenu, and reference that field in your lambda.

However, that field will be populated only when the user selects an item, which is not right after you call Show on the ContextMenu.

Upvotes: 2

Related Questions