When do I unsubscribe from events inside Custom Control - c#

I'm creating custom control that contain multiple parts. Inside template creation I'm subscribing for different events like so:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.partAreaCode = this.GetTemplateChild(PartAreaCode) as TextBox;
this.partExchange = this.GetTemplateChild(PartExchange) as TextBox;
this.partSubscriber = this.GetTemplateChild(PartSubscriber) as TextBox;
if (this.partAreaCode == null || this.partExchange == null || this.partSubscriber == null)
{
throw new NullReferenceException("Template part(s) not available");
}
this.partAreaCode.KeyDown += this.AreaKeyDown;
this.partAreaCode.TextChanged += this.AreaTextChanged;
this.partExchange.KeyDown += this.ExchangeKeyDown;
this.partExchange.TextChanged += this.ExchangeTextChanged;
this.partSubscriber.KeyDown += this.SubscriberKeyDown;
// Template might be applied after dependency property set
// lets refresh UI in this case
this.UIFromValue();
}
So, I wonder if I should unsubscribe from those events and if so - where and how?

You don't have to. Because your PART elements are children of the event subscriber. If your main control gets garbage collected, so do your PART elements.
A short example. Lets say you have 2 instances A and B ... A keeps a hard reference to B. If B is only hold in memory by the reference in A and A gets garbage collected so does B. You don't need to clear the reference in A first.
If for some reason you don't want events anymore while your element, with your PART child elements, lives you have to unsubsribe of course.
A Rule of thumb: Always unsubscribe events if the event owner lives longer than the subscriber.

Well you've accepted an answer already and you may be able to get away that approach but its a too risky for my tastes. It assumes that OnApplyTemplate only ever gets called once. Potentially though your custom control may live a long time with OnApplyTemplate getting called more than once.
I'll outline here what hard-core control developers do, I'll just use one TextBox for brevity.
[TemplatePart(Name = MyControl.PartAreaCode, Type = typeof(TextBox))]
public partial class MyControl: Control
{
public MyControl()
{
DefaultStyleKey = typeof(MyControl);
}
#region Template Part Names
private const string PartAreaCode = "AreaCodeTextBox";
#endregion
#region internal TextBox AreaCodeTextBox
private TextBox _AreaCodeTextBox;
internal TextBox AreaCodeTextBox
{
get { return _AreaCodeTextBox; }
set
{
if (_AreaCodeTextBox != null)
{
_AreaCodeTextBox -= AreaCodeTextBox_KeyDown;
_AreaCodeTextBox -= AreaCodeTextBox_TextChanged;
}
_AreaCodeTextBox = value;
if (_AreaCodeTextBox != null)
{
_AreaCodeTextBox += AreaCodeTextBox_KeyDown;
_AreaCodeTextBox += AreaCodeTextBox_TextChanged;
}
}
}
#endregion
public overide void OnApplyTemplate()
{
base.OnApplyTemplate();
AreaCodeTextBox = GetTemplateChild(PartAreaCode) as TextBox;
}
#region Part Event Handlers
// Your event handlers here
#endregion
}
Yes I know that this looks like overkill but the code is boilerplate and we use regions to rollup the repeative stuff so we can inspect code that actually does something interesting without being concerned with the plumbing. With this one instance its easy to roll it out to multiple parts.

Related

Creating custom Label in C# and passing data in events

