Dynamic created event in c# - c#

I have a dynamical create button object where I want it to send an event to the mainform when I click on a button inside this object.
I have the code like this :
class Class1
{
internal class FObj_BtnRow
{
private Button[] _Btnmembers;
internal event EventHandler<SelValue_EArgs>[] e_BtnMember; //subscribe to a change
internal class SelValue_EArgs : EventArgs//events args (selected value)
{//return arguments in events
internal SelValue_EArgs(Boolean ivalue) { refreshed = ivalue; }//ctor
internal Boolean refreshed { get; set; }//accessors
}
private Boolean[] _ActONOFFValue; //Pump=0, valveInput = 1, Shower = 3, Washtool = 4 , WashWPcs = 5
private Boolean ActONOFFValue(int number)
{
_ActONOFFValue[number] = !_ActONOFFValue[number];
{
if (e_BtnMember[number] != null && number == 0) e_BtnMember[number](this, new SelValue_EArgs(_ActONOFFValue[number]));
}
return _ActONOFFValue[number];
}
public FObj_BtnRow(String[] TxtBtn, String UnitName)//ctor
{
_Btnmembers = new Button[TxtBtn.Length];
e_BtnMember = new EventHandler<SelValue_EArgs>[TxtBtn.Length];
for (int i = 0; i < TxtBtn.Length / 2; i++)
{
_Btnmembers[i].Click += new EventHandler(_Btnmembers_Click);
}
}
protected virtual void _Btnmembers_Click(object sender, EventArgs e)
{
int index = Array.IndexOf(_Btnmembers, (Button)sender);
ActONOFFValue(index);
}
}
}
But in the line : internal event EventHandler[] e_BtnMember;
the compiler told me that I should use a delegate. I don't understand good this remark, could you help me?
At the end the mainform should only subscribe to the event button click it wants.
And then in main we could use it to now when a button in the object multibutton row is clicked... like this:
public void main()
{
String[] txtbtn = new String[] { "btn1", "btn2", "btn3" };
FObj_BtnRow thisbtnrow = new FObj_BtnRow(txtbtn);
thisbtnrow.e_BtnMember[0] += new EventHandler<FObj_BtnRow.SelValue_EArgs> (btnmember0haschanged);
}
public void btnmember0haschanged(object sender, FObj_BtnRow.SelValue_EArgs newvalue)
{
bool thisnewvalue = newvalue.refreshed;
}
Can you help me?
Thanks in advance!!

I solved the problem by myself, thanks for your advices.
Code
class Class1
{
internal class FObj_BtnRowtest
{
private Button[] _Btnmembers;
public delegate void del_Btnmember(Boolean value);
public del_Btnmember[] btnvaluechanged;
internal class SelValue_EArgs : EventArgs//events args (selected value)
{//return boolean arguments in events
internal SelValue_EArgs(Boolean ivalue) { refreshed = ivalue; }//ctor
internal Boolean refreshed { get; set; }//accessors
}
private Boolean[] _ActONOFFValue;
private Boolean ActONOFFValue(int number)
{
_ActONOFFValue[number] = !_ActONOFFValue[number];
return _ActONOFFValue[number];
}
public FObj_BtnRowtest(int numofbtn, String UnitName)//ctor
{
_Btnmembers = new Button[numofbtn];
btnvaluechanged = new del_Btnmember[numofbtn];
_ActONOFFValue = new bool[numofbtn];
for (int i = 0; i < numofbtn / 2; i++)
{
_Btnmembers[i].Click += new EventHandler(_Btnmembers_Click);
}
}
protected virtual void _Btnmembers_Click(object sender, EventArgs e)
{
int index = Array.IndexOf(_Btnmembers, (Button)sender);
if (btnvaluechanged[index] != null) btnvaluechanged[index](ActONOFFValue(index));
}
}
}
And then in main
thisrow = new Class1.FObj_BtnRowtest(4,"thisunittest");//4 buttons
thisrow.btnvaluechanged[0] += new Class1.FObj_BtnRowtest.del_Btnmember(valuetesthaschanged);//to subscribe to btn0 change
using delegates make it easier. and yes we do need those kinds of stuffs to make code clearer and faster to developp.
Thanks all!!

Related

c# poker checkbox value value dont change

