Passing value using an event - c#

So, first I generate a List containing custom usercontrols made of a button and progressbar, I generate this using a for loop.
Inside this loop I send each events to the desired methods, now what I need is access to the progress bar inside of the reset method, how do I do that?
ProgressTimerList[i].Button.Reset += Button_Reset;
ProgressTimerList[i].Progressbar //////Need access to this object
And
void Button_Reset(object sender, EventArgs e)
{
//////Inside of here
}

Create a class inherited from EventArgs with a property of type Progressbar and pass it to the handler:
public class MyButtonEventArgs : EventArgs{
public --WhateverProgressbarTypeIs-- Bar {get;set;}
}
ProgressTimerList[i].Button.Reset += (sender, e) => Button_Reset(sender, new MyEventArgs { Bar = ProgressTimerList[i].Progressbar });
void Button_Reset(object sender, MyButtonEventArgs e)
{
var wunderBar = e.Bar;
}

By far the easiest way to handle this is to use anonymous methods.
At the point in your code where you are attaching the handler, try this:
ProgressTimerList[i].Button.Reset += (s, e) =>
{
//////Inside of here
ProgressTimerList[i].Progressbar //////Can access this object
};
No need whatsoever for the Button_Reset method.
The other nice thing is that this encapsulates the event handling within a method so that other code can't directly call Button_Reset. As encapsulation is one of the four pillars of OOP this helps to make your code more robust.
If you need to detach the handler you can do this:
EventHandler button_reset = (s, e) =>
{
//////Inside of here
ProgressTimerList[i].Progressbar; //////Can access this object
///more code
///detach
ProgressTimerList[i].Button.Reset -= button_reset;
};
ProgressTimerList[i].Button.Reset += button_reset;
If you have a clash with the name of e within your MainForm_Load then just call it e2 instead.
One other gotcha you might hit is that you're accessing items in an array within your event handler. You probably need to capture the variable locally before using it in the handler.
Like this:
for (var i = 0; i < ProgressTimerList.Count(); i++)
{
var local_i = i;
EventHandler button_reset = (s, e) =>
{
//////Inside of here
ProgressTimerList[local_i].Progressbar; //////Can access this object
///more code
///detach
ProgressTimerList[local_i].Button.Reset -= button_reset;
};
ProgressTimerList[i].Button.Reset += button_reset;
}

((ProgressTimerListType)((Button)sender).Parent).ProgressBar
Solved it using this thanks to Ron Beyer! Thanks!
If someone is up for more detail can I ask, why do I need to cast the sender before I can use it as a Button, not just use sender.Parent?

Related

How to remove ViewTreeObserver in Xamarin?

Let's just say I need to get and set a View's height. In Android, it's known you can get a view height only after it's drawn. If you're using Java, many answers, one of the most well-known way is like this one below, taken from this answer:
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
view.getHeight(); //height is ready
}
});
Thus I search C#/Xamarin version, and found this works:
int viewHeight = 0;
ViewTreeObserver vto = view.ViewTreeObserver;
vto.GlobalLayout += (sender, args) =>
{
viewHeight = view.Height;
};
Thing is, it fired again and again. In Java version, it can be removed with
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
How to do it in C#\Xamarin? Should I resort to using boolean properties to know whether it's executed or not? Is there not way to do it like the android one?
If you are using C# Events, avoid using anonymous events if you need to unsubscribe, or you can implement the IOnGlobalLayoutListener and add/remove the listener:
C# EventHandler Style:
Create an EventHandler method for the event to invoke:
void Globallayout_handler(object sender, EventArgs e)
{
// ViewTreeObserver.IOnGlobalLayoutListener events
}
Subscribe:
var viewTreeObserver = aView.ViewTreeObserver;
viewTreeObserver.GlobalLayout += Globallayout_handler;
Unsubscribe:
var viewTreeObserver = aView.ViewTreeObserver;
viewTreeObserver.GlobalLayout -= Globallayout_handler;
Java Listener Style in C#:
Add and implement ViewTreeObserver.IOnGlobalLayoutListener:
public class CustomButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer,
ViewTreeObserver.IOnGlobalLayoutListener
{
~~~~
public void OnGlobalLayout()
{
// ViewTreeObserver.IOnGlobalLayoutListener events
}
}
Now you can use the Java way to add and remove this listener:
aView.ViewTreeObserver.RemoveOnGlobalLayoutListener(this);
aView.ViewTreeObserver.AddOnGlobalLayoutListener(this);
Even though the answer given by ShshiHangover is correct in principle, the unsubscribing didn't work for me as expected (using the regular method #1).
The reason is probably that the ViewTreeObserver in the called method can be different from the one the event handler subscribed to, so removing it may not work (i.e., the handler method is called continuously).
The correct way of doing this is to unsubscribe from the event sender object while ensuring that IsAlive yields true:
void ViewTreeObserver_GlobalLayout(object sender, EventArgs e)
{
ViewTreeObserver vto = (ViewTreeObserver)sender;
if (vto.IsAlive) {
vto.GlobalLayout -= ViewTreeObserver_GlobalLayout;
}
}
Neither #Daniel or #SushiHangover methods would actually unsubscribe for me (maybe an sdk bug?). My only solution was to set a bool flag on first run. It would be nice to know how to actually unsubscribe however...
Getting the ViewTreeObserver via sender never seems to be IsAlive whereas getting the tree from the View does. However either way the event doesn't get properly removed.
private void Setup()
{
cameraView = FindViewById<SurfaceView>(Resource.Id.camera_view);
//need to wait for view to inflate to get size
isSetup = false;
ViewTreeObserver vto = cameraView.ViewTreeObserver;
vto.GlobalLayout += Vto_GlobalLayout;
}
void Vto_GlobalLayout(object sender, System.EventArgs e)
{
//this didn't work either
//ViewTreeObserver vto = cameraView.ViewTreeObserver;
//vto.GlobalLayout -= Vto_GlobalLayout;
ViewTreeObserver vto = (ViewTreeObserver)sender;
if (vto.IsAlive)
vto.GlobalLayout -= Vto_GlobalLayout; //even after removing it seems to continue to fire...
if (!isSetup)
{
isSetup = true;
DoYourCodeNow();
}
}

