I'm new to event programming, and I'm obviously misunderstanding something that I'm trying to do.
I have a Windows Forms application that subscribes to events from another class. T
//Class that provides event handler to Windows Forms application.
class Foo
{
public string Value{get; set;}
// Lots of other code
public void OnEventFired(object sender, EventArgs e)
{
// Attempt to access variable Value here.
}
}
From the Windows Form code I'm first setting the variable Value in class Foo before triggering the event that will execute the code in OnEventFired above.
What I'm seeing is that when used in the event handler the variable Value doesn't contain the value that was set before the event was fired (Value is null).
I know I can extend EventArgs to include the variable data, but I'm trying to understand why what I'm doing doesn't work.
Here's a short example which works. Compare this to your code to work out what's wrong.
using System;
using System.Windows.Forms;
class Foo
{
public string Value { get; set; }
public void HandleClick(object sender, EventArgs e)
{
((Control)sender).Text = Value;
}
}
class Program
{
public static void Main()
{
Foo foo = new Foo { Value = "Done" };
Button button = new Button { Text = "Click me!" };
button.Click += foo.HandleClick;
Form form = new Form
{
Controls = { button }
};
Application.Run(form);
}
}
My guess is that you've hooked up the event handler using a different instance of Foo than the one you've set Value in. For example, like this:
Foo foo = new Foo { Value = "Done" };
...
// Different instance of Foo!
button.Click += new Foo().HandleClick;
... but it's hard to tell without seeing any more code.
The only reason that you can not access the variable Value is
Value is not set
You are binding event to a different instance, not the one with the Value been set.
The best would be to get the Value in constructor, so that it is guaranteed that the Value is set.
class Foo
{
public string Value { get; set; }
public Foo(Value value)
{
}
public void HandleClick(object sender, EventArgs e)
{
((Control)sender).Text = Value;
}
}
Related
How to pass a reference to a form object (i.e. TextBox) to a class, so I could specify what text box this instance needs to be working with when creating this instance?
Specific example:
I have a class that processes some text strings. I have few instances of this class.
I also have few text boxes on my form. I have a method in a class that shows some text in a text box. What do I need to do to tell to a specific instance of my class what text box to use when creating this instance? Should be in constructor, something like:
public MyClass (string textString, /ref to a text box/)
This is my class:
public class LogClass
{
private readonly TextBox _textBox;
private string logText;
public string LogText
{
get
{
return logText;
}
set
{
logText = value;
}
}
public void AddToLog(string textString)
{
try
{
if (string.IsNullOrEmpty(textString))
{
throw new ArgumentException("message", nameof(textString));
}
logText = logText+ "\n" + textString;
_textBox.Text = logText;
}
catch (Exception)
{
throw;
}
}
public LogClass(string initialText, TextBox textBox)
{
logText = initialText;
_textBox = textBox;
_textBox.Text = logText;
}
}
And this is my form:
public partial class LogWindow : Form
{
LogClass myLog = new LogClass("this is initial string", logOutputBox);
public LogWindow()
{
InitializeComponent();
}
public string LogTextToPass {
get { return logOutputBox.Text; }
set { logOutputBox.Text = value; }
}
private void buttonWriteLog_Click(object sender, EventArgs e)
{
myLog.AddToLog(inputText.Text);
}
private void logOutputBox_TextChanged(object sender, EventArgs e)
{
}
}
Error CS0236 is on this line:
LogClass myLog = new LogClass("this is initial string", logOutputBox);
Error CS0236 A field initializer cannot reference the non-static field, method, or property 'LogWindow.logOutputBox'
logOutputBox is highlighted
You can pass it in any way you want. A constructor argument seems sensible, if the class isn't useable without a TextBox. In the constructor you can subscribe to the TextBox's events.
class TextBoxHandler
{
private readonly TextBox _textbox;
public TextBoxHandler(TextBox textbox)
{
_textbox = textbox;
_textbox.Click += HandleClick;
}
public void HandleClick(object sender, EventArgs e)
{
//Do something
}
}
It should be as easy as, passing a reference to it in the Constructor
Whenever a class or struct is created, its constructor is called. A
class or struct may have multiple constructors that take different
arguments. Constructors enable the programmer to set default values,
limit instantiation, and write code that is flexible and easy to read.
public class MyLovelyHorse
{
// private field of TextBox to play with internally
private readonly TextBox _textbox;
// constructor
public MyLovelyHorse(TextBox textbox)
{
_textbox = textbox;
}
// some awesome method that does stuff
public void SomeMethodThatDoesStuff()
{
_textbox.Text = "rah";
}
}
Usage
var myLovelyHorse = new MyLovelyHorse(MyTextBox);
// do stuff
myLovelyHorse.SomeMethodThatDoesStuff();
I tried like this but it will return null values,
public event EventHandler<EventArgs> myevent;
public void Btn_Click(object sender, EventArgs e)
{
if (myevent!= null) //Here I get null value.
myevent(null, new EventArgs());
}
How to achieve the event fire?
Edit:
I have a UserControl in that user control which contain button event,inside the ButtonClick method I created this event.
I have a Form. In that form i m using this UserControl. So i need to Fire a event from User Control button click event to Form page particular function.
I wrote up a very simple, and basic solution for you. Please read the comments in the code to make sense of the solution. If anything is unclear, please ask questions.
Below sample, will cause the event to fire, if the person's name changes:
Here is the Person object:
public class Person
{
//Event to register to, when you want to capture name changed of person
public event EventHandler<NameChangedEventArgs> nameChangedEvent;
//Property
private string _name;
public string Name
{
get { return _name; }
set
{
this._name = value;
//Call the event. This will trigger the OnNameChangedEvent_Handler
OnNameChangedEvent_Handler(new NameChangedEventArgs() {NewName = value});
}
}
private void OnNameChangedEvent_Handler(NameChangedEventArgs args)
{
//Check if event is null, if not, invoke it.
nameChangedEvent?.Invoke(this, args);
}
}
//Custom event arguments class. This is the argument type passed to your handler, that will contain the new values/changed values
public class NameChangedEventArgs : EventArgs
{
public string NewName { get; set; }
}
Here is the code that instantiates and uses the Person object:
class Program
{
static void Main(string[] args)
{
//Instantiate person object
var person = new Person();
//Give it a default name
person.Name = "Andrew";
//Register to the nameChangedEvent, and tell it what to do if the person's name changes
person.nameChangedEvent += (sender, nameChangedArgs) =>
{
Console.WriteLine(nameChangedArgs.NewName);
};
//This will trigger the nameChanged event.
person.Name = "NewAndrewName";
Console.ReadKey();
}
}
can you please try the below way of calling event :
public event EventHandler myevent;
myevent += new EventHandler(Button1_Click);
if (myevent != null) //Here I get null value.
myevent(1, new EventArgs());
I have a main form (let's call it frmHireQuote) that is a child of a main MDI form (frmMainMDI), that shows another form (frmImportContact) via ShowDialog() when a button is clicked.
When the user clicks the 'OK' on frmImportContact, I want to pass a few string variables back to some text boxes on frmHireQuote.
Note that there could be multiple instances of frmHireQuote, it's obviously important that I get back to the instance that called this instance of frmImportContact.
What's the best method of doing this?
Create some public Properties on your sub-form like so
public string ReturnValue1 {get;set;}
public string ReturnValue2 {get;set;}
then set this inside your sub-form ok button click handler
private void btnOk_Click(object sender,EventArgs e)
{
this.ReturnValue1 = "Something";
this.ReturnValue2 = DateTime.Now.ToString(); //example
this.DialogResult = DialogResult.OK;
this.Close();
}
Then in your frmHireQuote form, when you open the sub-form
using (var form = new frmImportContact())
{
var result = form.ShowDialog();
if (result == DialogResult.OK)
{
string val = form.ReturnValue1; //values preserved after close
string dateString = form.ReturnValue2;
//Do something here with these values
//for example
this.txtSomething.Text = val;
}
}
Additionaly if you wish to cancel out of the sub-form you can just add a button to the form and set its DialogResult to Cancel and you can also set the CancelButton property of the form to said button - this will enable the escape key to cancel out of the form.
I normally create a static method on form/dialog, that I can call. This returns the success (OK-button) or failure, along with the values that needs to be filled in.
public class ResultFromFrmMain {
public DialogResult Result { get; set; }
public string Field1 { get; set; }
}
And on the form:
public static ResultFromFrmMain Execute() {
using (var f = new frmMain()) {
var result = new ResultFromFrmMain();
result.Result = f.ShowDialog();
if (result.Result == DialogResult.OK) {
// fill other values
}
return result;
}
}
To call your form;
public void MyEventToCallForm() {
var result = frmMain.Execute();
if (result.Result == DialogResult.OK) {
myTextBox.Text = result.Field1; // or something like that
}
}
Found another small problem with this code... or at least it was problematic when I tried to implement it.
The buttons in frmMain do not return a compatible value, using VS2010 I added the following and everything started working fine.
public static ResultFromFrmMain Execute() {
using (var f = new frmMain()) {
f.buttonOK.DialogResult = DialogResult.OK;
f.buttonCancel.DialogResult = DialogResult.Cancel;
var result = new ResultFromFrmMain();
result.Result = f.ShowDialog();
if (result.Result == DialogResult.OK) {
// fill other values
}
return result;
}
}
After adding the two button values, the dialog worked great!
Thanks for the example, it really helped.
delegates are the best option for sending data from one form to another.
public partial class frmImportContact : Form
{
public delegate void callback_data(string someData);
public event callback_data getData_CallBack;
private void button_Click(object sender, EventArgs e)
{
string myData = "Top Secret Data To Share";
getData_CallBack(myData);
}
}
public partial class frmHireQuote : Form
{
private void Button_Click(object sender, EventArgs e)
{
frmImportContact obj = new frmImportContact();
obj.getData_CallBack += getData;
}
private void getData(string someData)
{
MessageBox.Show("someData");
}
}
I just put into constructor something by reference, so the subform can change its value and main form can get new or modified object from subform.
If you want to pass data to form2 from form1 without passing like new form(sting "data");
Do like that
in form 1
using (Form2 form2= new Form2())
{
form2.ReturnValue1 = "lalala";
form2.ShowDialog();
}
in form 2 add
public string ReturnValue1 { get; set; }
private void form2_Load(object sender, EventArgs e)
{
MessageBox.Show(ReturnValue1);
}
Also you can use value in form1 like this if you want to swap something in form1
just in form1
textbox.Text =form2.ReturnValue1
I use MDI quite a lot, I like it much more (where it can be used) than multiple floating forms.
But to get the best from it you need to get to grips with your own events. It makes life so much easier for you.
A skeletal example.
Have your own interupt types,
//Clock, Stock and Accoubts represent the actual forms in
//the MDI application. When I have multiple copies of a form
//I also give them an ID, at the time they are created, then
//include that ID in the Args class.
public enum InteruptSource
{
IS_CLOCK = 0, IS_STOCKS, IS_ACCOUNTS
}
//This particular event type is time based,
//but you can add others to it, such as document
//based.
public enum EVInterupts
{
CI_NEWDAY = 0, CI_NEWMONTH, CI_NEWYEAR, CI_PAYDAY, CI_STOCKPAYOUT,
CI_STOCKIN, DO_NEWEMAIL, DO_SAVETOARCHIVE
}
Then your own Args type
public class ControlArgs
{
//MDI form source
public InteruptSource source { get; set; }
//Interrupt type
public EVInterupts clockInt { get; set; }
//in this case only a date is needed
//but normally I include optional data (as if a C UNION type)
//the form that responds to the event decides if
//the data is for it.
public DateTime date { get; set; }
//CI_STOCKIN
public StockClass inStock { get; set; }
}
Then use the delegate within your namespace, but outside of a class
namespace MyApplication
{
public delegate void StoreHandler(object sender, ControlArgs e);
public partial class Form1 : Form
{
//your main form
}
Now either manually or using the GUI, have the MDIparent respond to the events of the child forms.
But with your owr Args, you can reduce this to a single function. and you can have provision to interupt the interupts, good for debugging, but can be usefull in other ways too.
Just have al of your mdiparent event codes point to the one function,
calendar.Friday += new StoreHandler(MyEvents);
calendar.Saturday += new StoreHandler(MyEvents);
calendar.Sunday += new StoreHandler(MyEvents);
calendar.PayDay += new StoreHandler(MyEvents);
calendar.NewYear += new StoreHandler(MyEvents);
A simple switch mechanism is usually enough to pass events on to appropriate forms.
First you have to define attribute in form2(child) you will update this attribute in form2 and also from form1(parent) :
public string Response { get; set; }
private void OkButton_Click(object sender, EventArgs e)
{
Response = "ok";
}
private void CancelButton_Click(object sender, EventArgs e)
{
Response = "Cancel";
}
Calling of form2(child) from form1(parent):
using (Form2 formObject= new Form2() )
{
formObject.ShowDialog();
string result = formObject.Response;
//to update response of form2 after saving in result
formObject.Response="";
// do what ever with result...
MessageBox.Show("Response from form2: "+result);
}
I raise an event in the the form setting the value and subscribe to that event in the form(s) that need to deal with the value change.
So i have 2 forms.
Form 1 is my main form, and form 2 is where I enter text in a textbox to be displayed on a label on form 1. Also the button to 'confirm' and actually change the entered text of my label is on form 2 which needs to stay that way.
for some reason this does not work.
Form 2 has a text-box and a button, when I press the button, it changes the string value of the designated string.
This string is linked to a label on form 1. the string is being changed so that is not the problem I confirmed this by a adding a button which pops up a message box showing the new string value.
While searching for an answer I found that is must be some sort of refreshing problem, I tried a lot of methods with no success. Only methods that did work where those who would put my button onto form 1 instead of 2.
I've been googling for 3 hours straight on how to fix this problem but either the methods don't work or they change my button from form 2 to my main form (form 1).
Please don't call me lazy I really can't find a method that works!
EDIT:
Code
GameScreen.cs
namespace _2pGame
{
public partial class GameScreen : Form
{
public GameScreen()
{
InitializeComponent();
P1NameLabel.Text = gm.P1Name;
P1ClassLabel.Text = gm.P1Class;
P2NameLabel.Text = gm.P2Name;
P2ClassLabel.Text = gm.P2Class;
}
private void PlayerInfoButton_Click(object sender, EventArgs e)
{
PlayerInfo playerinfoload = new PlayerInfo();
playerinfoload.Show();
}
}
}
PlayerInfo.cs
namespace _2pGame
{
public partial class PlayerInfo : Form
{
public PlayerInfo()
{
InitializeComponent();
}
public void ConfirmPlayerInfo_Click(object sender, EventArgs e)
{
gm.P1Class = P1ClassChoice.Text;
gm.P1Name = P1TextBox.Text;
gm.P2Class = P2ClassChoice.Text;
gm.P2Name = P2TextBox.Text;
}
}
}
Refs.cs
namespace _2pGame
{
public partial class gm
{
public static string
P1Class,
P2Class,
P1Name,
P2Name;
}
}
An approach to this very well know situation is through delegates....
In your PlayerInfo form declare
public partial class PlayerInfo : Form
{
// define the delegate type (a parameterless method that returns nothing)
public delegate void OnConfirmPlayer();
// declare a public variable of that delegate type
public OnConfirmPlayer PlayerConfirmed;
.....
public void ConfirmPlayerInfo_Click(object sender, EventArgs e)
{
gm.P1Class = P1ClassChoice.Text;
gm.P1Name = P1TextBox.Text;
gm.P2Class = P2ClassChoice.Text;
gm.P2Name = P2TextBox.Text;
// Check is someone is interested to be informed of this change
// If someone assign a value to the public delegate variable then
// you have to call that method to let the subscriber know
if (PlayerConfirmed != null)
PlayerConfirmed();
}
}
Then in your GameScreen form, just before showing the PlayerInfo form, set the public PlayerInfo.PlayerConfirmed to a method into the GameScreen form class
private void PlayerInfoButton_Click(object sender, EventArgs e)
{
PlayerInfo playerinfoload = new PlayerInfo();
// Subscribe to the notification from PlayerInfo instance
playerinfoload.PlayerConfirmed += PlayerHasBeenConfirmed;
playerinfoload.Show();
}
// Method that receives the notification from PlayerInfo
private void PlayerHasBeenConfirmed()
{
P1NameLabel.Text = gm.P1Name;
P1ClassLabel.Text = gm.P1Class;
P2NameLabel.Text = gm.P2Name;
P2ClassLabel.Text = gm.P2Class;
}
This approach has the advantage to avoid a coupling between the GameScreen and the PlayerInfo. No need to know inside the PlayerInfo the existance of a GameScreen form and the name of its properties. You just publish a delegate that a subscriber could register to be informed of the changes and let the subscriber acts on its own code.
You need a reference to your main form and assign the textbox values each time they need to be updated.
public partial class PlayerInfo : Form
{
private readonly GameScreen _main;
public PlayerInfo(GameScreen main)
{
_main = main;
InitializeComponent();
}
public void ConfirmPlayerInfo_Click(object sender, EventArgs e)
{
gm.P1Class = P1ClassChoice.Text;
gm.P1Name = P1TextBox.Text;
gm.P2Class = P2ClassChoice.Text;
gm.P2Name = P2TextBox.Text;
main.P1NameLabel.Text = gm.P1Name;
main.P1ClassLabel.Text = gm.P1Class;
main.P2NameLabel.Text = gm.P2Name;
main.P2ClassLabel.Text = gm.P2Class;
}
}
You also need to pass the reference when the PlayerInfo form is created
private void PlayerInfoButton_Click(object sender, EventArgs e)
{
PlayerInfo playerinfoload = new PlayerInfo(this); //pass ref to self
playerinfoload.Show();
}
Note that there are other better ways to do this, but this is the easiest that I can think of.
You can probably look at events or Mediator pattern if you want something better.
I have a main form (let's call it frmHireQuote) that is a child of a main MDI form (frmMainMDI), that shows another form (frmImportContact) via ShowDialog() when a button is clicked.
When the user clicks the 'OK' on frmImportContact, I want to pass a few string variables back to some text boxes on frmHireQuote.
Note that there could be multiple instances of frmHireQuote, it's obviously important that I get back to the instance that called this instance of frmImportContact.
What's the best method of doing this?
Create some public Properties on your sub-form like so
public string ReturnValue1 {get;set;}
public string ReturnValue2 {get;set;}
then set this inside your sub-form ok button click handler
private void btnOk_Click(object sender,EventArgs e)
{
this.ReturnValue1 = "Something";
this.ReturnValue2 = DateTime.Now.ToString(); //example
this.DialogResult = DialogResult.OK;
this.Close();
}
Then in your frmHireQuote form, when you open the sub-form
using (var form = new frmImportContact())
{
var result = form.ShowDialog();
if (result == DialogResult.OK)
{
string val = form.ReturnValue1; //values preserved after close
string dateString = form.ReturnValue2;
//Do something here with these values
//for example
this.txtSomething.Text = val;
}
}
Additionaly if you wish to cancel out of the sub-form you can just add a button to the form and set its DialogResult to Cancel and you can also set the CancelButton property of the form to said button - this will enable the escape key to cancel out of the form.
I normally create a static method on form/dialog, that I can call. This returns the success (OK-button) or failure, along with the values that needs to be filled in.
public class ResultFromFrmMain {
public DialogResult Result { get; set; }
public string Field1 { get; set; }
}
And on the form:
public static ResultFromFrmMain Execute() {
using (var f = new frmMain()) {
var result = new ResultFromFrmMain();
result.Result = f.ShowDialog();
if (result.Result == DialogResult.OK) {
// fill other values
}
return result;
}
}
To call your form;
public void MyEventToCallForm() {
var result = frmMain.Execute();
if (result.Result == DialogResult.OK) {
myTextBox.Text = result.Field1; // or something like that
}
}
Found another small problem with this code... or at least it was problematic when I tried to implement it.
The buttons in frmMain do not return a compatible value, using VS2010 I added the following and everything started working fine.
public static ResultFromFrmMain Execute() {
using (var f = new frmMain()) {
f.buttonOK.DialogResult = DialogResult.OK;
f.buttonCancel.DialogResult = DialogResult.Cancel;
var result = new ResultFromFrmMain();
result.Result = f.ShowDialog();
if (result.Result == DialogResult.OK) {
// fill other values
}
return result;
}
}
After adding the two button values, the dialog worked great!
Thanks for the example, it really helped.
delegates are the best option for sending data from one form to another.
public partial class frmImportContact : Form
{
public delegate void callback_data(string someData);
public event callback_data getData_CallBack;
private void button_Click(object sender, EventArgs e)
{
string myData = "Top Secret Data To Share";
getData_CallBack(myData);
}
}
public partial class frmHireQuote : Form
{
private void Button_Click(object sender, EventArgs e)
{
frmImportContact obj = new frmImportContact();
obj.getData_CallBack += getData;
}
private void getData(string someData)
{
MessageBox.Show("someData");
}
}
I just put into constructor something by reference, so the subform can change its value and main form can get new or modified object from subform.
If you want to pass data to form2 from form1 without passing like new form(sting "data");
Do like that
in form 1
using (Form2 form2= new Form2())
{
form2.ReturnValue1 = "lalala";
form2.ShowDialog();
}
in form 2 add
public string ReturnValue1 { get; set; }
private void form2_Load(object sender, EventArgs e)
{
MessageBox.Show(ReturnValue1);
}
Also you can use value in form1 like this if you want to swap something in form1
just in form1
textbox.Text =form2.ReturnValue1
I use MDI quite a lot, I like it much more (where it can be used) than multiple floating forms.
But to get the best from it you need to get to grips with your own events. It makes life so much easier for you.
A skeletal example.
Have your own interupt types,
//Clock, Stock and Accoubts represent the actual forms in
//the MDI application. When I have multiple copies of a form
//I also give them an ID, at the time they are created, then
//include that ID in the Args class.
public enum InteruptSource
{
IS_CLOCK = 0, IS_STOCKS, IS_ACCOUNTS
}
//This particular event type is time based,
//but you can add others to it, such as document
//based.
public enum EVInterupts
{
CI_NEWDAY = 0, CI_NEWMONTH, CI_NEWYEAR, CI_PAYDAY, CI_STOCKPAYOUT,
CI_STOCKIN, DO_NEWEMAIL, DO_SAVETOARCHIVE
}
Then your own Args type
public class ControlArgs
{
//MDI form source
public InteruptSource source { get; set; }
//Interrupt type
public EVInterupts clockInt { get; set; }
//in this case only a date is needed
//but normally I include optional data (as if a C UNION type)
//the form that responds to the event decides if
//the data is for it.
public DateTime date { get; set; }
//CI_STOCKIN
public StockClass inStock { get; set; }
}
Then use the delegate within your namespace, but outside of a class
namespace MyApplication
{
public delegate void StoreHandler(object sender, ControlArgs e);
public partial class Form1 : Form
{
//your main form
}
Now either manually or using the GUI, have the MDIparent respond to the events of the child forms.
But with your owr Args, you can reduce this to a single function. and you can have provision to interupt the interupts, good for debugging, but can be usefull in other ways too.
Just have al of your mdiparent event codes point to the one function,
calendar.Friday += new StoreHandler(MyEvents);
calendar.Saturday += new StoreHandler(MyEvents);
calendar.Sunday += new StoreHandler(MyEvents);
calendar.PayDay += new StoreHandler(MyEvents);
calendar.NewYear += new StoreHandler(MyEvents);
A simple switch mechanism is usually enough to pass events on to appropriate forms.
First you have to define attribute in form2(child) you will update this attribute in form2 and also from form1(parent) :
public string Response { get; set; }
private void OkButton_Click(object sender, EventArgs e)
{
Response = "ok";
}
private void CancelButton_Click(object sender, EventArgs e)
{
Response = "Cancel";
}
Calling of form2(child) from form1(parent):
using (Form2 formObject= new Form2() )
{
formObject.ShowDialog();
string result = formObject.Response;
//to update response of form2 after saving in result
formObject.Response="";
// do what ever with result...
MessageBox.Show("Response from form2: "+result);
}
I raise an event in the the form setting the value and subscribe to that event in the form(s) that need to deal with the value change.