Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Am I using the right approach to monitor the tasks I want to perform when a handle is created?

Is there a generally-accepted best practice for creating an event handler that unsubscribes itself?

E.g., the first thing I came up with is something like:

// Foo.cs

// ...
Bar bar = new Bar(/* add'l req'd state */);
EventHandler handler = new EventHandler(bar.HandlerMethod);

bar.HandlerToUnsubscribe = handler;

eventSource.EventName += handler;
// ...

// Bar.cs

class Bar
{
    /* add'l req'd state */

    // .ctor

    public EventHandler HandlerToUnsubscribe { get; set; }

    public void HandlerMethod(object sender, EventArgs args)
    {
        // Do what must be done w/ add'l req'd state
        ((EventSourceType)sender).EventName -= this.HandlerToUnsubscribe;
    }
}

To say that this feels hackish/bad is an understatement. It's tightly coupled with a temporal dependency (HandlerToUnsubscribe must be assigned the exact value at the exact right time). I feel like I must be playing the part of a complicator in this case--- is there something stupid or simple that I'm missing?

Context:

I'm creating a binding between the UI and a proprietary commanding infrastructure in Winforms (using the useful ICommand in System.Windows.Input). One aspect of the binding infrastructure is that users who create a binding between a UI command component (like a toolbar button or menu item) have the option to listen to the command's CanExecuteChanged event and then update the UI's state based on that-- typically setting the Enabled property to true or false.

The technique generally works quite well, but there are ways for the event to be fired prior to a ui component's handle having been created. I'm trying to guarantee that the provided handler isn't run unless the handle has been created. Resultantly, I'm considering providing a general helper class ("Bar") that will aid implementation. The goal of Bar is to check to see if the appropriate handle exists. If so, great! If not, it will subscribe to appropriate IsHandleCreated event so that the supplied handlers get run when the handle eventually is created. (This is important b/c the client may set their bindings in the UI's .ctor, before a handle exists.) I want this subscription to be completely transparent, however, and so I also want each event handler to automatically unsubscribe itself from IsHandleCreated once it's finished running.

I'm still at a point where I'm trying to figure out if this is a good idea, so I haven't generalized the concept yet-- I've only implemented it directly against ToolStripItems in this case to verify that the idea is sound. I'm not sold on it yet, though.

I understand that I also have the option of simply mandating that bindings can only be created once the UI's handle has been created, in the OnLoad event of a form (e.g.). I know that can work, I've done it in the past. I'd like to see if I can ease that particular requirement in this case though. If it's even practical.

like image 722
Greg D Avatar asked Oct 12 '09 14:10

Greg D


4 Answers

Greg,

What you have is not an observer pattern, but rather a message queue. So you're just using the wrong design pattern for the problem you're trying to solve.

Its easy enough to implement your own message queue from scratch using a Queue{Action{object}}, where objects enqueue themselves, and you simply dequeue items as you invoke them.

like image 141
Juliet Avatar answered Oct 19 '22 14:10

Juliet


The usual way would be to store a boolean value as to whether it should run...

bool runMyEvent = true;

void Handler(object sender, EventArgs e) {
   if (runMyEvent) {
      // handler here
      runMyEvent = false;
   } else {
      return;
   }
}
like image 20
Fenton Avatar answered Oct 19 '22 14:10

Fenton


You can use the run once method here, mentioned a few times here, but there are a couple problems with that, depending on your use case.

1) You may want to re-hook the method later again, and have it run once again. Though I suppose you can reset your bool

2) You still have that reference which could end up keeping your class in memory instead of being garbage collected.

One option is to use an anonymous method and a closure when you define the event handling:

public class Foo
{
    public EventHandler<EventArgs> MyEvent;

    public void FireEvent()
    {
        if(MyEvent != null)
            MyEvent(this, EventArgs.Empty);
    }
}

Foo obj = new Foo();

Action<object, EventArgs> action = new Action<object, EventArgs>((sender, args) =>
{
    // We're now running the event handler, so unsubscribe
    obj.MyEvent -= new EventHandler<EventArgs>(action);

    // Do whatever you wanted to do when the event fired.
});

obj.MyEvent += new EventHandler<EventArgs>(action);
like image 1
Matt Avatar answered Oct 19 '22 13:10

Matt


how about a handler that runs only once? Something like this:


if (wasRun)
    return;
wasRun = true;

like image 1
Michał Piaskowski Avatar answered Oct 19 '22 12:10

Michał Piaskowski