C#: Get which button was pressed in an array of gtk#-buttons? - c#

I have an array of buttons which shall all call the same method but with the index of the button as an argument.
using System;
using Gtk;
public class Input : Gtk.Window {
private Gtk.Button[] plus;
public Input() : base(Gtk.WindowType.Toplevel) {
plus = new Button[10];
[...]
for (uint i = 0; i < 10; i++) {
plus[i] = new Button();
plus[i].Name = i.ToString();
plus[i].ButtonPressEvent += AddButtonPressed;
}
}
I tried it using this method, but it seems it gets not even called as there is no output:
protected virtual void AddButtonPressed(object sender, System.EventArgs e) {
Console.WriteLine("Button pressed");
for (uint i = 0; i < plus.Length; i++) {
if (sender.Equals(plus[i])) {
uint index = i;
i = (uint)plus.Length;
Console.WriteLine(index);
}
}
Maybe someone can point me in the right direction?
Thanks.

Quick answer:
[GLib.ConnectBefore]
protected virtual void AddButtonPressed(object sender, EventArgs e) {
Console.WriteLine("Button pressed");
for (uint i = 0; i < plus.Length; i++) {
if (sender.Equals(plus[i])) {
uint index = i;
i = (uint)plus.Length;
Console.WriteLine(index);
}
}
}
Rambling explanation:
This is actually an interesting question. It took a bit of searching to find, but GTK#'s FAQ (but I guess not frequently linked to) says,
"As of release 0.15, Gtk# started
using the CONNECT_AFTER flag when
connecting event handlers to signals.
This means that the event handlers are
not run until after the default signal
handlers, which means that the widget
will be updated when the event
handlers run. A side effect of this
change is that in the case where
default handlers return true to stop
signal propogation, Gtk# events will
not be emitted. This is the case for
example in Gtk.Button, where the
button-press-event default signal
handler is overridden to emit Pressed
events.
While potentially confusing, this is
not really a bug. When you use a
Gtk.Button, you are getting a widget
that emits Pressed events in response
to Button1 presses. If you also want
your Button to change colors, or popup
a context menu on Button3 presses,
that's not a Gtk.Button. The correct
way to implement such a widget is to
subclass Gtk.Button and override the
OnButtonPressEvent virtual method to
implement the new behaviors you
desire."
If it weren't for, "public outcry" (rarely a sign of a good interface), there would be no way to avoid this, except subclassing which is sometimes annoying in C# due to the lack of anonymous classes. But luckily, you're not the first person to have this issue. So that's where the GLib.ConnectBefore attribute comes in. It basically says, call this event handler first so the event isn't devoured by Gtk+.
The annoyance doesn't end there though. I originally was going to suggest applying a good proven solution to passing "extra" parameters to event handlers. In this case, this would allow you to find the index without using equals or the Name string It basically involves creating a wrapper delegate that "pretends" to be a ButtonPressEventHandler but internally passes an int to your backing method:
Func<uint, ButtonPressEventHandler> indexWrapper = ((index) => ((s, e) => { AddButtonPressed_wrapped(s, e, index); }));
...
plus[i].ButtonPressEvent += indexWrapper(i);
...
protected virtual void AddButtonPressed_wrapped(object sender, EventArgs e, uint index)
{
Console.WriteLine("Button pressed");
Console.WriteLine("Index = {0}", index);
}
It compiles and runs without errors, but it has the same problem, the event never fires. I realized that you can't put an attribute directly on a delegate/lambda. So even though the backing method has [GLib.ConnectBefore] the delegate doesn't, so it fails.
As a final note, you could use the Clicked event as in this API example. I verified that it works as expected. One would think that it would only fire on mouse-clicks, but it actually does fire on spacebar as well.

for (uint i = 0; i < 10; i++)
{
plus[i] = new Button();
plus[i].Data.Add("Index",i);
plus[i].ButtonPressEvent += AddButtonPressed;
Add(plus[i]);
}
Handler:
protected virtual void AddButtonPressed(object sender, System.EventArgs e) {
Console.WriteLine("Button pressed");
Gtk.Button button = sender as Gtk.Button;
Console.WriteLine("Index: {0}", button.Data["Index"]);
}

I'm fairly certain you need to actually add the buttons to the GTK window hierarchy, something like:
for (uint i = 0; i < 10; i++) {
plus[i] = new Button();
plus[i].Name = i.ToString();
plus[i].ButtonPressEvent += AddButtonPressed;
Add(plus[i]);
}
Should be similar to that, never actually used GTK <.<

Use Tag property, if Gtk button has one.
for (uint i = 0; i < 10; i++) {
plus[i] = new Button();
plus[i].Name = i.ToString();
plus[i].ButtonPressEvent += AddButtonPressed;
plus[i].Tag = i;
Add(plus[i]);
}
Handler:
protected virtual void AddButtonPressed(object sender, System.EventArgs e) {
Console.WriteLine("Button pressed");
Gtk.Button button = sender as Gtk.Button;
Console.WriteLine("Index: {0}", button.Tag);
}

Related

SlimDx Events on button pressed

I am using slimdx to interpret xbox controller button presses. I poll every 200ms to read the xbox button states and all works for me. I use
JoystickState state = Joystick.GetCurrentState();
// get buttons states
bool[] buttonsPressed = state.GetButtons();
Is there anyway to generate events on the button press instead of polling? To explain imagine if my poll time was 5 seconds. And the user presses a button in the 2nd second and releases it. In the next poll time my application will never know that the button was pressed
No - in DirectX you must poll. To do this efficiently you want to create a polling thread, and have a class which raises cross thread events to your consuming thread.
I know this is 4 years old but the answer is incorrect. The most efficient way may be to poll, but you can raise an event when you poll.
This is a work in progress but it should get someone started. Save this as a new class, it derives from a Timer, so once you add this to your project, build it, and drag it onto the Form you want to use it, you can then subscribe to the buttonPressed event.
public class GamePadController : Timer
{
public delegate void ButtonPressedDelegate(object sender, int ButtonNumber);
public event ButtonPressedDelegate ButtonPressed;
List<DeviceInstance> directInputList = new List<DeviceInstance>();
DirectInput directInput = new DirectInput();
List<SlimDX.DirectInput.Joystick> gamepads = new List<Joystick>();
SlimDX.DirectInput.JoystickState state;
public GamePadController()
{
this.Interval = 10;
this.Enabled = true;
this.Tick += GamePadController_Tick;
RefreshGamePads();
}
private void RefreshGamePads()
{
directInputList.Clear();
directInputList.AddRange(directInput.GetDevices(DeviceClass.GameController, DeviceEnumerationFlags.AttachedOnly));
gamepads.Clear();
foreach (var device in directInputList)
{
gamepads.Add(new SlimDX.DirectInput.Joystick(directInput, directInputList[0].InstanceGuid));
}
}
private void GamePadController_Tick(object sender, EventArgs e)
{
foreach (var gamepad in gamepads)
{
if (gamepad.Acquire().IsFailure)
continue;
if (gamepad.Poll().IsFailure)
continue;
if (SlimDX.Result.Last.IsFailure)
continue;
state = gamepad.GetCurrentState();
bool[] buttons = state.GetButtons();
for (int i = 0; i < buttons.Length; i++)
{
if (buttons[i])
{
if (ButtonPressed != null)
{
ButtonPressed(gamepad, i);
}
}
}
gamepad.Unacquire();
}
}
}
}

Event Driven Programming

I've been reading this MSDN article and this question to try to understand events in .NET. Unfortunately, its not clicking for me and I'm having a lot of trouble. I'm trying to integrate this technique into my project, with little success.
Basically, I've got this class that will read numbers. Whenever it encounters a new number, I want it to fire an event called numberChanged.
So, I set up my event public event EventHandler numberChanged;. Later on, I fire my event when it encounters a number than isn't the same as the previous one.
if(currentNumber != previousNumber){
if(numberChanged != null){
numberChanged(this, new EventArgs());
}
}
But then I'm having trouble 'subscibing' to this event. If I do numberChanged += [something to do here] it errors saying that numberChanged is an event and not a type.
Is my explanation clear enough for some advice to be offered? Many thanks.
There are a number of ways to handle it, the most basic is to create a function:
public void MyNumberChangedHandler(object sender, EventArgs e)
{
//Your code goes here that gets called when the number changes
}
You then subscribe (one time only, usually in the constructor) by going:
numberChanged += MyNumberChangedHandler;
Or, you can use something called an anonymous (lambda) method, which is also assigned in your constructor (typically):
numberChanged += (sender, e) => {
//Your code here to handle the number changed event
};
To expand a little bit, care must be taken when using the lambda approach since you can create memory leaks and zombie objects. The .NET memory garbage collector is a mark-and-sweep system that removes objects when they are no longer in use. This post shows how hard it is to remove lambda event handlers: How to remove a lambda event handler .
Having an active event handler can keep your object alive even if it has been disposed! Here is an example of creating a zombie object (doesn't run in Fiddle but you can copy to your own console app) https://dotnetfiddle.net/EfNpZ5
Prints out:
I'm still alive
I'm still alive
I was disposed!
Press any key to quit
I'm still alive
I'm still alive
I'm still alive.
As everything else in the C# programming world, the events concept also follows specific rules and has it's own syntax. The wording is as follows:
an event defined as EventHandler is actually just a shortcut for a special method (delegate) signature - public delegate void EventHandler(object sender, EventArgs e)[1]. Whenever you have a signature in C# you always know what you need to write on the right sight or as a parameter, in order to connect/call some objects/methods/and so on.
after the event is defined, you need to subscribe in order to be informed whenever something happens. The syntax for subscribing an event is +=. Naturally for unsubscribing is -=. MSDN says that the syntax should be object.event += eventHandler (or object.event += new EventHandler(eventHandler);)
so after an event is defined (event Event SomeEvent;) all that left is to create a method that can be bound to this event. This method has to have the same signature as the EventHandler, so it should match the signature of [1] and can be something like private void numberChangedEventHandler(object sender, EventArgs eventArguments)
Now you know what you need to write on the right side of +=.
An example:
public class NumberSequence
{
// numbers to be compared
private readonly List<int> numbers = new List<int>();
// used to generate a random collection
private readonly Random random = new Random();
// tell me if the previous and next number are different
public event EventHandler DifferentNumbersEvent;
public NumberSequence()
{
// fill the list with random numbers
Enumerable.Range(1, 100).ToList().ForEach(number =>
{
numbers.Add(random.Next(1, 100));
});
}
public List<int> Numbers { get { return numbers; } }
public void TraverseList()
{
for (var i = 1; i < this.numbers.Count; i++)
{
if (this.numbers[i - 1] != this.numbers[i])
{
if (this.DifferentNumbersEvent != null)
{
// whoever listens - inform him
this.DifferentNumbersEvent(this, EventArgs.Empty);
}
}
}
}
}
Now before the class is used, define the event handler, that will listen and will be called, when the event is fired (wording again):
private void differentNumberEventHandler(Object sender, EventArgs eventArguments)
{
Console.WriteLine("Different numbers...");
}
And the usage:
var ns = new NumberSequence();
ns.DifferentNumbersEvent += differentNumberEventHandler;
ns.TraverseList();
Everything else is just syntactic sugar for this notation (lambda / anonymous methods / ...), for example:
object.Event += (s, e) => { // code ... }; is the same as object.Event += (Object sender, EventArgs eventArguments) => { // code ... };. Do you recognise the signature? - it is the same as the private void differentNumberEventHandler....
Often we need to pass information through the event, in this case maybe we want to see the two numbers. C# allows you to do this easily using custom event arguments. Just create a class that inherits the EventArgs class and add properties for the data that should be passed, in this case the numbers:
public class NumbersInfoEventArgs : EventArgs
{
public int Number1 { get; set; }
public int Number2 { get; set; }
}
And then specify, when declaring the event, that it will pass data of type NumbersInfoEventArgs (signatures again):
public event EventHandler<NumbersInfoEventArgs> DifferentNumbersEvent;
...
this.DifferentNumbersEvent(this, new NumbersInfoEventArgs
{
Number1 = this.numbers[i - 1],
Number2 = this.numbers[i]
});
And last but now least, the signature of the event handler should match the signature of the event:
private void differentNumberEventHandler(Object sender, NumbersInfoEventArgs eventArguments)
{
Console.WriteLine("Different numbers {0} - {1}", eventArguments.Number1, eventArguments.Number2);
}
And voila, the output is:
Different numbers 89 - 86
Different numbers 86 - 53
Different numbers 53 - 12
Different numbers 12 - 69
you can subscribe the event in this way:
using System;
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
var num = new Number();
num.numberChanged +=(s,e) =>{
Console.WriteLine("Value was changed to {0}",num.Value); // in the demo below you can find another implementation for this sample using custom events
};
num.Value=10;
num.Value=100;
}
}
public class Number{
public event EventHandler numberChanged;
private int _value=0;
public int Value
{
get{
return _value;
}
set{
if(value!=_value){
_value=value;
if(numberChanged!=null)
numberChanged(this,null);
}
}
}
}
explanation:
since the EventHandler delegate has 2 parameters (sender, eventArgs) as mentioned here, you need to pass these params and I passed them as s and e
another way to subscribe this event like this:
var num = new Number();
num.numberChanged += NumberChanged_Event; // below is the delegate method
public void NumberChanged_Event(object sender,EventArgs e)
{
// your code goes here
}
I updated the demo to work with you own delegate to pass the old value and new value which can help in many cases.
here a working demo

Make buttons in List send unique data to method

I made a application which will place out buttons in a grid where the user specifies how big the playfield should be.
I create the buttons in a list, specify some data like backgroundimage, size, and location. I then need to, in some way make the different buttons execute different code. I figured I could do this in one method, (if there aren't any good ways to programmatically create methods), if I could somehow make the buttons send a unique piece of information to the method to identify which button is pressed.
public void buttonplacer()
{
int nbrofbtns = Form2.puzzlesize * Form2.puzzlesize;
List<Button> btnslist = new List<Button>();
for (int i = 0; i < nbrofbtns; i++)
{
Button newButton = new Button();
btnslist.Add(newButton);
this.Controls.Add(newButton);
newButton.Name = "btn" + i.ToString();
newButton.Width = btnsidelength;
newButton.Height = btnsidelength;
newButton.Top = btnsidelength * Convert.ToInt32(Math.Floor(Convert.ToDouble(i / Form2.puzzlesize)));
newButton.Left = btnsidelength * Convert.ToInt32(Math.Floor(Convert.ToDouble(i)) - Math.Floor((Convert.ToDouble(i)) / (Form2.puzzlesize)) * (Form2.puzzlesize));
newButton.BackgroundImage = Lights_out_.Properties.Resources.LightsOutBlack;
newButton.Click += new EventHandler(Any_Button_Click);
}
}
void Any_Button_Click(object sender, EventArgs e)
{
}
(If you want to know I'm doing a game called "Light's out")
Thanks in advance!
The Any_Button_Click method receives an object sender that is the button that got clicked. You just need to cast it to a Button:
void Any_Button_Click(object sender, EventArgs e)
{
Button b = (Button)sender;
// do stuff here
}
You can use the button's Location property to figure out where it sits on the game board, or you can assign an arbitrary object to the button with any information you choose at initialization time using the Tag property like this:
button.Tag = "someHelpfulString";
or like this:
button.Tag = new Tuple<int, int>(xpos, ypos);
(where xpos and ypos are positions in the button grid)
or like this:
button.Tag = new ButtonInfoObject(foo, bar, baz);
(Here it's up to you to define the ButtonInfoObject class.)
As an alternative to other answers, and in particular to somewhat address the part "good ways to programmatically create methods", there is part of the C# language called Lambda Expressions. To keep long story short, you could write something along these lines:
newButton.Click += (s, e) =>
{
//here you have access to all variables accessible in current scope,
//including "newButton" and "i";
//you could, for example, call some method passing "i" as an argument
//or just put that method's code inside this block
};
The only downside of this approach is that you need to take some extra care if you're planning to unregister the handler at some later point (see this question or this question for reference).
EDIT
As pointed in comments I overlooked the fact that i stays in scope for the whole for loop, so using it inside lambda is pretty much pointless (all handlers will use it's final value). To make it behave like expected one can simply define a variable inside the loop so it goes out of scope at the end of each iteration and is stored separately for each handler:
var btnNo = i;
newButton.Click += (s, e) =>
{
//use "btnNo" instead of "i"
//you can still safely use "newButton" reference
//since it's defined inside the loop
}
Use the sender object to get the button's name that was pressed:
void Any_Button_Click(object sender, EventArgs e)
{
switch ((sender as Button).Name)
{
case "btn0":
//...
break;
case "btn1":
//...
break;
//...
}
}

Delegate(?) action events

I'd like to give my app users a reward after they've successfully watched a chartboost ad video, but the problem is that I can't add a function to it.
Here's the line of code in the script:
public static event Action<CBLocation,int> didCompleteRewardedVideo;
Now I have no knowledge about event actions, So I thought this piece of code would do:
Chartboost.didCompleteRewardedVideo += new EventHandler(DidCompleteRewardedVideo);
And he function:
public void DidCompleteRewardedVideo(object sender, EventArgs e)
{
PlayerPrefs.SetInt("Energy", 10);
energy = 10;
energyText.text = energy.ToString();
}
But got a compile error saying they don't match. An eventHandler is probably not the same as an action event, but just to show you how I think this works.
Thanks
Nick
Whe you subscribe to the event, you have to match the event signature:
public void DidCompleteRewardedVideo(CBLocation location, int x)
{
PlayerPrefs.SetInt("Energy", 10);
energy = 10;
energyText.text = energy.ToString();
}
Subscribe like this:
Chartboost.didCompleteRewardedVideo += DidCompleteRewardedVideo;

How to wait for a click in c#

I am currently taking a beginner's class in c#. We missed 2 consecutive classes because the teacher couldn't be there. So we didn't really see what we needed to do this. He said to just go see on MSDN, but that is usually way too complicated for me. So here is my problem:
I have to create a "Simon Says" program. Here is my current code (sorry for the French variables):
public partial class Form1 : Form
{
const byte LIMITE = 255;
const byte LIMITEBOUTON = 5;
byte[] _abyBouton = new byte[LIMITE];
Random _rand = new Random();
public Form1()
{
InitializeComponent();
}
//Blinks the Button. Works correctly.
void AnimerBouton(Button btnBouton, Color Cocoleur)
{
btnBouton.BackColor = Color.Black;
btnBouton.ForeColor = Color.White;
Update();
System.Threading.Thread.Sleep(500); // C'est inscrit en miliseconde
btnBouton.BackColor = Cocoleur;
btnBouton.ForeColor = Color.Black;
Update();
System.Threading.Thread.Sleep(500); // C'est inscrit en miliseconde
}
private void btnDémarrer_Click(object sender, EventArgs e)
{
//Creates an array with the 255 eventual moves.
for (byte byIndex = 0; byIndex <= LIMITE - 1; byIndex++)
{
_abyBouton[byIndex] = (byte)_rand.Next(1, LIMITEBOUTON);
}
for (byte byIndex = 0; byIndex <= LIMITE - 1; byIndex++)
{
//Takes care of the current progress in the game.
for (byte byIndex2 = 0; byIndex2 <= byIndex; byIndex2++)
{
switch (_abyBouton[byIndex2])
{
case 1:
{
AnimerBouton(btn1, Color.Green);
}
break;
case 2:
{
AnimerBouton(btn2, Color.Red);
}
break;
case 3:
{
AnimerBouton(btn3, Color.Yellow);
}
break;
case 4:
{
AnimerBouton(btn4, Color.Cyan);
}
break;
}
//Wait to see if the click is correct. No idea how to do this.
}
}
}
}
So I have to wait for the user to click a button and see if it is the correct one. I looked around and it was talking about events, but I couldn't grasp what I needed to do. I would appreciate some help on how to do this.
Thanks!
In the designer, on the properties window, click the lightning bolt icon. You will get a list of events for the selected control. Make sure the btnBouton control is selected, and find the Click event in the list. You should see btnDemarrer_Click in the drop down list. Select it. Now when the button is clicked, it should call your btnDemarrer_Click handler.
When you have not already written a handler, you can double-click the event in the list, and it will generate the method skeleton for you automatically. You can also double-click the control itself to generate the default event handler for that control. (In the case of buttons, I think the default event is the click event.)
If you want a particular method to wait until some work is done, you could look into AutoResetEvent. An extremely simplified example might help you get on the right track:
using System.Threading;
public static AutoResetEvent arEvent = new AutoResetEvent(false);
static void Main()
{
DoWork();
arEvent.WaitOne(); //WaitOne() "pauses" Main and waits for some work to be done.
DoWork();
arEvent.WaitOne();
}
static void DoWork();
{
//Some work is done here.
arEvent.Set(); //This lets Main() continue where it left off.
}
Using this, you could have btnDémarrer_Click wait for the user input then continue on after the user has done his clicking.

Categories

Resources