I have two Forms. One with where all the main code is being executed. And the other form is displayed when clicking a menu item by using this method:
Form2 videoSettings = new Form2();
private void videoToolStripMenuItem_Click(object sender, EventArgs e)
{
videoSettings.Show();
}
The form which is then opened containsfields where the user gets to set some settings for the application.
Then when clicking the "save" button I want this variable: public int deviceIndex;
to be fetched from the original Form.
So I'm wondering if I can add any event or something in Form1 which detects when the save button is clicked in videoSettings (Form2)?
I would do it a different way. I'd separate the code between the UI handling and the business logic layers. So your scenario would run in such a way:
The first form issues an event notifying that the button with certain semantics has been activated. The data needed for the processing is included into the event's data.
The business logic listens to this event, and decides to issue a command on the second form. It calls an appropriate method on the form's class, passing the needed information as a parameter (and maybe preprocessing the parameter if needed).
The second form receives the command from the business logic and updates the view.
This way the problem doesn't arise at all.
Example: (I'm not the winforms expert, beware it can be totally wrong from the POV of best winforms practices.)
Part 1 (first form):
class ProcessingActivatedEventArgs : EventArgs
{
public ProcessingActivatedEventArgs(int data) { MoreData = data; }
public int MoreData { get; protected set; }
}
class Form1 : Form
{
private int currentData;
public event EventHandler<ProcessingActivatedEventArgs> ProcessingActivated;
void OnButtonClick(object sender, EventArgs args)
{
// ...
if (ProcessingActivated != null)
ProcessingActivated(new ProcessingActivatedEventArgs(currentData));
}
}
Part 2: (business logic)
class Controller
{
Form1 f1;
Form2 f2;
void StartFirstForm()
{
f1 = new Form1();
f1.ProcessingActivated += OnProcessingActivated;
f1.Show();
}
void OnProcessingActivated(object sender, ProcessingActivatedEventArgs args)
{
int data = args.MoreData;
f1.DisableProcessingRequests();
model.ProcessingFinished += OnProcessingFinished;
model.StartProcessing(data);
if (data > 0)
f2.DisplayDataProcessing(0, data);
else if (data < 0)
f2.DisplayDataProcessing(data, 0);
else
throw new SomeCoolException("impossible data");
}
}
Part 3: (second form)
class Form2 : Form
{
public void DisplayDataProcessing(int lower, int upper)
{
// ... update the UI
}
}
Note that this implementation ties the Controller and forms tighter than it could be done. In WPF, the decoupling is achieved by using the appropriate DataContext (but I don't know how to do it properly in WinForms).
Let me suggest another way, something between the simplest ShowDialog() and the elaborated way of separation between business logic and interface.
I wish to create a new event in Form2. I call this event SettingsSaved
In Form2 add as global declaration
public delegate void SettingsSavedEventHandler(object sender, SettingsSavedEventArgs e);
public event SettingsSavedEventHandler SettingsSaved;
and in the cmdSave_Click event
if(SettingsSaved != null)
{
SettingsSavedEventArgs ss = new SettingsSavedEventArgs() { DeviceIndex = deviceIndex};
SettingsSaved(this, ss);
}
the skeleton for the class SettingsSavedEventArgs
public class SettingsSavedEventArgs: EventArgs
{
public int DeviceIndex {get; set;}
// Other settings could be added here
}
now in the code calling the Form2 we can subscribe to the event and get notified when the user clicks on the Form2 Save button
Form2 videoSettings = new Form2();
videoSettings.SettingsSaved += new SettingsSavedEventHandler(SavedHandler);
videoSettings.Show();
....
private void SavedHandler(object sender, SettingsSavedEventArgs ss)
{
int deviceIndex = ss.DeviceIndex;
}
Observer Pattern
There are many suggestions, but I'd like to add my two cents.
You could use form2.ShowDialog(); which will stop the execution of your form1 thread until the form2 is closed. Which means you can just do this from form1:
Form2 videoSettings = new Form2();
//show options
videoSettings.ShowDialog();
//at this point, the user has either clicked save, cancel, or closed the form
//(because the form is closed, obviously :) )
int device = videoSettings.deviceIndex;
If you cant have it locking up your form like that, here is another way using an event in Form2:
Form2 : Form
{
public event EventHandler Saved;
OnSaveButtonClicked(...)
{
if(Saved != null) Saved(this, new EventArgs());
}
}
and then from Form1:
Form2 frm = new Form2();
frm.Saved += (s, e) =>
{
//saved button clicked, retrieve value.
//also could be handled as a method, or really any way.
};
frm.Show();
Maybe you could try to have your second form to implement INotifyPropertyChanged interface. Then when you click on Save, you Raise the PropertyChanged event, and you capture it in the first form.
You can pass information something like this
private Form1 mainForm = null;
public Form2(Form callingForm)
{
mainForm = callingForm as Form1;
InitializeComponent();
}
Then, you can access the Form1 property from Form2 like this:
//Call this in Save button click event
this.mainForm.deviceIndex;
Related
I have a windows form that allows different user controls to show and be hidden by different button.
I would like this user controls to communicate directly with each other and change values etc.
For example usercontrol1 below
User will fill form, then when they click Proceed, usercontrol1 should close and usercontrol2 should now be visible with user information picked and displayed as follows
So far I have the code below
private void btnProceed_Click(object sender, EventArgs e)
{
string name = tbName.ToString();
string email = tbEmail.ToString();
string phone = tbPhone.ToString();
string color = tbColor.ToString();
this.Hide();
UserControl2 u2 = new UserControl2();
MainForm f1 = new MainForm();
f1.panelMain.Controls.Add(u2);
f1.listView1.Items.Add(new ListViewItem(new[]{
name,
email,
phone,
color}));
}
It does not work. Only the usercontol1 is hidden and I'm left with a blank.
What I'm I doing wrong?
(Using visual studio 13 and c# language)
You are adding the new usercontrol to a new instance of MainForm. You can't see it because this new instance is never shown. To avoid this problem in the simplest possible way you pass the instance of the current MainForm to the UserControl constructor, keep that instance stored in a global variable of the UserControl and use that instance when you need to switch the two usercontrols.
In MainForm.cs (when you create the first usercontrol)
UserControl1 uc = new UserControl1(this);
.....
In UserControl1.cs
public class UserControl1
{
MainForm _current;
public UserControl1(MainForm f)
{
InitializeComponent();
_current = f;
}
private void btnProceed_Click(object sender, EventArgs e)
{
.....
UserControl2 u2 = new UserControl2();
_current.panelMain.Controls.Add(u2);
u2.listView1.Items.Add(new ListViewItem(new[]{
name,
email,
phone,
color}));
}
}
This will be problematic to correctly handle for other tasks. I would recommend to redesign your application to let the MainForm decide which usercontrol to show when needed.
In this approach you use events to get informed in MainForm.cs when the user clicks the UserControl1, so you get this in MainForm.cs
UserControl1 uc = new UserControl1();
uc.UserClick += UserAdded;
.....
where UserAdded is a method of MainForm that received the info about the new user though the class UserInfoArgs
public void UserAdded(UserControl1 sender, UserInfoArgs args)
{
sender.Hide();
UserControl2 u2 = new UserControl2();
this.panelMain.Controls.Add(u2);
u2.listView1.Items.Add(new ListViewItem(new[]{
args.name,
args.email,
args.phone,
args.color}));
}
And in UserControl1 you add the delegate, the event and raise the event when you need to communicate to MainForm the info about your new user.
public class UserControl1
{
public delegate void onClick(UserControl1 sender, UserInfoArgs args);
public event onClick UserClick;
....
private void btnProceed_Click(object sender, EventArgs e)
{
UserInfoArgs args = new UserInfoArgs()
{
name = tbName.Text,
email = tbEmail.Text,
phone = tbPhone.Text,
color = tbColor.Text
};
if(UserClick != null)
UserClick(this, args);
}
public class UserInfoArgs
{
public string name {get;set;}
public string email {get;set;}
public string phone {get;set;}
public string color {get;set;}
}
I think using backgroundworker control for this actually works for your case .
private void btnProceed_Click(object sender, EventArgs e)
{
string name = tbName.ToString();
string email = tbEmail.ToString();
string phone = tbPhone.ToString();
string color = tbColor.ToString();
string a = " "+name+" "+email+" "+phone+" "+color;
backgroundWorker1.RunWorkAsync(a);//passing the variables to the backgroundWorker
}
And then in backgroundWorker_doWork(),you can populate the data you're getting as you like .
You are overcomplicating things. You can just layer the two controls on your UI on the same position, and set the second control's Visible property to False.
Make the first user control expose an event for the button's click (or just expose the button so you can access its Click event), so that you can detect that mouse click from the main form. Then, when the listener for that button is called; the main form can get the data out of the first control, use it to initialize the second control, and then hide the first one and show the second one.
I have two forms, on one is month, on another is TextBox.
Problem is that I don't get displayed date on the TextBox after selecting it.
To be certain that I wrote working code, I did same thing just on one form, and it works fine.
Set MonthCalendar to public and same with the TextBox where date needs to be displayed.
Here is the code for Button and for the monthcalendar:
public void mcKalendar_DateChanged()
{
frmNovoVozilo fNv = new frmNovoVozilo();
fNv.txtDatKupovine.Text =
mcKalendar.SelectionRange.Start.ToShortDateString();
}
private void btnDatum_Click(object sender, EventArgs e)
{
frmKalendar fKalen = new frmKalendar();
fKalen.StartPosition = FormStartPosition.CenterScreen;
fKalen.Show();
}
Did try also DateSelected and it's giving me the same result, nothing.
Thanks upfornt.
One method to transfer the value to the previous form, is to use form closing event handlers and get/set methods to extract the value. For example:
form1 = new Form1();
form1.FormClosing += new FormClosingEventHandler(GetDateFromForm);
private void GetDateFromForm(object sender, FormClosingEventArgs e)
{
textbox1.Text = from1.GetDate;
}
then in your month calendar form, use
string date;
public string GetDate {get {return date;} set {date = value;}}
Public Form2()
{
date = monthCalendar1.SelectionStart.ToShortDateString();
}
once you close the calendar, the selected value will be transfered.
Another method is to send a reference to the form you want to apply the changes to in the constructor of the second form.
in your primary form (the one with the button) when you create the popup form, use the syntax Form2 calendarform = new Form2(this) and write a public method that does what you need to
public void SetText(string text)
{
textbox.Text = text;
}
In your second form (the one with the month calendar) you will be able to refer to the previous method. For example:
public partial class Form2 : Form
{
Form1 _owner;
public Form2(Form1 form)
{
InitializeComponent();
_owner = form;
}
private void monthCalendar1_DateChanged(object sender, DateRangeEventArgs e)
{
_owner.SetText(monthCalendar1.SelectionEnd.ToShortDateString());
}
Because we are referencing the previous form in the second form, you are able to call all the public methods that will run on form1. So in this case you can call the SetText() method from form2, which will change the textbox to whatever we want - in your case the selected value of the monthcalendar.
Using the DateChanged method means that it will call said method in real time as you change the date.
I would like to ask how I can change buttons properties in main form from class.
I have form with button called Call. This button is disabled.
I have class which is checking network connection. When is my program connected I want enabled button Call from class network but I can't change properties from this class.
Code
public class network
{
...
MainForm frm = new MainForm();
..
.
.
if(isNetwork==true)
{
frm.Call.Enabled = true; //nothing happends, button is still disabled
methodUpdate(); // second solution with calling method, but it
doesn't work too ...
}
}
MainForm
...
public void methodUpdate()
{
Call.Enabled = true;
}
but when I have another button in main form and in method click I have methodUpdate() all work well;
button click method in MainForm
private void button1_Click(object sender, EventArgs e)
{
methodUpdate(); //this is working well
}
Have you tried to put your MainForm into the Network Class through the constructor?
In your Network Class:
public class Network
{
private MainForm _frm;
public Network(MainForm frm)
{
_frm = frm;
}
.
.
.
if(isNetwork == true)
{
_frm.Call.Enabled = true;
}
}
When instantiating an Network-object in your MainForm you call something like that:
public Network myNetwork = new Network(this);
I've read through several pages here on Events and Delegates and understand the idea behind them but am unsure of how to use them across multiple classes. Until now, I've simply relied on the IDE to set everything up for me and I didn't realize it worked inside a single class alone.
public class MyForm : Form
{
public MyForm()
{
...
this.Controls.Add(menuBuilder.GenerateMenuForMyForm());
//load other controls into the form to visualize/manipulate data
}
public void UpdateDataInControls()
{
//reloads info into controls based on data in serializable class.
}
}
public class MenuBuilder
{
public MenuStrip GenerateMenuForMyForm()
{
MenuStrip menu = new MenuStrip();
...
ToolStripMenuItem loadfile = new ToolStripMenuItem();
loadfile.name = "loadfile";
loadfile.text = "Load File";
loadfile.Click += new EventHandler(loadfile_Click);
file.DropDownItems.Add(loadfile);
...
return menu;
}
void loadfile_Click(object sender, EventArgs e)
{
//Open a file dialog and deserialize file
//Need to send an event to MyForm letting it know that it needs to
//update controls in the form to reflect the deserialized data.
}
}
So in this instance, I have events working within a single class, but I'm unsure how to set things up so MyForm can receive an event from MenuBuilder. I have tried something like
loadfile.Click += new EventHandler(myFormObject.loadfile_Click);
and make the loadfile_Click() function in MyForm, but that seems counter-intuitive to the idea of driving functionality through events themselves since it needs the form's object itself to be passed into the constructor. If that's the case, I might as well just call the function directly.
Here is one simple way to achieve what your'e looking for. Its a basic event model where a class declares its events, and an observer class subscribes to it.
using System;
using System.Windows.Forms;
public class MyForm : Form
{
public MyForm()
{
var menuBuilder = new MenuBuilder();
menuBuilder.FileLoaded += (sender, args) => UpdateDataInControls();
Controls.Add(menuBuilder.GenerateMenuForMyForm());
//load other controls into the form to visualize/manipulate data
}
public void UpdateDataInControls()
{
//reloads info into controls based on data in serializable class.
}
}
internal class FileLoadedEventArgs : EventArgs
{
// customize event arguments if need be
// e.g. public string FileName {get;set;}
}
public class MenuBuilder
{
// declare event delegate
internal delegate void FileLoadedEvent(object sender, FileLoadedEventArgs e);
// declare event for observers to subscribe
internal event FileLoadedEvent FileLoaded;
public MenuStrip GenerateMenuForMyForm()
{
MenuStrip menu = new MenuStrip();
/*
ToolStripMenuItem loadfile = new ToolStripMenuItem();
loadfile.name = "loadfile";
loadfile.text = "Load File";
loadfile.Click += new EventHandler(loadfile_Click);
file.DropDownItems.Add(loadfile);
*/
return menu;
}
void loadfile_Click(object sender, EventArgs e)
{
// fire the event
FileLoaded(this, new FileLoadedEventArgs());
}
}
I want to close a window form that is hosting a WPF user control. Something like this as used while closing a current form in window application. But for WPF application I am not able to get reference to user controls parent
How to get Form which is hosting this control so that I can close my form
this.Close()
Add to your WpfControl property
public Form FormsWindow { get; set; }
In your WinForm add event handler for ElementHost's event ChildChanged:
using System.Windows.Forms.Integration;
public MyForm() {
InitializeComponent();
elementHost.ChildChanged += ElementHost_ChildChanged;
}
void ElementHost_ChildChanged(object sender, ChildChangedEventArgs e) {
var ctr = (elementHost.Child as UserControl1);
if (ctr != null)
ctr.FormsWindow = this;
}
After that you can use the FormsWindow property of your WpfControl to manipulate window. Example:
this.FormsWindow.Close();
An alternative solution could be,
Window parent = Window.GetWindow(this);
parent.Close();
Just want to add to #The_Smallest's otherwise very clear answer.
If you just copy and past the event handler code, you will still need to set your Forms's ChildChanged event to ElementHost_ChildChanged. I missed that step and spent 30 minutes trying to figure out why FormsWindow was null.
In order to call the Form object of the MyControl class already. We have in it a Form field to which we pass an instance object open Form. Having an assigned object we can freely manipulate it (including also call the function form.Close ();
WPF Control (with XAML):
public class MyControl : UserControl
{
public Form form = null;
public MyControl()
{
InitializeComponent();
this.PreviewKeyDown += new KeyEventHandler(HandleEsc);
}
private void HandleEsc(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
form.Close();
}
}
}
Form:
public class MainForm
{
//...
public Form form = null;
public MainForm(MyControl myControl)
{
InitializeComponent();
//...
myControl.form = (Form)this;
}
}