I am doing MVC and I am new at it. I am making a game where 5 dice roll by clicking on the 'roll dice' button and show random numbers. Under each die label there is a check box. If the player clicks on the check box the dice don't roll again and the other dice do roll.
I can show dice numbers in my view but I don't understand how to make it so the dice don't roll if you check the checkbox. I translated my program because it's in dutch: Waarde=value; dobbesteen=dice; werp=throw.
public class Dobbelsteen
{
Random rnd = new Random();
public int Waarde { get; set; }
public bool Checked { get; set; }
public int Worpen { get; set; }
public void Rollen()
{
if (Checked == false)
Waarde = rnd.Next(1, 7);
}
}
public class BusinessController
{
List<int> dice = new List<int>();
Dobbelsteen objdobbelsteen = new Dobbelsteen();
public BusinessController()
{
}
public int Roll()
{
for (int i = 0; i < 5; i++)
{
objdobbelsteen.Rollen();
dice.Add(i);
}
return objdobbelsteen.Waarde;
}
public int Werp()
{
objdobbelsteen.Worpen++;
return objdobbelsteen.Worpen;
}
public int nietroll()
{
int i = 1;
return i;
}
/*public bool Winnaar()
{
if (dice[1] == dice[2])
{
return true;
}
else return false;
}*/
public void SetLock(int p)
{
if(objdobbelsteen.Checked==false)
{
nietroll();
}
}
}
public partial class Form1 : Form
{
BusinessController busniessController = new BusinessController();
public Form1()
{
InitializeComponent();
}
public void Gooien_Click(object sender, EventArgs e)
{
lblWorpen.Text = Convert.ToString(busniessController.Werp());
dblsteen1.Text = Convert.ToString(busniessController.Roll());
dblsteen2.Text = Convert.ToString(busniessController.Roll());
dblsteen3.Text = Convert.ToString(busniessController.Roll());
dblsteen4.Text = Convert.ToString(busniessController.Roll());
dblsteen5.Text = Convert.ToString(busniessController.Roll());
if (dblsteen1.Text==dblsteen2.Text)
MessageBox.Show("u win");
}
public void checkBox1_CheckedChanged(object sender, EventArgs e)
{
busniessController.SetLock(1);
}
private void checkBox2_CheckedChanged(object sender, EventArgs e)
{
busniessController.SetLock(2);
}
private void checkBox3_CheckedChanged(object sender, EventArgs e)
{
busniessController.SetLock(3);
}
private void checkBox4_CheckedChanged(object sender, EventArgs e)
{
busniessController.SetLock(4);
}
private void checkBox5_CheckedChanged(object sender, EventArgs e)
{
busniessController.SetLock(5);
}
}
You need 5 different dices, not just one. In this way each dice can have its own Checked property set to a different value
List<Dobbelsteen> dices = new List<Dobbelsteen>()
{
{new Dobbelsteen()},
{new Dobbelsteen()},
{new Dobbelsteen()},
{new Dobbelsteen()},
{new Dobbelsteen()}
};
public void RollAll()
{
for (int i = 0; i < 5; i++)
dices[i].Rollen();
}
public int GetDiceValue(int i)
{
if(i >= 0 && i <= dices.Count)
return dices[i].Waarde;
else
throw new IndexOutOfRangeException($"Invalid index {i}");
}
public void SetLock(int p)
{
if(p >= 0 && p <= dices.Count)
return dices[p].Checked = true;
else
throw new IndexOutOfRangeException($"Invalid index {p}");
}
Also you should make your Random variable of the class DobbelSteen static otherwise the strict loop inside the Rollen method results in the same value returned for every dice.
After this you can call
busniessController.RollAll();
dblsteen1.Text = busniessController.GetDiceValue(0).ToString();
dblsteen2.Text = busniessController.GetDiceValue(1).ToString();
dblsteen3.Text = busniessController.GetDiceValue(2).ToString();
dblsteen4.Text = busniessController.GetDiceValue(3).ToString();
dblsteen5.Text = busniessController.GetDiceValue(4).ToString();
Finally remember that in NET the index for a collection like a list starts at zero and ends to the collection count - 1. So your call to SetLock should be changed accordingly to avoid the exception
You never actually set the "Checked" value, SetLock refers to the nietRol function which just sets an int. Here you need to set the "Checked" value to true.
And please programmeer niet in het Nederlands maar doe alles in het Engels.

Test helper for expected events sequence reporting duplicate events

