-1

I have a class UserControl class (says MyDataGrid) which inherits from a 3rd party DataGrid (says TheirDataGrid). In this case, I only create MyDataGrid control, but there is no MyDataGridColumn and MyDataGridCell which inherit from TheirDataGridColumn and TheirDataGridCell

My case is that I create some extension method for the TheirDataGridColumn, which will add some event handler to TheirDataGridColumn (by += operator)

If we call the extension method once only, it is fine But if we call the extension method more than one times, the number of events will add up. (In my case, it is still ok, because it only modifies a primitive property in the event and the last event will take effective finally. But still want to know if there is any better way to do so).

There are several approaches I can think of at the moment:

  1. Clearing all the event handler at TheirDataGridColumn each time I call the extension method. But it seems that we can only do it by reflection to retrieve nonpublic variable (How to remove all event handlers from an event). Also, in the case above, it is a well-known default Win Button Control, but I am using a 3rd party one.

  2. Have my own MyDataGridColumn class, which inherits from TheirDataGridColumn, with an extra variable to store added event handle. So, everytime I call the extension function (indeed, at this case, no extension function is needed as we can add the function directly at MyDataGridColumn class), it can remove the event handler (by -= handler).

  3. Have somewhere to store the added event handler so that it can be removed later. But since I am at the extension method, there is no way for me to save the added event handler because there is no extension property. If I save it in static level of another class, it is in static level but not instance level. Sure, I may have a Dictionary at static level, but need to think of a unique key to identify each column.

For 1, it is quite dirty way to do so. Also, it may remove some other event handlers unintentionally

For 2, it needs quite much refactoring, which I would like to avoid

For 3, personally speaking, I prefer this approach, provided I can find a way to save the added event handler and remove it next time when I call this method again

Any help is highly appreciated.

kzfid
  • 688
  • 3
  • 10
  • 17
  • Have you read this? https://stackoverflow.com/questions/136975/has-an-event-handler-already-been-added - it seems like your problem is that you're registering multiple event handlers when you only want one. If you remove the handler first it will always succeed even if no handler is registered thus ensuring only one is added – Caius Jard Apr 01 '20 at 06:24
  • Seeing some code for this might really help! – Caius Jard Apr 01 '20 at 06:29
  • When you declare an extension method, it is a (static) method that gets the instance it is called on as a first parameter. So yes, you can access that – Hans Kesting Apr 01 '20 at 06:29
  • My other thought I that if your extension method is on a column, and the column knows which grid owns it, and the grid is yours, why not get access to your grid via the column at the time the handler is added and store the fact that it's been added in your grid, perhaps some dictionary of column:bool. I'm still favouring the "remove then add" approach. Post your extension method code and the code that calls it (curious why, given that their code cannot know it exists it must be your code that calls it multiple times so you have control over your complaint?) – Caius Jard Apr 01 '20 at 06:34
  • @CaiusJard Thanks for the detailed answer. For the solution in https://stackoverflow.com/questions/136975/has-an-event-handler-already-been-added, it (GetInvocationList) should be only valid if you own the class; otherwise, still need to use reflection. It seems the best solution for me now, as you said, it is to have a Dictionary at the Grid class (which I have control) so that when the extension method is called, a new item was added to the Dict. When the extension method is called again, it will retrieve the delegate from the Dict and remove it by -= operator before adding – kzfid Apr 08 '20 at 07:25
  • @CaiusJard For the code, you can refer to my another question here (https://stackoverflow.com/questions/60929523/undefined-protocol-in-the-url-cell/60944045#60944045). The reason is that the 3rd party control cannot disable the OpenLink property directly at the column class but only at the event. My solution is that I add a extension method to TheirDataGridColumn class to pretend that it can be set directly at the column. Yes, you are right. It must be my code that calls it multiple times. If it is me, I will not do so. But this is a lib call to others as well, I cannot control how others do. – kzfid Apr 08 '20 at 07:29
  • @CaiusJard Btw, I would like to mark yours as answer. Would you mind post your comments as answer, so that I can mark it? Thanks. – kzfid Apr 08 '20 at 07:30

1 Answers1

0

Updating the code from a previous answer, we could either remove the handler before we add it (because removing won't fail if there are no handlers):

private void UltraGrid1_InitializeLayout(object sender, InitializeLayoutEventArgs e)
{
    var column = e.Layout.Bands[YOUR_BAND_INDEX].Columns[YOUR_COLUMN_INDEX];
    column.Style = Infragistics.Win.UltraWinGrid.ColumnStyle.URL;
    var editor = column.Editor as FormattedLinkEditor;

    //remove the handler before add, to prevent adding multiple handlers
    editor.LinkClicked -= this.Editor_LinkClicked;

    editor.LinkClicked += this.Editor_LinkClicked;
}

Or we could use one of the controls' Tag properties to remember that we've added a handler for this. Windows forms controls have a Tag property that is an object and is intended for storing general purpose data to be associated with the control. If we store a string in the tag and check for it later, we can avoid to add the handler multiple times. Perhaps like one of these:

private void UltraGrid1_InitializeLayout(object sender, InitializeLayoutEventArgs e)
{
    var column = e.Layout.Bands[YOUR_BAND_INDEX].Columns[YOUR_COLUMN_INDEX];

    //did we add the handler already?
    if(column.Tag != null && column.Tag.ToString() == "handlerIsAdded")
      return;

    column.Style = Infragistics.Win.UltraWinGrid.ColumnStyle.URL;

    var editor = column.Editor as FormattedLinkEditor;
    editor.LinkClicked += this.Editor_LinkClicked;

    column.Tag = "handlerIsAdded"; //remember for next time, so we don't add handler again
}

I don't know if it's at the column level or the editor level you want to add this; I've never worked with the grid control you're using. If there is only one editor control for the column and it doesn't get renewed then you could also use the editor control:

private void UltraGrid1_InitializeLayout(object sender, InitializeLayoutEventArgs e)
{
    var column = e.Layout.Bands[YOUR_BAND_INDEX].Columns[YOUR_COLUMN_INDEX];
    column.Style = Infragistics.Win.UltraWinGrid.ColumnStyle.URL;
    var editor = column.Editor as FormattedLinkEditor;

    //did we add the handler already?
    if(editor.Tag != null && editor.Tag.ToString() == "handlerIsAdded")
      return;

    editor.LinkClicked += this.Editor_LinkClicked;

    editor.Tag = "handlerIsAdded"; //remember for next time
}
Caius Jard
  • 72,509
  • 5
  • 49
  • 80
  • The event at the 3rd party control is at the editor level, but my extension method pretends that it is at the column level – kzfid Apr 08 '20 at 14:02