Navigating between user controls in same form - c#

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.

Related

Changing form control from user control

I'm trying to changing a user control informations (labels, pictures etc.) from auto added user control. But i cant do it.
Here's my code;
private void KitapButton_Click(object sender, EventArgs e)
{
BıtıkForm BForm = new BıtıkForm();
BForm.kitapGoruntuleme.Visible = true;
}
public partial class BıtıkForm : Form
{
//create controls public instance
public Label label;
public BıtıkForm()
{
InitializeComponent();
//initialize the control
label = new Label();
}
}
Now you can access it from other place like;
BıtıkForm BForm = new BıtıkForm();
BForm.label.Visible = true;
/////// But my Suggestion do not do it like that instead do it like below ///////
BıtıkForm BForm = new BıtıkForm(controlVisible);//Pass the bool value as parameter to the constructor of form
BForm.Show();
And then in form
public partial class BıtıkForm : Form
{
public BıtıkForm(bool controlVisible)
{
InitializeComponent();
//Set Control Visibility
someControl.Visible = controlVisible;
}
}
I didn't use C# too much but It's eventually object oriented. The mistake I made is; I was creating a new instance of 'BıtıkForm' everytime event fired. It could be solved by adding new property where event belongs, and property will carry 'BıtıkForm' object. So It can be managed trough all over the program.

How to show date from MonthCalendar to TextBox C#

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.

Set StatusLabel text in one form from UserControl

I have a Windows Form (MainForm1) that contains a ToolStrip with a label in it (StatusLabel). MainForm1 also contains a User Control (UserControl1). The User Control contains a button (Button1). When Button1 is clicked it initializes a DataGridView, but that is not important.
When Button1 is clicked in UserControl1, I want to display text in the MainForms StatusLabel.
But I don't know how to do that from one UserControl to the MainForm.
The flow chart describes how I would like it to function.
You can do this simply by creating an event in your User Control
public event EventHandler<string> MessageHasSent;
public void SendMessage(string message)
{
EventHandler<string> ms = MessageHasSent;
if (ms!= null)
{
ms(this,message);
}
}
And in every where in your class that you want Raise this event.In your case you want by clicking on button send message
public Button1_Click( object sender,EventArgs e)
{
SendMessage("YourMessage");
}
And use it like other events.In your MainForm use this event of your UserControl .
public class MainForm:Form
{
public MainForm()
{
UserControl1.MessageHasSent +=SetToolStripLabel;
}
public SetToolStripLabel( object sender,string e)
{
//Set e to Label
}
}

Pass variable between forms when clicking button

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;

how to close window form which is hosting WPF user control from within a WPF user control

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;
}
}

Categories