I have a helper method for my unit tests that asserts that a specific sequence of events were raised in a specific order. The code is as follows:
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction)
{
var expectedSequence = new Queue<int>();
for (int i = 0; i < subscribeActions.Count; i++)
{
expectedSequence.Enqueue(i);
}
ExpectEventSequence(subscribeActions, triggerAction, expectedSequence);
}
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence)
{
var fired = new Queue<int>();
var actionsCount = subscribeActions.Count;
for(var i =0; i< actionsCount;i++)
{
subscription((o, e) =>
{
fired.Enqueue(i);
});
}
triggerAction();
var executionIndex = 0;
var inOrder = true;
foreach (var firedIndex in fired)
{
if (firedIndex != expectedSequence.Dequeue())
{
inOrder = false;
break;
}
executionIndex++;
}
if (subscribeActions.Count != fired.Count)
{
Assert.Fail("Not all events were fired.");
}
if (!inOrder)
{
Assert.Fail(string.Format(
CultureInfo.CurrentCulture,
"Events were not fired in the expected sequence from element {0}",
executionIndex));
}
}
Example usage is as follows:
[Test()]
public void FillFuel_Test([Values(1, 5, 10, 100)]float maxFuel)
{
var fuelTank = new FuelTank()
{
MaxFuel = maxFuel
};
var eventHandlerSequence = new Queue<Action<EventHandler>>();
eventHandlerSequence.Enqueue(x => fuelTank.FuelFull += x);
//Dealing with a subclass of EventHandler
eventHandlerSequence.Enqueue(x => fuelTank.FuelChanged += (o, e) => x(o, e));
Test.ExpectEventSequence(eventHandlerSequence, () => fuelTank.FillFuel());
}
And the code under test:
public float Fuel
{
get
{
return fuel;
}
private set
{
var adjustedFuel = Math.Max(0, Math.Min(value, MaxFuel));
if (fuel != adjustedFuel)
{
var oldFuel = fuel;
fuel = adjustedFuel;
RaiseCheckFuelChangedEvents(oldFuel);
}
}
}
public void FillFuel()
{
Fuel = MaxFuel;
}
private void RaiseCheckFuelChangedEvents(float oldFuel)
{
FuelChanged.FireEvent(this, new FuelEventArgs(oldFuel, Fuel));
if (fuel == 0)
{
FuelEmpty.FireEvent(this, EventArgs.Empty);
}
else if (fuel == MaxFuel)
{
FuelFull.FireEvent(this, EventArgs.Empty);
}
if (oldFuel == 0 && Fuel != 0)
{
FuelNoLongerEmpty.FireEvent(this, EventArgs.Empty);
}
else if (oldFuel == MaxFuel && Fuel != MaxFuel)
{
FuelNoLongerFull.FireEvent(this, EventArgs.Empty);
}
}
So the test expects FuelFilled to be fired before FuelChanged but in actuality FuelChanged is fired first, which fails the test.
However my test is instead reporting that FuelChanged is being fired twice, but when I step through the code it is clear that FuelFilled is fired after FuelChanged and FuelChanged is only fired once.
I assumed that it was something to do with the way lambdas work with local state, maybe the for loop iterator variable was only ever set to the final value, so I replaced the for loop with this:
var subscriptions = subscribeActions.ToList();
foreach (var subscription in subscriptions)
{
subscription((o, e) =>
{
var index = subscriptions.IndexOf(subscription);
fired.Enqueue(index);
});
}
However the result is the same, fired contains {1;1} instead of {1;0}.
Now I'm wondering if the same lambda is being assigned to both events instead of using the different subscription / index state. Any ideas?
Update: I was unable to get success with either answer posted so far (same as my initial results), despite their similarities to my actual code, so I presume the issue is located elsewhere in my FuelTank code. I've pasted the full code for FuelTank below:
public class FuelTank
{
public FuelTank()
{
}
public FuelTank(float initialFuel, float maxFuel)
{
MaxFuel = maxFuel;
Fuel = initialFuel;
}
public float Fuel
{
get
{
return fuel;
}
private set
{
var adjustedFuel = Math.Max(0, Math.Min(value, MaxFuel));
if (fuel != adjustedFuel)
{
var oldFuel = fuel;
fuel = adjustedFuel;
RaiseCheckFuelChangedEvents(oldFuel);
}
}
}
private float maxFuel;
public float MaxFuel
{
get
{
return maxFuel;
}
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("MaxFuel", value, "Argument must be not be less than 0.");
}
maxFuel = value;
}
}
private float fuel;
public event EventHandler<FuelEventArgs> FuelChanged;
public event EventHandler FuelEmpty;
public event EventHandler FuelFull;
public event EventHandler FuelNoLongerEmpty;
public event EventHandler FuelNoLongerFull;
public void AddFuel(float fuel)
{
Fuel += fuel;
}
public void ClearFuel()
{
Fuel = 0;
}
public void DrainFuel(float fuel)
{
Fuel -= fuel;
}
public void FillFuel()
{
Fuel = MaxFuel;
}
private void RaiseCheckFuelChangedEvents(float oldFuel)
{
FuelChanged.FireEvent(this, new FuelEventArgs(oldFuel, Fuel));
if (fuel == 0)
{
FuelEmpty.FireEvent(this, EventArgs.Empty);
}
else if (fuel == MaxFuel)
{
FuelFull.FireEvent(this, EventArgs.Empty);
}
if (oldFuel == 0 && Fuel != 0)
{
FuelNoLongerEmpty.FireEvent(this, EventArgs.Empty);
}
else if (oldFuel == MaxFuel && Fuel != MaxFuel)
{
FuelNoLongerFull.FireEvent(this, EventArgs.Empty);
}
}
}
FuelEventArgs looks like this:
public class FuelEventArgs : EventArgs
{
public float NewFuel
{
get;
private set;
}
public float OldFuel
{
get;
private set;
}
public FuelEventArgs(float oldFuel, float newFuel)
{
this.OldFuel = oldFuel;
this.NewFuel = newFuel;
}
}
The FireEvent extension method is looks like this:
public static class EventHandlerExtensions
{
/// <summary>
/// Fires the event. This method is thread safe.
/// </summary>
/// <param name="handler"> The handler. </param>
/// <param name="sender"> Source of the event. </param>
/// <param name="args"> The <see cref="EventArgs"/> instance containing the event data. </param>
public static void FireEvent(this EventHandler handler, object sender, EventArgs args)
{
var handlerCopy = handler;
if (handlerCopy != null)
{
handlerCopy(sender, args);
}
}
/// <summary>
/// Fires the event. This method is thread safe.
/// </summary>
/// <typeparam name="T"> The type of event args this handler has. </typeparam>
/// <param name="handler"> The handler. </param>
/// <param name="sender"> Source of the event. </param>
/// <param name="args"> The <see cref="EventArgs"/> instance containing the event data. </param>
public static void FireEvent<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs
{
var handlerCopy = handler;
if (handlerCopy != null)
{
handlerCopy(sender, args);
}
}
}
The full test code can be found above in the question, there is no other code called during test execution.
I am using NUnit test framework via the Unity Testing Tools plugin for the Unity3D engine, .NET version 3.5 (ish, it's closer to Mono 2.0, I believe), and Visual Studio 2013.
Update 2:
After extracting the code and tests to their own project (outside of the Unity3D ecosystem) all tests run as expected, so I'm going to have to chalk this one up to a bug in the Unity -> Visual Studio bridge.
I have the following implementation based upon Nick's question.
First the class for the FuelTank:
public class FuelTank
{
private float fuel;
//Basic classes for the event handling, could be done by providing a few simple delegates,
//but this is just to stick as close to the original question as possible.
public FuelChanged FuelChanged = new FuelChanged();
public FuelEmpty FuelEmpty = new FuelEmpty();
public FuelFull FuelFull = new FuelFull();
public FuelNoLongerEmpty FuelNoLongerEmpty = new FuelNoLongerEmpty();
public FuelNoLongerFull FuelNoLongerFull = new FuelNoLongerFull();
public float MaxFuel { get; set; }
public float Fuel
{
get
{
return fuel;
}
private set
{
var adjustedFuel = Math.Max(0, Math.Min(value, MaxFuel));
if (fuel != adjustedFuel)
{
var oldFuel = fuel;
fuel = adjustedFuel;
RaiseCheckFuelChangedEvents(oldFuel);
}
}
}
public void FillFuel()
{
Fuel = MaxFuel;
}
private void RaiseCheckFuelChangedEvents(float oldFuel)
{
FuelChanged.FireEvent(this, new FuelEventArgs(oldFuel, Fuel));
if (fuel == 0)
{
FuelEmpty.FireEvent(this, EventArgs.Empty);
}
else if (fuel == MaxFuel)
{
FuelFull.FireEvent(this, EventArgs.Empty);
}
if (oldFuel == 0 && Fuel != 0)
{
FuelNoLongerEmpty.FireEvent(this, EventArgs.Empty);
}
else if (oldFuel == MaxFuel && Fuel != MaxFuel)
{
FuelNoLongerFull.FireEvent(this, EventArgs.Empty);
}
}
}
As the code for the event handlers was missing, I made an assumption to use this. As the comment describes in the previous code block, it could be done more easily with plain delegates. It's just a matter of choice, for which I think this implementation isn't the best yet, but suitable enough for debugging:
public class FuelEventArgs : EventArgs
{
private float oldFuel, newFuel;
public FuelEventArgs(float oldFuel, float newFuel)
{
this.oldFuel = oldFuel;
this.newFuel = newFuel;
}
}
public class FuelEvents
{
public event EventHandler FireEventHandler;
public virtual void FireEvent(object sender, EventArgs fuelArgs)
{
EventHandler handler = FireEventHandler;
if (null != handler)
handler(this, fuelArgs);
}
}
public class FuelChanged : FuelEvents
{
public override void FireEvent(object sender, EventArgs fuelArgs)
{
Console.WriteLine("Fired FuelChanged");
base.FireEvent(sender, fuelArgs);
}
}
public class FuelEmpty : FuelEvents
{
public override void FireEvent(object sender, EventArgs fuelArgs)
{
Console.WriteLine("Fired FuelEmpty");
base.FireEvent(sender, fuelArgs);
}
}
public class FuelFull : FuelEvents
{
public override void FireEvent(object sender, EventArgs fuelArgs)
{
Console.WriteLine("Fired FuelFull");
base.FireEvent(sender, fuelArgs);
}
}
public class FuelNoLongerEmpty : FuelEvents
{
public override void FireEvent(object sender, EventArgs fuelArgs)
{
Console.WriteLine("Fired FuelNoLongerEmpty");
base.FireEvent(sender, fuelArgs);
}
}
public class FuelNoLongerFull : FuelEvents
{
public override void FireEvent(object sender, EventArgs fuelArgs)
{
Console.WriteLine("Fired FuelNoLongerFull");
base.FireEvent(sender, fuelArgs);
}
}
And to test it all, I used this class, containing most code from the original question:
[TestFixture]
public class Tests
{
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction)
{
var expectedSequence = new Queue<int>();
for (int i = 0; i < subscribeActions.Count; i++)
{
expectedSequence.Enqueue(i);
}
ExpectEventSequence(subscribeActions, triggerAction, expectedSequence);
}
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence)
{
var fired = new Queue<int>();
var actionsCount = subscribeActions.Count;
//This code has been commented out due to the fact that subscription is unknown here.
//I stuck to use the last solution that Nick provided himself
//for (var i = 0; i < actionsCount; i++)
//{
// subscription((o, e) =>
// {
// fired.Enqueue(i);
// });
//}
var subscriptions = subscribeActions.ToList();
foreach (var subscription in subscriptions)
{
subscription((o, e) =>
{
var index = subscriptions.IndexOf(subscription);
Console.WriteLine("[ExpectEventSequence] Found index: {0}", index);
fired.Enqueue(index);
});
}
triggerAction();
var executionIndex = 0;
var inOrder = true;
foreach (var firedIndex in fired)
{
if (firedIndex != expectedSequence.Dequeue())
{
inOrder = false;
break;
}
executionIndex++;
Console.WriteLine("Execution index: {0}", executionIndex);
}
if (subscribeActions.Count != fired.Count)
{
Assert.Fail("Not all events were fired.");
}
if (!inOrder)
{
Console.WriteLine("Contents of Fired Queue: {0}", PrintValues(fired));
Assert.Fail(string.Format(
CultureInfo.CurrentCulture,
"Events were not fired in the expected sequence from element {0}",
executionIndex));
}
}
private static string PrintValues(Queue<int> myCollection)
{
return string.Format( "{{0}}", string.Join(",", myCollection.ToArray()));
}
[Test()]
[ExpectedException(typeof(DivideByZeroException))]
public void FillFuel_Test([Values(1, 5, 10, 100)]float maxFuel)
{
var fuelTank = new FuelTank()
{
MaxFuel = maxFuel
};
var eventHandlerSequence = new Queue<Action<EventHandler>>();
eventHandlerSequence.Enqueue(x => fuelTank.FuelFull.FireEventHandler += x);
//Dealing with a subclass of EventHandler
eventHandlerSequence.Enqueue(x => fuelTank.FuelChanged.FireEventHandler += (o, e) => x(o, e));
ExpectEventSequence(eventHandlerSequence, () => fuelTank.FillFuel());
}
}
Now, when running the tests with NUnit, I noticed the following results:
The first event that got triggered was the FuelChanged event, this causes the fired queue within the method
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence)
to contain {1}.
The next event that triggers is the FuelFull event, which means that the fired queue now contains:
{1,0} as expected according to the question of Nick.
The last event that triggers is the FuelNoLongerEmpty event and this one fails the test.
Note:
As this code does not yet provide an answer to the original question of the fact that lambda's might cause some interference, as the code I provided above, does just the right thing.
The following rules apply to variable scope in lambda expressions:
A variable that is captured will not be garbage-collected until the
delegate that references it goes out of scope.
Variables introduced within a lambda expression are not visible in
the outer method.
A lambda expression cannot directly capture a ref or out parameter
from an enclosing method.
A return statement in a lambda expression does not cause the
enclosing method to return.
A lambda expression cannot contain a goto statement, break statement,
or continue statement whose target is outside the body or in the body
of a contained anonymous function.
So the problem in Nick's original question might be caused by the fact that you enumerate over a queue. While enumerating and passing these directly to a lambda expression, you will work with a reference. A trick might be to actually de-reference it by copying it to a local variable within the scope of the iteration loop. This is exactly what smiech is referring to in his post.
EDIT:
I've just looked into it again for you. Are you sure the 'challenge' that you are having is not just the fact that comparing the fired dictionary's indices to the expectedSequence.Dequeue is happening in reversed order? Please note that queue's are FIFO based, so when dequeueing, it will retrieve the first that is inserted...
I noticed that (according to my code) the fired dictionary contains {1,0} whereas the expectedSequence dictionary contains {0,1}. By looking at the expected events, this is good for the expectedSequence queue. So actually the fired queue (filled within your last code block) is built up incorrectly by means of the 'age' of the eventhandler.
When I change one statement within the code that you provided in the original
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence)
method from
var subscriptions = subscribeActions.ToList();
foreach (var firedIndex in fired)
{
if (firedIndex != expectedSequence.Dequeue())
{
inOrder = false;
break;
}
executionIndex++;
Console.WriteLine("Execution index: {0}", executionIndex);
}
to this:
//When comparing indexes, you'll probably need to reverse the fired queue
fired = new Queue<int>(fired.Reverse());
foreach (var firedIndex in fired)
{
if (firedIndex != expectedSequence.Dequeue())
{
inOrder = false;
break;
}
executionIndex++;
Console.WriteLine("Execution index: {0}", executionIndex);
}
then everything in your test will pass flawlessly, as you can see with this screenshot:
for the first part: yes, it had to do with the way lambdas variable scope. See Access to Modified Closure.
Because I spent some time trying to figure it out, I allow myself to paste the code I've used (all tests passing).
class Test
{
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction)
{
var expectedSequence = new Queue<int>();
for (int i = 0; i < subscribeActions.Count; i++)
expectedSequence.Enqueue(i);
ExpectEventSequence(subscribeActions, triggerAction, expectedSequence);
}
public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence)
{
var fired = new Queue<int>();
var subscriptions = subscribeActions.ToList();
foreach (var subscription in subscriptions)
{
subscription((o, e) =>
{
var index = subscriptions.IndexOf(subscription);
fired.Enqueue(index);
});
}
triggerAction();
var executionIndex = 0;
var inOrder = true;
foreach (var firedIndex in fired)
{
if (firedIndex != expectedSequence.Dequeue())
{
inOrder = false;
break;
}
executionIndex++;
}
if (subscribeActions.Count != fired.Count)
Assert.Fail("Not all events were fired.");
if (!inOrder)
Assert
.Fail(string.Format(
CultureInfo.CurrentCulture,
"Events were not fired in the expected sequence from element {0}",
executionIndex));
}
}
public class Fueled
{
public event EventHandler<FuelEventArgs> FuelChanged = delegate { };
public event EventHandler FuelEmpty = delegate { };
public event EventHandler FuelFull = delegate { };
public event EventHandler FuelNoLongerFull = delegate { };
public event EventHandler FuelNoLongerEmpty = delegate { };
private float fuel;
public float Fuel
{
get{ return fuel; }
private set
{
var adjustedFuel = Math.Max(0, Math.Min(value, MaxFuel));
if (fuel != adjustedFuel)
{
var oldFuel = fuel;
fuel = adjustedFuel;
RaiseCheckFuelChangedEvents(oldFuel);
}
}
}
public void FillFuel()
{
Fuel = MaxFuel;
}
public float MaxFuel { get; set; }
private void RaiseCheckFuelChangedEvents(float oldFuel)
{
FuelChanged(this, new FuelEventArgs(oldFuel, Fuel));
if (fuel == 0)
FuelEmpty(this, EventArgs.Empty);
else if (fuel == MaxFuel)
FuelFull(this, EventArgs.Empty);
if (oldFuel == 0 && Fuel != 0)
FuelNoLongerEmpty(this, EventArgs.Empty);
else if (oldFuel == MaxFuel && Fuel != MaxFuel)
FuelNoLongerFull(this, EventArgs.Empty);
}
}
public class FuelEventArgs : EventArgs
{
public FuelEventArgs(float oldFuel, float fuel)
{
}
}
[TestFixture]
public class Tests
{
[Test()]
public void FillFuel_Test([Values(1, 5, 10, 100)]float maxFuel)
{
var fuelTank = new Fueled()
{
MaxFuel = maxFuel
};
var eventHandlerSequence = new Queue<Action<EventHandler>>();
//Dealing with a subclass of EventHandler
eventHandlerSequence.Enqueue(x => fuelTank.FuelChanged += (o, e) => x(o, e));
eventHandlerSequence.Enqueue(x => fuelTank.FuelFull += x);
Test.ExpectEventSequence(eventHandlerSequence, () => fuelTank.FillFuel());
}
}
Basically I've only changed the order of expected events in test method. If you are still getting incorrect results after changing the loop I think the problem must be outside of your pasted code scope. I'm using VS 2013 community + resharper 8 , nunit 2.6.4.14350
Edit: different approach
I was trying to solve the problem you actually posted, but maybe this will actually be what you want:
wouldn't you consider trying a simplified version of your approach?:
[Test()]
public void FillFuel_Test([Values(1, 5, 10, 100)]float maxFuel)
{
var fuelTank = new Fueled()
{
MaxFuel = maxFuel
};
var expectedEventSequence = new[]
{
"FuelChanged",
"FuelFull"
};
var triggeredEventSequence = new List<string>();
fuelTank.FuelChanged += (o, e) => triggeredEventSequence.Add("FuelChanged");
fuelTank.FuelFull += (o, e) => triggeredEventSequence.Add("FuelFull");
fuelTank.FillFuel();
Assert.AreEqual(expectedEventSequence,triggeredEventSequence);
}