C# pass additional parameter to an event handler while binding the event at the run time

I have a link button which have a regular click event :
protected void lnkSynEvent_Click(object sender, EventArgs e)
{
}
And I bind this event at the runtime :
lnkSynEvent.Click += new EventHandler(lnkSynEvent_Click);
Now I need the function to accept additional argument:
protected void lnkSynEvent_Click(object sender, EventArgs e, DataTable dataT)
{
}
And pass the same as parameter while binding this event :
lnkSynEvent.Click += new EventHandler(lnkSynEvent_Click, //somehow here);
Not sure how to achieve this.
Please help.
Thanks in advance.
Vishal
You can use anonymous delegate for that:
lnkSynEvent.Click +=
new EventHandler((s,e)=>lnkSynEvent_Click(s, e, your_parameter));
I don't know exactly when it's changed, but now it's even easier!
lnkSynEvent.Click += (s,e) => lnkSynEvent_Click(s, e, your_parameter);
All answers above seem to be fine, but I have discovered one pitfall which is not so obvious and it took some time to figure out what is going on, so I wanted to share it.
Assume that myList.Count returns 16.
In the following case, OnValueChangeWithIndex(p1, p2, i) will always be called with i = 16.
for (int i = 0; i < myList.Count; i++)
{
myList[i].OnValueChange += (p1, p2) => OnValueChangeWithIndex(p1, p2, i);
}
To avoid that, you would need to initialize a new variable inside of the loop, then pass the new variable to the function.
for (int i = 0; i < myList.Count; i++)
{
int index = i;
myList[i].OnValueChange += (p1, p2) => OnValueChangeWithIndex(p1, p2, index);
}
Closures close over variables, not over values.
Closing over the loop variable considered harmful, part one
by use of delegate:
lnkbtnDel.Click += delegate(object s, EventArgs e1) {
Dynamic_Click(s, e1, lnkbtnDel.ID);
};`
EventHandler myEvent = (sender, e) => MyMethod(myParameter);//my delegate
myButton.Click += myEvent;//suscribe
myButton.Click -= myEvent;//unsuscribe
private void MyMethod(MyParameterType myParameter)
{
//Do something
}

Multiple Invokes off of one Delegate

At the moment I am in the process of building a custom button handler (I needed to integrate the kinect into the button system which also used a mouse) then I got to a horrible thing called Event Handling.. at least an hour yelling at my pc :P. I was wondering, before I go and spend a while changing my system to allow for my new want, which is to have multiple events per handler, I was wondering, is the way I'm going to try work (I would just try, but I'm getting off for the night, so my hope is that I can save some time when I boot the computer up tomorrow and not attempt if my system isn't designed for it)
Also, ive seen a getInvoc list or somthing like that before when I was coding.. Would I add multiple delegates onto it then get that list and itterate over it?
On previous examples I had seen where people used:
public event EventHandler myEventHandler;
I had to use:
private Dictionary<BtnEvent, Delegate> m_events;
and then they did the following to add a handler (their way, not mine):
myObj.myEventHandler += delegate(object sender, EventArgs ea)
{
//do stuff on event
};
first.. If they ran this twice, once with funcA and second with funcb would it run both? or just one?
second, if I applied that logic of += to a Delegate would it work? (I had to use Delegate as I was storing the handlers inside of a dictionary, this allowed for logical access to handlers through use of an enum)
(my code)
private Dictionary<BtnEvent, Delegate> m_events;
//....
m_events = new Dictionary<BtnEvent, Delegate>(6);
m_events.Add(BtnEvent.CLICK_ENTER, null);
m_events.Add(BtnEvent.CLICK_LEAVE, null);
m_events.Add(BtnEvent.CLICK_STAY, null);
m_events.Add(BtnEvent.HOVER_ENTER, null);
m_events.Add(BtnEvent.HOVER_LEAVE, null);
m_events.Add(BtnEvent.HOVER_STAY, null);
//....
public bool addHandle(BtnEvent stateToGet, Delegate function)
{
bool success = false;
if(m_events.ContainsKey(stateToGet))
{
m_events[stateToGet] = function;
}
return(success);
}
// CHANGE ABOVE TO:
public bool addHandle(BtnEvent stateToGet, Delegate function)
{
bool success = false;
if(m_events.ContainsKey(stateToGet))
{
m_events[stateToGet] += function;
}
return(success);
}
Will changing m_events[stateToGet] = function; to m_events[stateToGet] += function; allow me to have multiple event handles (functions I passed to addHandle) be called through the following code?
private void ExecuteEvent(BtnEvent currEvent)
{
if(m_events.ContainsKey(currEvent))
{
if(m_events[currEvent] != null)
{
m_events[currEvent].DynamicInvoke(null);
}
}
}
Please see below code which answers your first question:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Load += new EventHandler(Form1_Load);
}
void Form1_Load(object sender, EventArgs e)
{
funcA();
funcB();
}
private void funcA()
{
button1.Click += new EventHandler(button1_Click);
}
private void funcB()
{
button1.Click += new EventHandler(button1_Click);
}
void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("I am in event handler");
}
}
On clicking the Button, "I am in event handler" message is shown twice which means += operator works in similar way with delegates as it works with integers or strings. It simply adds the function handler to the queue and upon execution of events, calls all the function pointers in queue.
Regarding your second question, I think you wont achieve the expected behavior by changing = to +=. What I understand from your statement is that, you wish to execute multiple events handlers like CLICK_ENTER, CLICK_LEAVE on calling ExecuteEvent() function. However, since you are storing event handlers and their delegates in a Dictionary, changing = to += will only work in the same way as illustrated in above code.

Removing anonymous event handler

I have the following code where SprintServiceClient is a reference to a WCF Service-
public class OnlineService
{
private SprintServiceClient _client;
public OnlineService()
{
_client = new SprintServiceClient();
}
public void AddMemberToTeam(MemberModel user, int projectId, Action<int> callback)
{
_client.AddMemberToTeamCompleted += (s, e) => callback(e.Result);
_client.AddMemberToTeamAsync(user.ToUser(), projectId);
}
}
the problem is that every time AddMemberToTeam is called it adds another callback to client.AddMemberToTeamCompleted
i.e the first time AddMemberToTeam is called the callback is called once, the second time AddMemberToTeam is called the callback is called twice ect.
Is there any way to remove the eventhandler from AddMemberToTeamCompleted once the eventhandler has been called or use another method which takes in the callback?
You can refer to your anonymous method from inside itself as long as you assign a delegate to a variable first:
EventHandler<SomeEventArgs> handler = null;
handler = (s, e) =>
{
_client.AddMemberToTeamCompleted -= handler;
callback(e.Result);
};
_client.AddMemberToTeamCompleted += handler;
Note that you need to declare the variable and assign it separately or the compiler will deem it uninitialized when you come to use it inside the method body.
The trick to making a self-unsubscribing event-handler is to capture the handler itself so you can use it in a -=. There is a problem of declaration and definite assignment, though; so we can't do something like:
EventHandler handler = (s, e) => {
callback(e.Result);
_client.AddMemberToTeamCompleted -= handler; // <===== not yet defined
};
So instead we initialize to null first, so the declaration is before the usage, and it has a known value (null) before first used:
EventHandler handler = null;
handler = (s, e) => {
callback(e.Result);
_client.AddMemberToTeamCompleted -= handler;
};
_client.AddMemberToTeamCompleted += handler;
No there is no way,
Apparantly Tim and Marc have another nice solution
But you can always just name them, and do the -= on the named eventhandler on this method ;)
Guessing your event:
_client.AddMemberToTeamCompleted += OnAddMemberToTeamCompleted;
and
public void OnAddMemberToTeamCompleted(object sender, EventArgs args)
{
_client.AddMemberToTeamCompleted -= OnAddMemberToTeamCompleted;
callback(e.Result)
}
Next problem is getting this callback in your listener. Perhaps putting it on a Property in the EventArgs (but that feels kinda dirty, I agree)

How can I find a parameter's original variable name as a string at runtime?

I got in Form_Load :
for (int i = 1; i < 10; i++)
{
this.Controls["txtPrix"+i].Enter += new EventHandler(DlgFacture_Enter);
}
I got the event :
void DlgFacture_Enter(object sender, EventArgs e)
{
this.setTextBoxPrixEnter((TextBox)sender);
}
In that last event, I want to be able to print the TextBox root variable name (thus txtPrix1 to txtPrix10) as a string, depending on which one calls the event.
How can that be done ?
You mean the Name property?
this.setTextBoxPrixEnter(((TextBox)sender).Name);
Not easily. What you're attempting to do is print the name of a reference to an object at runtime. There can be many references to a given object so printing one in the abstract is not possible.
What you would have to do is wrap the Enter handler with a new method that captures the variable name and passes it along.
For example:
var name = "txtPrix"+i;
Controls[name].Enter += (sender,e) => DlgFacture_Enter(name, sender, e);
void DlgFacture_Enter(string name, object sender)
{
this.setTextBoxPrixEnter((TextBox)sender);
}

Categories