Use 'custom' parameters in function, for entry widget (GTK#) - c#

Is there a way that I am able to use 'custom' parameters with a function that is used when the value in an entry widget is changed.
the basic function is:
public void(object sender, EventArgs args){
Entry ent1 = (Entry)sender;
}
what I tried:
public void onChange(object sender, EventArgs args, String name){
Entry ent1 = (Entry)sender;
}
but It would run, because when I was calling the function it was:
entry1 += onChange("testname");
which only passes one paramter to the function, when it needs three.
What I'm asking is how can I use a 'custom' paramter like 'name', and still pass the values for 'sender' and 'args'?

You will have to create a delegate that wraps code that passes that extra parameter.
You should be able to do it with this syntax:
entry1 += (s, e) => onChange(s, e, "testname");
However, this will make you unable to unsubscribe that event. If you need that, you will have to store the delegate:
ChangeEventHandler onchange = (s, e) => onChange(s, e, "testname");
entry1 += onchange;
...
entry1 -= onchange;

Related

Using the C# Lambda Operator

I am trying to understand how the lambda operator in C# is used when the given parameters are formatted in ( ), like so:
_backgroundVideoWorker.DoWork += (s, e) =>
{
outputFile = _videoEditor.JoinVideo(selectedConfiguration, videoFiles);
};
My main goal from asking this question is to understand how the different operators are being used with the lambda, += (s, e) =>.
For reference, this code excerpt is taken from an application that is joining two video files together by using the FFMPEG utility.
_backgroundVideoWorker.DoWork is an event that needs handlers.
+= indicates we are adding an event handler to handle the event.
(s, e) is saying that the expression is creating a function which accepts the s and e parameters required by the event for a function to handle it. These represent an object (s) and the DoWorkEventArgs (e). If you were to write a full function block instead of an inline lambda, they would look like (object s, DoWorkEventArgs e) or more familiarly (object sender, DoWorkEventArgs e);
BackgroudWorker.DoWork is an event with a delegate type of DoWorkEventHandler, which has the signature:
public delegate void DoWorkEventHandler(object sender, DoWorkEventArgs e)
which means that DoWork would be a function that receives two parameters, of type object and DoWorkEventArgs, respectively.
The event handler has to follow the same signature as the event's delegate, therefore, when you speficy (s, e) => { /*..*/ } as the event handler, the compiler will assume that s corresponds to the object sender parameter, while e corresponds to the DoWorkEventargs e parameter. Of course, you don't have to name them s and e

Change order of the Event

I have a control (button) with a messagebox containing the text "Test A" and added a delegate with another message "Test B". The code below first shows the messge "Test A`" and then "Test B":
c.Click += delegate(object sender, EventArgs e)
{
MessageBox.Show("Test B");
};
I need something similar to this code I did that is not working properly:
EventHandler handler;
c.click -= handler;
handler = delegate(Object sender, EventArgs e)
{
MessageBox.Show("Test B");
};
c.Click += handler;
I need to change the sequence, that is, show the message "Test B", then "Test A". Is it possible?
You can unregister and register it again:
button1.Click -= button1_Click; // unregister main event
button1.Click += delegate(object sender, EventArgs e) // reigster new one
{
MessageBox.Show("Test B");
};
button1.Click += button1_Click; // register again old
Button click Event:
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Test A");
}
Now it will call Call Test B first and secondly Test A
You mentioned that your program contains a compiler of some sort, and that the user is able to specify whether their code should run before existing code or after. It seems to me that you can simply handle the users code in an event handler of your own, which runs both your and the users code, in the desired order (as Nikola already mentioned).
In other words, don't expose these events to your users code directly, but 'wrap' them with an event handler of your own. Nothing changes from the users point of view - they still think they've got direct access to the event. But you control how you process your users code, so you're free to insert a layer in-between to guarantee a correct execution order:
c.Click += (sender, e) =>
{
if (hasUserEventHandler && runUserEventHandlerFirst)
UserEventHandler(sender, e);
StandardEventHandler(sender, e);
if (hasUserEventHandler && !runUserEventHandlerFirst)
UserEventHandler(sender, e);
};
I mean, you could do something like this:
public void SwapDelegateOrder(object obj, string eventName)
{
// Fetch the underlying event handler field (let's *assume* it has the same name):
var delegateField = obj.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var mcDelegate = delegateField.GetValue(obj) as MulticastDelegate;
if (mcDelegate == null)
return;
// Get the add and remove methods:
var evt = obj.GetType().GetEvent(eventName);
var add = evt.GetAddMethod(true);
var remove = evt.GetRemoveMethod(true);
// Remove all invocations...
var invocations = mcDelegate.GetInvocationList();
foreach (var invocation in invocations)
remove.Invoke(obj, new object[] { invocation });
// ...and add them back in, in reverse order (*assuming* that invocations are called in the order they're added):
foreach (var invocation in invocations.Reverse())
add.Invoke(obj, new object[] { invocation });
}
But that's based on assumptions about compiler and runtime implementation (event field name and execution order). What if some of these details are changed in a compiler update or in the next .NET framework version? You don't have control over that, but you do have control over your own code.