How do I transfer the value of a label to another label that currently is in another form using visual basic?

Increasing my score label 137 points per item I pickup
fMain
//HUD Score
public int Score()
{
labScore.Text = Convert.ToString(score);
labScore.Text = "00000" + score;
if (score >= 10)
labScore.Text = "0000" + score;
if (score >= 100)
labScore.Text = "000" + score;
if (score >= 1000)
labScore.Text = "00" + score;
return score;
}
And I want my labScore2.Text to be the same as labScore.text. But they are in different forms.
fMain2
public void btnIntroducir_Click(object sender, EventArgs e)
{
fMain f = new fMain();
f.Score();
try
{
string n = txtNombre.Text;
int s = int32.Parse(labScore2.Text);
lista[indice] = new Koby(n, s);
indice++;
muestraLista(ref lstJugadores);
txtNombre.Clear();
txtNombre.Enabled = false;
}
what you are doing is putting the value in a string that is not public. Either make the string public and access it from the other form or you can create a class and put all such variables in that class then access them from there when you need.
You could probably create a Custom Event for in FMain2 that would reflect back to your FMain LabScore.Text.
In your FMain2, create the Custom Event. Let's say we create a Custom Event with score as parameter. We will have the following:
public partial class FMain2 : Form
{
public delegate void ScoreChangedEvent(String score); // Event Handler Delegate
public event ScoreChangedEvent ScoreChanged; // Event Handler to subscribe to the Delegate
public FMain2()
{
InitializeComponent();
}
}
In your FMain you could initialize the event and make the necessary changes when event is triggered, like:
private void btnChangeScore_Click(object sender, EventArgs e)
{
FormMain2 FMain2 = new FormMain2();
FMain2.ScoreChanged += new FMain2.ScoreChangedEvent(FMain2_ScoreChanged);
FMain2.Show();
}
void FMain2_ScoreChanged(string score)
{
labScore.Text = score; // This will receive the changes from FMain2 LabScore2.Text
}
Then back in your FMain2 you add the following:
public void btnIntroducir_Click(object sender, EventArgs e)
{
try
{
string n = txtNombre.Text;
int s = int32.Parse(labScore2.Text);
ScoreChanged(labScore2.Text); // This will reflect back to FMain labScore.Text
lista[indice] = new Koby(n, s);
indice++;
muestraLista(ref lstJugadores);
txtNombre.Clear();
txtNombre.Enabled = false;
}
}
So, your final code behind for your FMain2 would be:
public partial class FMain2 : Form
{
public delegate void ScoreChangedEvent(String score); // Event Handler Delegate
public event ScoreChangedEvent ScoreChanged; // Event Handler to subscribe to the Delegate
public FMain2()
{
InitializeComponent();
}
public void btnIntroducir_Click(object sender, EventArgs e)
{
try
{
string n = txtNombre.Text;
int s = int32.Parse(labScore2.Text);
ScoreChanged(labScore2.Text); // This will reflect back to FMain labScore.Text
lista[indice] = new Koby(n, s);
indice++;
muestraLista(ref lstJugadores);
txtNombre.Clear();
txtNombre.Enabled = false;
}
}
}

Event Handlers & delegates (Simple Question)

I have a simple application. Here's how it works. I have a class (MyForm) that inherits from Windows.Forms. It has a button, a label and a textbox. It looks like a chat window.
There's another class (Cliente) that takes an array of strings and it returns a List with a MyForm instance for each element in the array.
I have a third class (Prueba) that makes use of the previous two classes to test them. This class creates four instances of MyForm, and displays them. (I will omit some code and functionality because I know it works correctly.)
I need to be able to type something in one window and when click on the button, it should broadcast this message and display it in all the other windows.
I know I have to use event handlers and delegates, but after hours of looking at tutorials everywhere I can't figure out what to put where.
Would you please help me? If you can point me to a good tutorial or example it'd be enough, but if you can be more specific on my code, it'd be great.
(I can't figure out how to make one instance of MyForm be aware of the other instances, who should be the listener here? I was thinking that Client, but I can't see how to do it.)
Any help will be appreciated!
//MyForm
namespace Dia26 {
//public delegate void ChangedEventHandler(object sender, EventArgs e);
public class MyForm : System.Windows.Forms.Form {
public Button btn = new Button();
public TextBox textbox = new TextBox();
public Label label = new Label();
public Button btnEnviar = new Button();
public delegate void OwnerChangedEventHandler(string newOwner); //~
public event OwnerChangedEventHandler OwnerChanged;
protected void btn_Click(object sender, System.EventArgs e) {
this.Close();
}
protected void btnEnviar_Click(object sender, System.EventArgs e) {
label.Text += textbox.Text + "\n";
textbox.Text = "";
if (this.OwnerChanged != null) {
this.OwnerChanged("something?");
}
}
public MyForm() {
btn.Text = "cerrar";
btn.Left = 400;
btn.Top = 280;
btn.Click += new EventHandler(this.btn_Click);
btnEnviar.Click += new EventHandler(this.btnEnviar_Click);
textbox.Left = 15;
textbox.Top = 20;
textbox.Width = 330;
label.Left = 15;
label.Top = 50;
label.AutoSize = false;
label.Height = 210;
label.Width = 450;
label.BackColor = Color.White;
btnEnviar.Left = 350;
btnEnviar.Top = 17;
btnEnviar.Text = "Enviar";
this.Controls.Add(textbox);
this.Controls.Add(label);
this.Controls.Add(btn);
this.Controls.Add(btnEnviar);
this.SuspendLayout();
this.Name = "MyForm";
this.ResumeLayout(false);
return;
}
}
}
//Cliente.cs
namespace Dia26Prueba {
public class Cliente {
public int creadas;
public int nocreadas;
public List<MyForm> MostrarVentanas(out bool error, ref int creadas, params string[] nombres) {
List<MyForm> list = new List<MyForm>();
int bienCreadas = 0;
foreach (string str in nombres) {
if (str.Length >= 1) {
MyForm mf = new MyForm();
mf.Text = str;
//mf.OwnerChanged += new OwnerChangedEventHandler(mf_OwnerChanged);
list.Add(mf);
mf.Show();
bienCreadas++;
}
}
error = (bienCreadas == creadas);
nocreadas = bienCreadas - creadas;
creadas = bienCreadas;
return list;
}
public void ModificarPosicionYMedidas(MyForm mf, int x = 262, int y = 209, int width = 500, int height = 350) {
mf.Left = x;
mf.Top = y;
mf.Width = width;
mf.Height = height;
}
}
}
// Prueba
namespace Dia29 {
class Prueba {
static void Main(string[] args) {
Cliente cliente = new Cliente();
int n = 4;
Console.WriteLine(cliente.Autor);
if (args.Length != n) {
return;
}
int InstanciasCreadas = n;
bool HayErrores;
List<Dia26.MyForm> list;
list = cliente.MostrarVentanas(
creadas: ref InstanciasCreadas,
error: out HayErrores,
nombres: new string[] { "FirstWindow", "2nd", "3rd", "4th" });
cliente.ModificarPosicionYMedidas(list.ElementAt<MyForm>(0), 0, 0, 512, 384);
cliente.ModificarPosicionYMedidas(list.ElementAt<MyForm>(1), 512, 0, 512, 384);
cliente.ModificarPosicionYMedidas(list.ElementAt<MyForm>(2), 0, 384, 512, 384);
cliente.ModificarPosicionYMedidas(list.ElementAt<MyForm>(3), 512, 384, 512, 384);
for (int i = 0; i < n; i++) {
// .....
Application.Run(list.ElementAt<MyForm>(i));
}
Console.ReadLine();
}
}
}
Here is a small sample. I'm using a interface to remove the coupling between the MainWindow and the ChatWindows.
public class ChatEventArgs : EventArgs
{
public string ChatEventArgs(string message)
{
Message = message;
}
public string Message { get; private set; }
}
public interface IChatMessageProvider
{
event EventHandler<ChatEventArgs> MessageArrived;
void TriggerEvent(object source, ChatEventArgs args);
}
public class MainWindow : IChatMessageProvider
{
public event EventHandler<ChatEventArgs> MessageArrived = delegate{};
public void AddChatWindow()
{
ChatWindow window = new ChatWindow(this);
window.Show();
}
public void TriggerEvent(object source, ChatEventArgs args)
{
MessageArrived(source, args);
}
}
public class ChatWindow :
{
IChatMessageProvider _provider;
public ChatWindow(IChatMessageProvider provider)
{
_provider = provider;
provider.MessageArrived += OnMessage;
}
public void OnMesage(object source, ChatEventArgs args)
{
// since we could have sent the message
if (source == this)
return;
myListBox.Items.Add(args.Message);
}
public void SendButton_Click(object source, EventArgs e)
{
_provider.TriggerEvent(this, new ChatEventArgs(Textbox1.Text));
}
}
There are actualy multiple ways to do it.
Simply make method on Cliente and call it from Prueba. This is simplest and most intuitive solutoin.
Add event to Prueba, pass instance of Prueba to Cliente and let Cliente register to this event.
Use some kind of global static messenger class. Either using events, or simple message passing.

.NET Propertygrid refresh trouble

Property grid do not show new value of selected object.
For example:
o.val = "1";
pg.SelectedObject = o;
o.val = "2";
pg.Refresh();
The property in property grid is still "1";
It is changing only if you click on this property.
Or like that:
o.val = "1";
pg.SelectedObject = o;
o.val = "2";
pg.SelectedObject = o;
but in this case focus will be changed to PropertyGrid.
As I told you in my comment, your code is not enough to understand your issue. Presented like this it should work. Here is mine that works well:
public class Target
{
private int _myInt = 1;
public int MyInt { set; get; }
public static Target target = new Target();
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Button button = new Button()
{
Text = "Click me",
Dock = DockStyle.Bottom
};
Form form = new Form
{
Controls = {
new PropertyGrid {
SelectedObject = Target.target,
Dock = DockStyle.Fill,
},
button
}
};
button.Click += new EventHandler(button_Click);
Application.Run(form);
}
static void button_Click(object sender, EventArgs e)
{
Target.target.MyInt = 2;
Form form = Form.ActiveForm;
(form.Controls[0] as PropertyGrid).Refresh();
}
}
The call to Refresh() actually rebuilds the grid. Remove it and you will see the change only when you click the property.
Cause you just not gave some code, here is a working example.
Just put a Button and a PropertyGrid onto a form.
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication
{
public partial class FormMain : Form
{
Random rand;
MyObject obj;
public FormMain()
{
InitializeComponent();
rand = new Random();
obj = new MyObject();
propertyGrid1.SelectedObject = obj;
}
private void button1_Click(object sender, EventArgs e)
{
obj.MyValue = rand.Next();
obj.IsEnabled = !obj.IsEnabled;
obj.MyText = DateTime.Now.ToString();
propertyGrid1.Refresh();
}
}
public class MyObject : INotifyPropertyChanged
{
private int _MyValue;
public int MyValue
{
get
{
return _MyValue;
}
set
{
_MyValue = value;
NotifyPropertyChanged("MyValue");
}
}
private string _MyText;
public string MyText
{
get
{
return _MyText;
}
set
{
_MyText = value;
NotifyPropertyChanged("MyText");
}
}
private bool _IsEnabled;
public bool IsEnabled
{
get
{
return _IsEnabled;
}
set
{
_IsEnabled = value;
NotifyPropertyChanged("IsEnabled");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
}

Categories

Resources