I had been playing around with an idea for a game, and implementation was going fairly well, but I have hit a stumbling block.
Basically, I have a form, which will show talent trees. I am just going to use labels to display the relevant details, and I want to create them programmatically. The display part is working fine, the part I am having trouble with is adding an event handler to the labels.
I want to be able to pass data during the event handling, so that I can identify which specific label was clicked, but I am hitting a brick wall. So when a particular label is clicked, the name of its associated skill (just passing a string) will be sent to the event handler. Any help would be appreciated. Here is the relevant code that I have:
public void DisplayTree()
{
int i=0;
startPoint.X = 40;
startPoint.Y = 125;
foreach(SkillNode s in tree.tier1)
{
for (i=0; i < s.labels.Count;i++ )
{
//Displays a label for each available rank for a skill
s.labels.ElementAt(i).Text = (i+1).ToString()+"/"+s.maxRank.ToString();
s.labels.ElementAt(i).Location = startPoint;
startPoint.Y += s.labels.ElementAt(i).Height + 2;
s.labels.ElementAt(i).Name = "lbl"+s.name+i.ToString();
//Only enable it to be clicked if the user is at the correct rank
if (s.rank == i)
{
s.labels.ElementAt(i).Enabled = true;
}
else
{
s.labels.ElementAt(i).Enabled = false;
}
//Add Event here
//I want to pass the name of the skill with the event
this.Controls.Add(s.labels.ElementAt(i));
}
startPoint.X += s.title.Width + 5;
startPoint.Y = 125;
}
}
public void LabelClick()
{
//Code here to pick out the name of the label
}
Try this:
public void LabelClick()
{
Console.WriteLine(((Control)sender).Name);
}
When you create an event and want to follow the official C# styleguide, you follow the following pattern:
public delegate void {YourName}EventHandler(object sender, {YourName}EventArgs args);
public event {YourName}EventHandler EventName;
Every information about what happened in the event or can be manipulated by the subscriber is stored in a class that inherits EventArgs. The delegate also contains a reference to the sender, which is the object that fires the event.
When you fire an event you do the following, regularly in a protected method that has the same name as the Event with an "On" as prefix:
EventName?.Invoke(this, new {YourName}EventArgs() { Initialize Stuff });
As you can see, you can work with the sender and identify the object. In your case you could also change object sender to UIElement sender (or similar) to make it easier to identify stuff without a cast.

C# checking if an event delegate is null or not [duplicate]

