2

In the program I have multiple panels being created with picture boxes. The picture boxes are delegated to be clickable. I would like to have the user to have the option to remove one of the panels/picture boxes from the order. Right now if it is removed and the order is rearranged all picture boxes after the one that was removed retains its delegated order. So clicking on any of them after the one that was removed, skips to the one next to it down the line (ie click on #9 and it will go to #10). I need to remove the delegation of the reordered ones and re delegate them correctly. I have tried:

int z2 = z;
var myClickDelegate = (EventHandler)delegate { clicked(z2, null); };
PicBx[z].Click += myClickDelegate;

to create and

PicBx[z].Click -= myClickDelegate;

to remove

and also

int z2 = z;
PicBx[z].Click -= delegate { clicked(z2, null); };

but both of them does not remove the origional delegation.

user770344
  • 473
  • 2
  • 8
  • 16
  • This would be why you would want to avoid anonymous delegates. – Claus Jørgensen Aug 10 '11 at 01:21
  • 1
    Ω is a valid identifier, no need to settle for z2. – Hans Passant Aug 10 '11 at 01:39
  • I assume by Ω, you mean z? if so its because it is within a loop and you need to declare a separate variable inside the loop. – user770344 Aug 10 '11 at 01:52
  • 1
    @Claus - Using anonymous delegates as event handlers shouldn't be avoided at all. Using (non-anonymous) methods as event handlers is bad OO - it doesn't encapsulate your logic. Also, because of the event handler signature you could wire-up the wrong event handlers to events, etc. Using anonymous delegates allows you to write in-line event handlers that can use local variables, don't disrupt the flow of your code, and are hidden from the rest of your class. They are awesome. – Enigmativity Aug 10 '11 at 02:07
  • 1
    Yeah, that would be for when you **do not** want to remove them again. – Claus Jørgensen Aug 10 '11 at 02:10

4 Answers4

1

Your first method should work, but the later one shouldn't work.
because when you do PicBx[z].Click -= delegate { clicked(z2, null); }; method you are not removing the old delegate, instead you are creating a new delegate and then removing it.

In your first try this should work:

private void SomeMethod()
{
    var myClickDelegate = (EventHandler)delegate { clicked(z2, null); };
    PicBx[z].Click += myClickDelegat;
    //Do extra work
    PicBx[z].Click -= mayClickDelegat;
}

Edit: Pair to your comment: I notice that you are only in your delegate adding clicked(z2, null), so I assumed that you are only creating the delegate at the first place just to pass that int z2 to represent the picture box index.
You can put that index with the picture box itself by using pictureBox.Tag and in the click event get that int from the tag:

int z2 = z;
picBx[z].Tag = z2;//here we embedded the number with the picture box.
PicBx[z].Click += clicked;
...
PicBx[z].Click -= clicked;

And so in the clicked event:

private void clicked(object sender, EventArgs e)
{
    PictureBox pictureBox = sender as PictureBox;

    if (pictureBox != null)
    {
        int number = Convert.ToInt32(pictureBox.Tag);
        ...
    }
}

Edit2: As pair to your comments, it seems like you have a different signature of the clicked method:

private void clicked(int tes,..
{
    Pnl[tes].BackColor = Color.Red;
}

Here we only change it to be:

private void clicked(object sender, EventArgs e)
{
    PictureBox pictureBox = sender as PictureBox;//when user clicks on picture box it will be the sender parameter.

    if (pictureBox != null)
    {
        //we add number to each of picture boxes at there tags. "picBx[z].Tag = z2"
        int tes = Convert.ToInt32(pictureBox.Tag);
        pnl[tes].BackColor = Color.Red;
    }
}
Jalal Said
  • 15,906
  • 7
  • 45
  • 68
  • Here is the issue. The removal is being called in a different area outside the original function. Ie `private void createpicbx()` will create the delegation, but when clicking a remove button `private void Button14_click()` it attempts to remove it. – user770344 Aug 10 '11 at 01:16
  • Then you should reference the handler of picture box(es) in your class, I will update the answer to show you how. – Jalal Said Aug 10 '11 at 01:19
  • So at the clicked event under `int number = Convert.ToInt32(pictureBox.Tag);` do i just put 'int z2 = tt; var myClickDelegate = (EventHandler)delegate { clicked(z2, null); }; PicBx[tt].Click -= myClickDelegate;' – user770344 Aug 10 '11 at 01:43
  • @user we get rid from that `var myClickDelegate = (EventHandler)delegate { clicked(z2, null); };` and simply just attach **All** picture boxes to the `clicked` event. don't confuse by the name `number` here, it is just represents your `z` "or `z2`" so fill free to rename it to what you think is relevant. So: every `pitureBox.Click` is assigned to `clicked` event, and when the event is occurs we just check the sender, and get the underlying picture box from it "when user click at any picture box, the sender will be the picture box the user clicked by default", and then get the `z` from its tag. – Jalal Said Aug 10 '11 at 01:52
  • Ok that makes sense. I'm getting a `No overload for 'clicked' matches delegate 'System.EventHandler'` – user770344 Aug 10 '11 at 01:57
  • @JalalAldeenSaa'd let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/2303/discussion-between-user770344-and-jalal-aldeen-saad) – user770344 Aug 10 '11 at 02:01
  • @user: add the signature to the `clicked` method in your class "you can replace your `clicked` method by this new one, copy paste the `private void clicked(object sender, EventArgs e)` method from the answer". – Jalal Said Aug 10 '11 at 02:02
  • then i cannot pass the needed int. – user770344 Aug 10 '11 at 02:30
  • @user you don't have to pass it explicitly, we put it in each of the pictures tag, when you do `picBx[z].Tag = z2;` we put that int embedded with the picture box, then `pciBx[z].Click += clicked;` and so at the `clicked` method we analyze the `sender` parameter and then get the **needed int** from the picture tag. – Jalal Said Aug 10 '11 at 02:37
  • ok but in the clicked method a set of code goes to the panel the picbox[z] is at... ie `Pnl[tes].BackColor = Color.Red;` and `i=tes'. 'z' was passed to 'tes'. When i just put `private void clicked(object sender, EventArgs e)' it won't do anything when clicked. Is there a way to pas the 'Z' from the clicked picboxs to 'tes'? – user770344 Aug 10 '11 at 02:48
  • @user You can do the same here: we already passed the `z` to `tes` by the picture tag, the `sender` here is represents a picture box clicked "let us say that it was the 3rd picture in the list", now when we can get the `z` of that picture box as the example in the answer "`PictureBox pictureBox = sender as PictureBox;` and then `int tes = Convert.ToInt32(pictureBox.Tag);`", the `tes` here will be `3` and then you then do `Pnl[tes].BackColor = Color.Red`.. – Jalal Said Aug 10 '11 at 02:58
1

You are not trying to delete the same delegate. You should try to store your delegates somewhere:

this.MyDelegate = delegate { clicked(z2, null); };

PicBx[z].Click += this.MyDelegate;


...

...


PicBx[z].Click -= this.MyDelegate;

But playing with delegates like that could lead to bad design choice. You should define a bool in which you'd determine if you execute your delegate or not.

public void OnClick(object sender, ...)
{
    if(myBool)
    {
        ...
    }
}
Jean-Philippe Leclerc
  • 6,713
  • 5
  • 43
  • 66
0

From your comment to Jalal answer, looks like you may actually need something like this

How to remove all event handlers from a control

Community
  • 1
  • 1
Fadrian Sudaman
  • 6,405
  • 21
  • 29
0

You have two issues here.

The first is, as others have pointed out, that you need to remove the same instance of the delegate that you added to the event handler.

The second is that you are using an array to store your picture boxes and your handlers are using an index to look up your picture box so when you remove a picture box from the array all of the event handlers for picture boxes higher in the array would be pointing at the wrong picture box.

Here's what to do:

Step 1: Change your event handler to take an instance of the picture box and not the index to the array.

Step 2: Create a class-level dictionary to hold your delegate references by picture box:

Dictionary<PictureBox, EventHandler> _pictureBoxHandlers =
    new Dictionary<PictureBox, EventHandler>();

Step 3: Write your subscribe code like this:

//After the picture box is added to `PicBx`
var pb = PicBx[z];
var myClickDelegate = (EventHandler)delegate { clicked(pb, null); };
_pictureBoxHandlers[pb] = myClickDelegate;
pb.Click += myClickDelegate;

Step 4: Write your unsubscribe code like this:

//Before the picture box is removed from `PicBx`
var pb = PicBx[z];
if (_pictureBoxHandlers.ContainsKey(pb))
{
    var myClickDelegate = _pictureBoxHandlers[pb];
    pb.Click -= myClickDelegate;
    _pictureBoxHandlers.Remove(pb);
}

See how that goes.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • On `clicked(pb, null)` pb is where an int would be (normally z2)... 'private void clicked(int tes, EventArgs e)'. Wouldn't that throw and error? – user770344 Aug 10 '11 at 02:07
  • @user770344 - Yes, it would. That's why my "Step 1" was to change the signature of your event handler to take the picture box and not the index. Using an integer in your event handler isn't going to work if your list of picture boxes changes. – Enigmativity Aug 10 '11 at 04:11