pass method to event handler in C# WPF

This works in adding an event handler in C# WPF
CheckBox ifPrint = new CheckBox();
ifPrint.AddHandler(CheckBox.ClickEvent, new RoutedEventHandler(
(sender, e) => //toggle check box event
{
//do stuff
}));
but it looks messy when the method body gets long, so I want to define the method elsewhere like this
ifPrint.AddHandler(CheckBox.ClickEvent, delegate(object sender, RoutedEventArgs e){
checkBoxClick(sender, e);
});
private void checkBoxClick(object sender, RoutedEventArgs e)
{
//do stuff
}
but this doesn't even compile with the error: Cannot convert anonymous type to type 'System.Delegate' because it is not a delegate type
Sorry, I am new to this and have no idea how it's supposed to be done. Is this even close? Thanks!
You can subscribe to a separate method like this, as long as the signature of checkBoxClick is correct:
ifPrint.Click += checkBoxClick;
You can also subscribe to an event inline like this:
ifPrint.Click += (s, e) => SomeMethod();
Which then allows you to name your method something more reasonable and not require it to accept parameters:
private void SomeMethod()
{
//do stuff
}
Just to explain it a little further, in the above code, s and e take the place of the parameters in your checkBoxClick event method, so it's basically equivalent to this:
ifPrint.Click += checkBoxClick;
private void checkBoxClick(object sender, RoutedEventArgs e)
{
SomeMethod();
}
Edit, in regards to your comment.
Given this is much simpler, when, if ever, should one use this? ifPrint.AddHandler(CheckBox.ClickEvent, new RoutedEventHandler( (sender, e) => { //do stuff }));
I honestly don't think I've ever used that syntax.
It seems that in most cases it does the same thing. According to the MSDN docs, there's a handledEventsToo parameter on the AddHandler() method, which I think could be significant.
Imagine you subscribed to an event multiple times, like this:
ifPrint.Click += checkBoxClick;
ifPrint.Click += checkBoxClick;
ifPrint.Click += checkBoxClick;
And inside your event, you set e.Handled = true. If you didn't have that line, you'd see the message box displayed 3 times. But with that line, you only get the message box once, because the first time the event fires, it marks the event "handled".
private void checkBoxClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("Clicked!");
e.Handled = true;
}
By passing in true for the last parameter (it's false by default), you actually tell it to fire that event, even if other events already "handled" the event.
ifPrint.AddHandler(CheckBox.ClickEvent,
new RoutedEventHandler((s, e) => { /* do stuff */ }), true);
try this logic to attach click event handler for your checkbox.
CheckBox ifPrint = new CheckBox();
ifPrint.Click+=checkBoxClick;
private void checkBoxClick(object sender, RoutedEventArgs e)
{
//do stuff
}

EventHandler with a different type of sender

I'm trying to implement drag and drop for a specific object of a Type that I've created in c# for windows phone 8. I'm using Manipulation Events like this :
deck[r[i, j]].card.ManipulationCompleted += new EventHandler<ManipulationCompletedEventArgs>(ImageManipulationCompleted);
private void ImageManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
//something
}
How can I change object to the type that I want ?
keyboardP's solution will work just fine. But I personally prefer to store the information I need in the Tag property of the control, which has been designed for this very purpose.
deck[r[i, j]].card.Tag = deck[r[i, j]];
deck[r[i, j]].card.ManipulationCompleted += ImageManipulationCompleted;
private void ImageManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
var deck = (Deck)((FrameworkElement)sender).Tag;
}
The good side of keyboardP's approach is that, since you receive directly the desired object as a parameter of your method, it's easier to read. The downside is that you have to declare a custom delegate for every event you need, and you lose the ability to assign event handlers directly from the XAML. My solution is a bit harder to read, but addresses this point.
In the end, which solution is better really depends on your tastes and your needs.
What you could do is just call a method that takes in your type instead of using the standard ImageManipulationCompleted handler. I don't know what the deck[r[i, j]] type is but you can replace MyType below with the correct type.
deck[r[i, j]].card.ManipulationCompleted += delegate(object s, ManipulationCompletedEventArgs e){ CardManipulated(s, e, deck[r[i, j]]); };
private void CardManipulated(object sender, ManipulationCompletedEventArgs e, MyType selectedObject)
{
//you know have access to selectedObject which is of type deck[r[i, j]],
//the ManipluationCompletedEvents properties if needed,
//and the actual card Image object (sender).
}
You cant.
Since you are subscribing to an event with this code new EventHandler<>(..), you cannot change the type of sender because in the description of EventHandler<> there is only object sender:
public delegate EventHandler<T>(object sender, T eventArgs) where T : EventArgs
If you need to create your own delegate, you could make a factory or simply write this:
public delegate EventHandler<T, TArgs>(T sender, TArgs eventArgs) where TTArgs : EventArgs
ManipulationCompletedEventHandler signature is using object in its first parameter
public delegate void ManipulationCompletedEventHandler(object sender,
ManipulationCompletedRoutedEventArgs e);
So, you can't change the signature but you can use delegate to typecast object always to your type like this -
deck[r[i, j]].card.ManipulationCompleted += (s, e) =>
ManipulateMe_ManipulationCompleted((YourType)s, e);
private void ImageManipulationCompleted(YourType sender,
ManipulationCompletedEventArgs e)
{
//something
}
Replace YourType with your desired Type (TextBox or something whichever you want)

No overload for 'dateTimePicker1_ValueChanged' matches delegate 'System.EventHandler'

private void dateTimePicker1_ValueChanged(object sender, Series myseries, int multiplier, EventArgs e)
{
if (datelimitsset == 1) {
var dt1 = dateTimePicker1.Value;
chart1.Series.Clear();
for (int i = 0; i < multiplier; i++)
{
config();
myseries.Points.AddXY(Convert.ToString(date[i]), Convert.ToDouble(array[i]));
string[] rowi = { Convert.ToString(date[i]), Convert.ToString(array[i]) };
dataGridView1.Rows.Add(rowi);
}
}
}
This is giving me the error:
No overload for 'dateTimePicker1_ValueChanged' matches delegate 'System.EventHandler'
I do not fully understand event handlers, can anyone give me advice?
The signature for System.EventHandler is (object sender, EventArgs e) so you either need to change your method signature to this:
private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
Or keep your current signature and use a lambda expression as a delegate adapter when you subscribe to the event:
dateTimePicker1.ValueChanged += (object sender, EventArgs e) =>
dateTimePicker1_ValueChanged(sender, [your Series variable], [your int multiplier variable], e);
When you use a lambda expression as a delegate adapter, you are essentially creating a delegate which conforms to the System.EventHandler signature (it is passed an object and an EventArgs argument), which then calls your original handler method passing all of the arguments required to satisfy your dateTimePicker1_ValueChanged method.
The reference documentation for the System.EventHandler delegate.
EDIT: documentation for an example handler for the DateTimePicker.ValueChanged event
It's because your handler must have the same signature specified by the EventHandler delegate.
That is, you'll have to remove your two middle parameters:
private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{
}
In terms of a workaround for passing these parameters into the function, you have a few options...
Generate the event handler as an anonymous function (as per James' answer)
Store and retrieve them from instance variables
Store them on the DateTimePicker control's Tag property and resolve them in the handler
The second option should be obvious enough...
The third option might look like:
// In control initialization somewhere
dateTimePicker1.Tag = new DateTimePickerParams() { Series = myseries, Multiplier = multiplier }; // Where DateTimePickerParams is your own private class/struct defined explicitly for this purpose...
private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{
var ctl = sender as DateTimePicker;
var parameters = ctl.Tag as DateTimePickerParams;
var mySeries = parameters.Series;
var multiplier = parameters.Multiplier;
// Execute...
}
You can't add arbitrary parameters to the event handler like that, the method signature must match the event delegate. How would the DateTimePicker know what to pass for the myseries and multiplier parameters?

Categories

Resources