I have a problem concerning delegates in a Windows.Forms application.
There are two forms:
the main form, which has a button named "Settings".
the "settings" form, this is the "child" form.
When I click the "Settings" button in the main form, it opens an instance of the Settings form.
My problem is that I need to pass a variable to the Settings form, when I open it. So that the new form will show the variable text. I don't know how to retrieve the information in the child "Settings" form. I did this by following a tutorial online and could not understand from the tutorial how to read the info in the destination form.
Here's what I've done so far, the code in the main form:
public partial class MainForm : Form
{
/// <summary>
/// delegate to send data between forms
/// </summary>
public delegate void PageInfoHandler(object sender, PageInfoEventArgs e);
/// <summary>
/// event of the delegate
/// </summary>
public event PageInfoHandler PageInfoRetrieved;
//other stuff, events blabla
private void toolStripBtnSettings_Click(object sender, EventArgs e)
{
PageInfoEventArgs args = new PageInfoEventArgs(SomeString);
this.OnPageInfoRetrieved(args);
SettingsForm settingsForm = new SettingsForm();
settingsForm.ShowDialog();
}
private void OnPageInfoRetrieved(PageInfoEventArgs args)
{
if (PageInfoRetrieved != null)
PageInfoRetrieved(this, args);
}
}
Pass any information you want to in to the constructor of Settings form, and provide accessor methods for things you need out of there.
public class SettingsForm : WinForm
{
private string m_Data;
private int m_nExample = 0;
// ctor
public SettingsForm(string _data)
{
m_Data = data; // you can now use this in SettingsForm
} // eo ctor
public int Example {get{return(m_nExample);} }
} // eo class SettingsForm
In the above "example" you can construct a SettingForm with a string and get at an integer it may use. In your code:
private void toolStripBtnSettings_Click(object sender, EventArgs e)
{
PageInfoEventArgs args = new PageInfoEventArgs(SomeString);
this.OnPageInfoRetrieved(args);
SettingsForm settingsForm = new SettingsForm("some data to pass");
settingsForm.ShowDialog();
int result = settingsForm.Example; // retrieve integer that SettingsForm used
}
The Setttings form is a class. It's yours now and you can do what you like with it. So add a parameter (or however many you want) to its constructor. Then in your MainForm call SettingsForm(whatever) and you're all set.
I would suggest adding a property to SettingsForm.
Then, call it like this:
SettingsForm settingsForm = new SettingsForm();
settingform.myProperty = myPropertyvalue;
settingsForm.ShowDialog();
Why don't you add a constructor to your settings form which takes parameters and pass in any data you need there?
You can create a parametrized constructor for your settings form which accepts the text, and sets it to a property in the form
public partial class SettingsForm : Form
{
public string DisplayText {get;set;}
public SettingsForm(string text)
{
DisplayText = text;
}
}
then, you'd just initialize the settings from like this (From your mainform)
var settingsForm = new SettingsForm("my init text");
your settings form will be properly initialized, and you have the desired text in the DisplayText property ready to use
Related
Currently I have multiple classes, one of which is called the 'Variable class' where I
{get;set;}
my values obtained from other classes. Accessing these values in voids is simply:
Private void (Variables vb)
{
}
However in the 'Load' part of Winforms,
private void Form1_Load(object sender, EventArgs e)
{
}
From the Variables Class:
public class Variables
{
public int Node { get; set; }
}
The object sender, EventArgs e part is occupying the space where I place the arguments. Is there any way that I could obtain Node from the class Variableson the winform?
Your method Form1_Load is an event handler (because it usually gets called as a result of some event occurring). The "Load" event is defined by WinForms, so you cannot change the fact that the arguments are object sender and EventArgs e.
WinForms creates one instance of your Form1 class before it displays your form. Whenever an event happens on your form, the event handler on that same object is called.
So, you can store values in fields and properties of your Form1 class:
public class Form1 : Form
{
Variables _myVariables;
public Form1()
{
InitializeComponent();
_myVariables = new Variables() { Node = 10 }
}
private void Form1_Load(object sender, EventArgs e)
{
MessageBox.Show("The current value of _myVariables.Node is: " + _myVariables.Node);
}
}
If your Variables object is created outside your form, then you can pass it into your Form1 constructor:
public class Form1 : Form
{
Variables _myVariables;
public Form1(Variables variables)
{
InitializeComponent();
_myVariables = variables;
}
// ...
}
// Then, somewhere else:
var variables = new Variables() { Node = 10 };
var myForm = new Form1(variables);
myForm.Show();
// or: Application.Run(myForm);
I'm not 100% sure if this is what you are looking for, but I think I can help.
namespace Example
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//class which can be used anywhere in the namespace
public class exampleClass
{
public const string SETSTATE = "0";
public const string GETSTATE = "1";
public const string SETVALUE = "2";
public const string GETVALUE = "3";
public const string SENDFILE = "4";
public const string STATE_RP = "129";
public const string VALUE_RP = "131";
public const string STATUS_RP = "128";
}
}
}
you can use exampleClass, and any of its enclosed members, anywhere within Form1 here. You do not need to pass it anywhere within the form to use it. You could add a function later that uses it directly like:
void exampleF()
{
//note that when you are accessing properties of UI elements from
//a non-UI thread, you must use a background worker or delegate
//to ensure you are doing so in a threadsafe way. thats a different problem though.
this.txtYourTextBox.txt = exampleClass.GETSTATE;
}
Maybe your attempt is it actually to use MVP-Pattern in WinForms. Very good idea.
Then you can use DataBinding to Bind your Forms-Controls to your "Variables classes" properties. Your variable class takes the presenter role, your form is the view, and your model(s) are the sources of your variable classes data.
Unfortunately, the pattern uses some advanced mechanisms you have to deal with.
For more information, you might take a first look here:
Databinding in MVP winforms
I have a main Windows Form (From1) which has a TextBox in it. I've also created another Windows Form (FindReplaceForm) which I'm going to use for implementing a form of find and replace dialog. I need to fully access all the properties and methods of my textbox in From1 from FindReplaceForm window.
Although I set the Modifiers property of my TextBox to Public, in FindReplaceForm window I can't access the text in it.
You can access the the owner form in the child using:
((Form1)Owner).textBox1.Text = "blah";
Assuming you have called your the child form using:
Form2 form = new Form2();
form.Show(this);
You need to pass a reference to the control or the form to your constructor so that you can reference the instance of the class. Add an argument of the same type as the calling form to the constructor:
private Form1 calling_form;
public FindReplaceForm (Form1 calling_form)
{
this.InitializeComponent()
this.calling_form = calling_form;
}
Then in your button call you can say:
calling_form.TextBox_value_text.SelectedText = "";
In your code, Form1 is a CLASS, not a variable. When you show your FindReplaceForm you should specify the Owner (just use this).
Now you can the Owner property on FindReplaceForm to get access to Form1.
Showing FindReplaceForm:
FindReplaceForm.Show(this);
In your button click event:
void Buttton1_Click(object sender, EventArgs e)
{
((Form1)this.Owner).TextBox_value_text.SelectedText = "";
}
Even if you set the textbox access modifier back to private, you can simply pass a reference to the textbox in the second form's constructor, thus enabling the second form to manipulate any property of the textbox:
first form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Form2 frm = new Form2(this.textBox1);
frm.Show();
}
}
second form:
public partial class Form2 : Form
{
private TextBox _OwnerTextBox;
public Form2(TextBox ownerTextBox)
{
InitializeComponent();
_OwnerTextBox = ownerTextBox;
this.textBox1.Text = _OwnerTextBox.Text;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
_OwnerTextBox.Text = this.textBox1.Text;
}
}
In your FindReplaceForm.button1_Click function you want to control an object of class Form1. The error in your code explains that you don't call a function of an object of class Form1, but a function of class Form1 itself. The latter can only be done on static functions
You describe that you have an existing Form1 object and that your FindReplaceForm needs to change Form1 by calling functions in Form1. Probably there might be more than 1 Form1 object. Therefor your FindReplaceForm instance somehow needs to know which Form1 object it needs to control. Someone needs to tell this to your FindReplaceForm. This is usually done using the constructor of the FindReplaceForm or using a property.
public class FindReplaceForm
{
private Form1 formToControl = null;
public FindReplaceForm(Form1 formToControl)
{
this.formToControl = formToControl;
}
private void OnButton1_Click(...)
{
this.formToControl.TextBox_Value_Text.SelectedText = ...
}
Another method would be not to put the formToControl in the constructor, but as a property that you can set separately. Both methods have their advantages:
via constructor: your FindReplaceForm knows for sure there is alway a formToControl. The only place you have to check if formToControl really exists is in the constructor.
Using a default constructor with a separate FormToControl property may cause run time errors if people forget to set the FormToControl.
If you have a lot of properties, or some properties may have default values, or may be changed later, then the property method is more flexible.
I have a C# form called Form1.cs. By pressing a button, a new Form called Form2.cs comes up and I do something in form2. I have some variables in both forms.
I want to communicate between these two forms like this.
in form1:
string s=frm2.textbox1.text;`
form2:
if(frm1.checkbox1.checked==true)
or something like these codes.
I have tried the below code:
form1:
Form2 f=new Form2(this);
f.showDialog();`
form2:
private Form1 mainForm = null;
public Form2(Form callingForm)
{
mainForm = callingForm as Form1;
InitializeComponent();
}
`
and this works. But I don't want to use pointers like "this" and call this.mainform. Is there another way to communicate between forms like function calls?
Thank you.
Here are a couple of different approaches you can take that remove the need for Form 2 to know about Form 1 and that will make Form 2 reusable:
Delegates
You can declare a delegate function on the second form, and then pass a delegate method from the first form to the second one, so the second form can call back to the first one.
That approach means your second form no longer has any direct knowledge of your first form. It also means you can reuse the second form from any form and just pass in a delegate with the correct signature. Example below:
Form1:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Form2 f=new Form2(UpdateTextBox);
f.ShowDialog();
}
private void UpdateTextBox(string newText)
{
label1.Text = newText;
}
}
Form 2:
public partial class Form2 : Form
{
public delegate void ChoiceMadeOnForm2Delegate(string choice);
private ChoiceMadeOnForm2Delegate _choiceDelegate;
public Form2(ChoiceMadeOnForm2Delegate choiceDelegate)
{
InitializeComponent();
_choiceDelegate = choiceDelegate;
}
private void saveButton_Click(object sender, EventArgs e)
{
_choiceDelegate(textBox1.Text);
Close();
}
}
In this example the delegate method just has one parameter, but if you want to pass back a series of values to Form 1 your delegate method declaration could include more parameters, or the parameter could be a class instead.
If you also want to set initial values for Form2 from Form 1 you can of course add those as constructor parameters for Form 2 and pass them in when you new up Form 2.
In your example Form 2 is shown as a dialog, but if you ever want to not show Form 2 modally you could even have a delegate on Form 1 that you pass to Form 2, so the forms can then communicate in two directions.
Use data binding and a shared class
Another approach is to use databinding, whereby you bind both forms to the same object and pass that object from Form 1 to Form 2 in its constructor when you open Form 2. When either form then changes the object those changes will then be reflected on both forms simultaneously and instantly.
To do that you need to read up on a concept called databinding and implement the INotifyPropertyChanged interface on the data class. You then bind the relevant controls on both forms to that class. Documentation on INotifyPropertyChanged can be found here
You can pass only information that Form2 needs and expose only information that Form1 needs
Form1
string valueOfForm2 = null;
using Form2 f = new Form2(this.checkbox1.Checked)
{
f.ShowDialog();
valueOfForm2 = f.ReturnValue;
}
Then use valueOfForm2 for you needs in the Form1
Form2
bool _IsForm1ValueChecked;
//By this property text will be exposed to outside of Form
public string ReturnValue { get { return this.textbox1.Text;} }
public Form2(bool isForm1ValueChecked)
{
_IsForm1ValueChecked = isForm1ValueChecked;
}
_IsForm1ValueChecked will be set in the contructor - then use it for your purpose in the Form2
I think for such stuff, I was using properties.
I prefer not access from one form the controls of the other one.
If I need information from one form, I prefer giving the access to this other form through properties.
More than that, you can define an interface that will contain all the properties/methods that you need for the communication between the forms. It will be clearer to work with an interface, you will get the information you need and won't be overloaded with other irrelevant information.
For example:
Interface IForm2
{
// your properties
string PersonName {get;} // Just an example
// your methods
}
class Form1: Form
{
private IForm2 _form2;
void Foo()
{
var pname = _form2.PersonName; // Just an example
}
}
class Form2: Form, IForm2
{
string PersonName
{
get
{
return personNameTextBox.Text;
}
}
}
For example after creating a new Windows Form project I have my class called Form1.cs and from that form I can simply start typing the name of a form control and it will auto populate the form control variable names and I am able to use them in the class. However I have other classes that need to be able to access these form control variables as well, but they are not accessible.
Make them public if they are going to be used in another assembly, or internal if they are going to be used in the same project. Making them static means you don't have to pass your Form1 into the other classes.
Example... Say your Form1 has a string that contains the text you display in the title bar. Making it internal static, like this:
internal static readonly string MsgBox_Title = " Best Application Evar!";
lets you access it from other classes like this:
Form1.MsgBox_Title
It doesn't have to be readonly; that's just an example I pulled from an old app...
If you don't want static variables, you'll have to pass in an instance of Form1.
public class SomeClass
{
private Form1 m_Form1;
public SomeClass(Form1 form1)
{
m_Form1 = form1;
}
private void someMethod()
{
string localValue = m_Form1.SomeMemberStringVariable;
}
}
It's a very contrived example, but hopefully you get the idea.
If you want to call the Refresh method from a class instantiated from Form1, you could use an event in the child class to notify Form1.
Example:
This Form1 has a button that I use to show a secondary form.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnShowPopup_Click(object sender, EventArgs e)
{
PopupForm f = new PopupForm();
f.CallRefreshHandler += PopupForm_CallRefreshHandler;
f.ShowDialog();
}
private void PopupForm_CallRefreshHandler(object sender, EventArgs e)
{
Refresh();
}
}
The secondary form, "PopupForm", has a button that I use to raise an event that the Form1 is subscribed to, and lets Form1 know to call Refresh.
public partial class PopupForm : Form
{
public event EventHandler CallRefreshHandler;
public PopupForm()
{
InitializeComponent();
}
private void btnRaiseEvent_Click(object sender, EventArgs e)
{
EventHandler handler = CallRefreshHandler;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
Hope this helps.
Create an object of that class & start using those variables like this
Form1 fm = new Form1();
string abc = fm.VAR;
Define a public property in your form.
public string MyProp { get; set; }
Form1 frm = new Form1();
frm.MyProp = "Value";
Or define the property as static to avoid having to instantiate Form1:
public static string MyProp { get; set; }
Form1.MyProp = "Value";
I ran into this issue recently. I was keeping some methods in a separate class. Maybe not a good design decision in my case, I'm not sure yet. And these methods sometimes needed to communicate with controls in the main Form1. For example, to write to textBox1.
Turns out easy enough. Just write your method signature to include a TextBox instance. For example you pass textBox1 in and inside the method you refer to it as tb. Then when you call that method (even though it is in another class) you set the tb.Text property to whatever you like and it will show on textBox1.
This makes sense when you consider that control is just a special kind of object, graphically represented in the Form. When you pass it as an argument to a method in another class or the same class, you are actually passing the reference. So writing text to it in the method call will write text to the original control.
So in my program I have a settings page. On the settings page, there is an option to set the program "Always on Top". When this option is checked and unchecked, it properly saves the setting, but it does not actually change TopMost property itself.
The program's main form is called "MainForm", but the settings page is called "SettingsForm". How would I change the "TopMost" property on "MainForm", from within the "SettingsForm"?
You could create an event on Settings form:
public event EventHandler TopMostEvent;
private void OnTopMostEvent()
{
if (TopMostEvent != null)
{
TopMostEvent(this, EventArgs.Empty);
}
}
On CheckedChanged event call the method after saving settings:
OnTopMostEvent();
And in Main form subscribe to the event and set the forms TopMost property
One approach would be to simply give SettingForm a reference to MainForm, e.g. via a constructor parameter which is then stored to a field where it can later be accessed when necessary.
For example:
public class SettingsForm
{
public SettingsForm(MainForm mainForm)
{
this.mainForm = mainForm;
}
public void Apple()
{
this.mainForm.TopMost = true;
}
private readonly MainForm mainForm;
}
public class MainForm
{
public void Banana()
{
var settingsForm = new SettingsForm(this);
settingsForm.ShowDialog();
}
}
(However, it may not be necessary to do this if the owner of SettingsForm is already the insntance of MainForm but this I cannot tell from what you have given.)
This is a good place for a mediator pattern. (Similar to a controller) The idea is you have one object that creates all of your windows and passes a reference to itself into each form through the constructor. You can call a method in the mediator from either form and the mediator will focus the MainForm. It's a very common practice in Windows Forms.
So you'll make a mediator class like so:
public class MyMediator
{
Form mainForm {get;set;}
Form settingsForm{get;set;}
public MyMediator()
{
mainForm = new MainForm(this);
mainForm.Show();
}
...
public FocusMainForm() // call this from settings form
{
mainForm.TopMost = true;
}
}