2

I need someone to help me with this little problem I have. Here's a code fragment:

void shuffle() 
{
    for (int i = 0; i < 4; i++) {

        // here should be the computations of x and y

        buttons[i].Click += (s, e) => { show_coordinates(x, y); };

        // shuffling going on
    }
}

void show_coordinates(int x, int y)
{
    MessageBox.Show(x + " " + y);
}

as you can see I create a new event handler with different x and y for each button every time I run the loop. There's another button in my form which shuffles the buttons randomly.

So here's the problem: If I press the shuffle button say 10 times and then press any of the shuffled buttons, event handlers stack up and I get 10 message boxes displaying x and y values.

So how can I overwrite the previous event handler with a new one each time I press shuffle.

Thanks in advance.

Linas
  • 560
  • 1
  • 5
  • 16
  • Seems like this would not compile as `x` and `y` do not exist at the point the lambda is constructed. – asawyer Dec 09 '13 at 14:20
  • True indeed, however the compiler would probably tell him that to :) And as he wrote he already managed to get the result 10 times when he clicks on it. We can expect that he just omitted that particular code. – woutervs Dec 09 '13 at 14:26

3 Answers3

7

I would redesign the code instead to do something like this:

private PointF[] points = new PointF[4];

//Run once
public void Initialize()
{
    for (int i = 0; i < 4; i++)
        buttons[i].Click += (s, e) => { show_coordinates(i); };
}

public void Shuffle()
{
    for (int i = 0; i < 4; i++) 
    {
        // here should be the computations of x and y
        points[i] = new PointF(x,y);
        // shuffling going on
    }
}

public void show_coordinates(int index)
{
    var point = points[index];
    MessageBox.Show(point.X + " " + point.Y);
}
Magnus
  • 45,362
  • 8
  • 80
  • 118
  • Using class indepently works nice. Be aware that leaving event handlers uncleared will result memoryleaks (atleast in winforms I have encountered this in a form of object handles) – Panu Oksala Dec 09 '13 at 19:53
0

Reflection is a one way to do it, but i would prefer a delegate and adding/removing that delegate. This leads to easier maintainable code than using a reflection:

How to remove a lambda event handler

Community
  • 1
  • 1
Panu Oksala
  • 3,245
  • 1
  • 18
  • 28
  • You mentioned removal of delegates. But will it work in this particular situation. Do I have to create an array to hold a delegate for each button? – Linas Dec 09 '13 at 14:32
  • Heres an good example of using delegates http://msdn.microsoft.com/en-us/library/ms173172.aspx – Panu Oksala Dec 09 '13 at 19:50
0

In your for loop first remove the event handlers then re-add them. However take into account that this is bad practice. You should use the button's datacontext and bind an object to it containing your x and y values. This way you only have to attach your event handler once. Instead of every time shuffle get's called. Then updating your buttons datacontext is way more convenient.

Example PseudoCode:
public MainWindow() {
ForEach Button addEvent(DoSomething);
ForEach Button Button.Datacontext = new Data();
}

public class Data {
prop X;
prop Y;
ctor(x,y)
ctor()
}

public void DoSomething()
{
var data = Button.datacontext as data
MessageBox(data.x, data.y)
}

public void Shuffle()
{
calc x, y
foreach Button (Button.datacontext as data).x = x, ...
}
woutervs
  • 1,500
  • 12
  • 28
  • DataContext is a WPF thingy, the question is tagged WinForms. – Maarten Dec 09 '13 at 14:27
  • Completely looked over that, In winforms instead of datacontext one could use the Tag (if that is a string then the tag in combination with a dictionary) – woutervs Dec 09 '13 at 14:31
  • Tag can be anything, the Tag property is of type Object. – Maarten Dec 09 '13 at 14:32
  • In that case you can just set the tag as the data object and then instead of getting the datacontext, you can get the Data object, the same way I showed in my pseudo code example. – woutervs Dec 09 '13 at 17:54