In my .NET application I am subscribing to events from another class. The subscription is conditional. I am subscribing to events when the control is visible and de-subscribing it when it become invisible. However, in some conditions I do not want to de-subscribe the event even if the control is not visible as I want the result of an operation which is happening on a background thread.
Is there a way through which I can determine if a class has already subscribed to that event?
I know we can do it in the class which will raise that event by checking the event for null, but how do I do it in a class which will subscribe to that event?
The event keyword was explicitly invented to prevent you from doing what you want to do. It restricts access to the underlying delegate object so nobody can directly mess with the events handler subscriptions that it stores. Events are accessors for a delegate, just like a property is an accessor for a field. A property only permits get and set, an event only permits add and remove.
This keeps your code safe, other code can only remove an event handler if it knows the event handler method and the target object. The C# language puts an extra layer of security in place by not allowing you to name the target object.
And WinForms puts an extra layer of security in place so it becomes difficult even if you use Reflection. It stores delegate instances in an EventHandlerList with a secret "cookie" as the key, you'd have to know the cookie to dig the object out of the list.
Well, don't go there. It is trivial to solve your problem with a bit of code on your end:
private bool mSubscribed;
private void Subscribe(bool enabled)
{
if (!enabled) textBox1.VisibleChanged -= textBox1_VisibleChanged;
else if (!mSubscribed) textBox1.VisibleChanged += textBox1_VisibleChanged;
mSubscribed = enabled;
}
Assuming that you have no access to the innards of the class declaring the event, you have no way to do it directly. Events only expose operators += and -=, nothing else. You will need a flag or some other mechanism in your subscribing class to know whether you are already subscribed or not.
/// <summary>
/// Determine if a control has the event visible subscribed to
/// </summary>
/// <param name="controlObject">The control to look for the VisibleChanged event</param>
/// <returns>True if the control is subscribed to a VisibleChanged event, False otherwise</returns>
private bool IsSubscribed(Control controlObject)
{
FieldInfo event_visible_field_info = typeof(Control).GetField("EventVisible",
BindingFlags.Static | BindingFlags.NonPublic);
object object_value = event_visible_field_info.GetValue(controlObject);
PropertyInfo events_property_info = controlObject.GetType().GetProperty("Events",
BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList event_list = (EventHandlerList)events_property_info.GetValue(controlObject, null);
return (event_list[object_value] != null);
}
Simply check whether the control is visible or not whenever the event handler is triggered.
Can you put the decision making logic into the method that fires the event? Assuming you're using Winforms it'd look something like this:
if (MyEvent != null && isCriteriaFulfilled)
{
MyEvent();
}
Where isCriteriaFulfilled is determined by your visible/invisible logic.
// UPDATES /////
Further to your 1st comment would it not make sense to alter the behaviour inside your event handler depending on the value of this.Visible?
a.Delegate += new Delegate(method1);
...
private void method1()
{
if (this.Visible)
// Do Stuff
}
Or if you really have to go with subscribing and unsubscribing:
private Delegate _method1 = null;
...
if(this.visible)
{
if (_method1 == null)
_method1 = new Delegate(method1);
a.Delegate += _method1;
}
else if (_method1 != null)
{
a.Delegate -= _method1;
}
using System;
//...
public event EventHandler Event;
public bool IsSubscribed(EventHandler Delegate)
{
if (Event == null)
{
return false;
}
var InvocationList = Event.GetInvocationList();
foreach (var Entry in InvocationList)
{
if (Entry.Equals(Delegate))
{
return true;
}
}
return false;
}
After 12 years it is here, works for me.
Can't you just remember whether you already subscribed? That approach worked fine for me so far. Even if you have a lot of events or objects, you may still want to just remember that (in a dictionary, for example).
On the other hand, visibility change was, at least for me, not a good point to subscribe/unsubscribe. I typically rather go with construction / Disposed, which are more clear than each time visibility changes.
I'm just expanding on Hans' answer. I'm just trying to ensure that I'm not installing my handler more than once, and not removing it when I still need it. This doesn't protect from a malicious or malfeasant caller unsubscribing repeatedly, for that you'd need to track the callers, and that would just open you up to having repeated subscriptions overrun the tracking mechanism.
// Tracks how many times the ReflectionOnlyResolveHandler has been requested.
private static int _subscribers = 0;
/// <summary>
/// Register or unregister the ReflectionOnlyResolveHandler.
/// </summary>
/// <param name="enable"></param>
public static void SubscribeReflectionOnlyResolve(bool enable)
{
lock(_lock)
{
if (_subscribers > 0 && !enable) _subscribers -= 1;
else if (enable) _subscribers += 1;
if (enable && _subscribers == 1)
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ReflectionHelper.ReflectionOnlyResolveHandler;
else if (_subscribers == 0)
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= ReflectionHelper.ReflectionOnlyResolveHandler;
}
}

Temporarily stop form events from either being raised or being handled?

I have a ton on controls on a form, and there is a specific time when I want to stop all of my events from being handled for the time being. Usually I just do something like this if I don't want certain events handled:
private bool myOpRunning = false;
private void OpFunction()
{
myOpRunning = true;
// do stuff
myOpRunning = false;
}
private void someHandler(object sender, EventArgs e)
{
if (myOpRunning) return;
// otherwise, do things
}
But I have A LOT of handlers I need to update. Just curious if .NET has a quicker way than having to update each handler method.
You will have to create your own mechanism to do this. It's not too bad though. Consider adding another layer of abstraction. For example, a simple class called FilteredEventHandler that checks the state of myOpRunning and either calls the real event handler, or suppresses the event. The class would look something like this:
public sealed class FilteredEventHandler
{
private readonly Func<bool> supressEvent;
private readonly EventHandler realEvent;
public FilteredEventHandler(Func<bool> supressEvent, EventHandler eventToRaise)
{
this.supressEvent = supressEvent;
this.realEvent = eventToRaise;
}
//Checks the "supress" flag and either call the real event handler, or skip it
public void FakeEventHandler(object sender, EventArgs e)
{
if (!this.supressEvent())
{
this.realEvent(sender, e);
}
}
}
Then when you hook up the event, do this:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
When WhateverEvent gets raised, it will call the FilteredEventHandler.FakeEventHandler method. That method will check the flag and either call, or not call the real event handler. This is pretty much logically the same as what you're already doing, but the code that checks the myOpRunning flag is in only one place instead of sprinkled all over your code.
Edit to answer question in the comments:
Now, this example is a bit incomplete. It's a little difficult to unsubscribe from the event completely because you lose the reference to the FilteredEventHandler that's hooked up. For example, you can't do:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
//Some other stuff. . .
this.Control.WhateverEvent -= new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler; //Not gonna work!
because you're hooking up one delegate and unhooking a completely different one! Granted, both delegates are the FakeEventHandler method, but that's an instance method and they belong to two completely different FilteredEventHandler objects.
Somehow, you need to get a reference to the first FilteredEventHandler that you constructed in order to unhook. Something like this would work, but it involves keeping track of a bunch of FilteredEventHandler objects which is probably no better than the original problem you're trying to solve:
FilteredEventHandler filter1 = new FilteredEventHandler(() => myOpRunning, RealEventHandler);
this.Control.WhateverEvent += filter1.FakeEventHandler;
//Code that does other stuff. . .
this.Control.WhateverEvent -= filter1.FakeEventHandler;
What I would do, in this case, is to have the FilteredEventHandler.FakeEventHandler method pass its 'this' reference to the RealEventHandler. This involves changing the signature of the RealEventHandler to either take another parameter:
public void RealEventHandler(object sender, EventArgs e, FilteredEventHandler filter);
or changing it to take an EventArgs subclass that you create that holds a reference to the FilteredEventHandler. This is the better way to do it
public void RealEventHandler(object sender, FilteredEventArgs e);
//Also change the signature of the FilteredEventHandler constructor:
public FilteredEventHandler(Func<bool> supressEvent, EventHandler<FilteredEventArgs> eventToRaise)
{
//. . .
}
//Finally, change the FakeEventHandler method to call the real event and pass a reference to itself
this.realEvent(sender, new FilteredEventArgs(e, this)); //Pass the original event args + a reference to this specific FilteredEventHandler
Now the RealEventHandler that gets called can unsubscribe itself because it has a reference to the correct FilteredEventHandler object that got passed in to its parameters.
My final advice, though is to not do any of this! Neolisk nailed it in the comments. Doing something complicated like this is a sign that there's a problem with the design. It will be difficult for anybody who needs to maintain this code in the future (even you, suprisingly!) to figure out the non-standard plumbing involved.
Usually when you're subscribing to events, you do it once and forget it - especially in a GUI program.
You can do it with reflection ...
public static void UnregisterAllEvents(object objectWithEvents)
{
Type theType = objectWithEvents.GetType();
//Even though the events are public, the FieldInfo associated with them is private
foreach (System.Reflection.FieldInfo field in theType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance))
{
//eventInfo will be null if this is a normal field and not an event.
System.Reflection.EventInfo eventInfo = theType.GetEvent(field.Name);
if (eventInfo != null)
{
MulticastDelegate multicastDelegate = field.GetValue(objectWithEvents) as MulticastDelegate;
if (multicastDelegate != null)
{
foreach (Delegate _delegate in multicastDelegate.GetInvocationList())
{
eventInfo.RemoveEventHandler(objectWithEvents, _delegate);
}
}
}
}
}
You could just disable the container where all these controls are put in. For example, if you put them in a GroupBox or Panel simply use: groupbox.Enabled = false; or panel.Enabled = false;. You could also disable the form From1.Enabled = false; and show a wait cursor. You can still copy and paste these controls in a container other than the form.

C#: Hook up all events from object in single statement

In my domain layer all domain objects emit events (of type InvalidDomainObjectEventHandler) to indicate invalid state when the IsValid property is called.
On an aspx codebehind, I have to manually wire up the events for the domain object like this:
_purchaseOrder.AmountIsNull += new DomainObject.InvalidDomainObjectEventHandler(HandleDomainObjectEvent);
_purchaseOrder.NoReason += new DomainObject.InvalidDomainObjectEventHandler(HandleDomainObjectEvent);
_purchaseOrder.NoSupplier += new DomainObject.InvalidDomainObjectEventHandler(HandleDomainObjectEvent);
_purchaseOrder.BothNewAndExistingSupplier += new DomainObject.InvalidDomainObjectEventHandler(HandleDomainObjectEvent);
Note that the same method is called in each case since the InvalidDomainobjectEventArgs class contains the message to display.
Is there any way I can write a single statement to wire up all events of type InvalidDomainObjectEventHandler in one go?
Thanks
David
I don't think you can do this in a single statement.. But you can make the code more readible like this:
_purchaseOrder.AmountIsNull += HandleDomainObjectEvent;
_purchaseOrder.NoReason += HandleDomainObjectEvent;
_purchaseOrder.NoSupplier += HandleDomainObjectEvent;
_purchaseOrder.BothNewAndExistingSupplier += HandleDomainObjectEvent;
Other than that - seems like the answer's no :(
You can create an aggregate event in some base class (or in some helper class, or in the PurchaseOrder class itself, if you have access to it):
abstract class BaseOrderPage : Page {
PurchaseOrder _purchaseOrder = new PurchaseOrder();
...
public event InvalidDomainObjectEventHandler InvalidDomainObjectEvent {
add {
_purchaseOrder.AmountIsNull += value;
_purchaseOrder.NoReason += value;
_purchaseOrder.NoSupplier += value;
_purchaseOrder.BothNewAndExistingSupplier += value;
}
remove {
_purchaseOrder.AmountIsNull -= value;
_purchaseOrder.NoReason -= value;
_purchaseOrder.NoSupplier -= value;
_purchaseOrder.BothNewAndExistingSupplier -= value;
}
}
}
And then just use it in the derived classes:
InvalidDomainObjectEvent += new DomainObject.InvalidDomainObjectEventHandler(HandleDomainObjectEvent);
C# 2.0 and above:
InvalidDomainObjectEvent += HandleDomainObjectEvent;
I've used this technique successfully to aggregate events of the FileSystemWatcher class.
You can use reflection to do this automatically. I think you want something like this:
public static void WireEvents(object subject)
{
Type type = subject.GetType();
var events = type.GetEvents()
.Where(item => item.EventHandlerType == typeof(InvalidDomainObjectEventHandler));
foreach (EventInfo info in events)
info.AddEventHandler(subject, new InvalidDomainObjectEventHandler(HandleDomainObjectEvent));
}
Then, all you have to do when you create a new object is this:
PurchaseOrder _purchaseOrder = new PurchaseOrder();
HelperClass.WireEvents(_purchaseOrder);
Don't forget that there is a performance penalty with reflection that will be apparent if you create PurchaseOrders and other similar objects in any great numbers.
Edit - other notes: you will need a using System.Reflection directive. As it stands, this code needs C#3 for the var keyword and .net framework 3.5 for the Where() method (and - if it's not automatically generated - using System.Linq;).
As David has done in a later answer, it can be re-written without changing the basic functionality for earlier versions.
I looked at Bob Sammers' suggestion. The compiler wasn't liking the .Where method of the EventInfo[] returned by GetEvents(), but I've changed the code slightly to the following:
private void HookUpEvents()
{
Type purchaseOrderType = typeof (PurchaseOrder);
var events = purchaseOrderType.GetEvents();
foreach (EventInfo info in events)
{
if (info.EventHandlerType == typeof(Kctc.Data.Domain.DomainObject.InvalidDomainObjectEventHandler))
{
info.AddEventHandler(_purchaseOrder, new Kctc.Data.Domain.DomainObject.InvalidDomainObjectEventHandler(HandleDomainObjectEvent));
}
}
}
After I added this method to the page, it all worked absolutely hunky dory. And I can add events to the purchase order object without having to remember to hook them up individually, which is exactly what I wanted.
You could consider to put the event handlers into an interface. Then you attach the interface:
public interface IPurchaseOrderObserver
{
void AmountIsNullEventHandler(WhateverArgs);
void NoReasonEventHandler(WhateverArgs);
void NoSupplierEventHandler(WhateverArgs);
void BothNewAndExistingSupplierEventHandler(WhateverArgs);
}
_purchaseOrder.RegisterObserver(DomainObject);
You either put this four lines into the RegisterObeserver method, or you replace the events and directly call the interfaces.

How to detect if ASP.NET control properties contain DataBinding expressions?

I have a custom control which inherits from System.Web.UI.Control and some of its properties can be declaratively set using databinding expressions. e.g.
<foo:Foo runat="server" MyFoo="<%# this.GetFoo() %>" />
Now, when I do that I need to call .DataBind() on the control (or one of its parents) to evaluate these expressions.
What I would like to be able to do is detect if any properties were set this way and just automatically have the custom control call this.DataBind() after OnPreRender or there about.
So the question: how do I detect if databinding expressions are waiting to be executed?
I'm convinced that in some ControlBuilder or DataBindContext class lives the information needed to determine this. I've hunted around with Reflector and cannot seem to find it.
I should add, that I don't want to pay the overhead of executing DataBind() if no direct properties have been assigned this way. This is why I'd like to detect before hand. This class is extremely light but I'd like the ability to declaratively set properties without needing any code behind.
Doing some deeper looking into ControlBuilder, I noticed that the compiled factory for each control instance will attach a DataBinding event handler when there are data binding expressions present. I've found that checking for this seems to be a very reliable method for determining if data binding needs to occur. Here is the basis of my solution to the problem:
using System;
using System.Reflection;
using System.Web.UI;
public class AutoDataBindControl : Control
{
private static readonly object EventDataBinding;
private bool needsDataBinding = false;
static AutoDataBindControl()
{
try
{
FieldInfo field = typeof(Control).GetField(
"EventDataBinding",
BindingFlags.NonPublic|BindingFlags.Static);
if (field != null)
{
AutoDataBindControl.EventDataBinding = field.GetValue(null);
}
}
catch { }
if (AutoDataBindControl.EventDataBinding == null)
{
// effectively disables the auto-binding feature
AutoDataBindControl.EventDataBinding = new object();
}
}
protected override void DataBind(bool raiseOnDataBinding)
{
base.DataBind(raiseOnDataBinding);
// flag that databinding has taken place
this.needsDataBinding = false;
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
// check for the presence of DataBinding event handler
if (this.HasEvents())
{
EventHandler handler = this.Events[AutoDataBindControl.EventDataBinding] as EventHandler;
if (handler != null)
{
// flag that databinding is needed
this.needsDataBinding = true;
this.Page.PreRenderComplete += new EventHandler(this.OnPreRenderComplete);
}
}
}
void OnPreRenderComplete(object sender, EventArgs e)
{
// DataBind only if needed
if (this.needsDataBinding)
{
this.DataBind();
}
}
}
This solution disables itself if no DataBinding event handler is attached or if the control is manually data bound (directly or via a parent).
Note that most of this code is just jumping through hoops to be able to test for the existence of the event. The only reflection needed is a one-time lookup to get the object used as the key for EventDataBinding.
There is an internal ArrayList called SubBuilders on the ControlBuilder class. For each databinding expression TemplateParser enocunters, ProcessCodeBlock() adds a CodeBlockBuilder object with a BlockType property CodeBlockType.DataBinding to SubBuilders.
So if you can get a handle to the ControlBuilder you want, you should be able to reflectively iterate over SubBuilders and look for objects of type CodeBlockBuilder where BlockType == CodeBlockType.DataBinding.
Note of course this is all kinds of nasty and I'm really suspicious this is the best way to solve your core problem. If you take two steps back and look at the original problem, maybe post that on Stackoverflow instead - there's plenty of super-smart people who can help come up with a good solution.

Categories