I've got a problem with subscribing from a form to an event in an user control.
MainForm-Code:
public partial class mainForm : Form
{
public mainForm()
{
InitializeComponent();
UserControl menuView = new mnlib.mnlibControl();
newWindow(menuView);
}
public void newWindow(UserControl control)
{
this.mainPanel.Controls.Clear();
this.mainPanel.Controls.Add(control);
}
mnlibControl.OnLearnClick += new EventHandler(ButtonClick); //Error in this line
protected void ButtonClick(object sender, EventArgs e)
{
//handling..
}
}
UserControl-Code:
public partial class mnlibControl : UserControl
{
public mnlibControl()
{
InitializeComponent();
}
private void btn_beenden_Click(object sender, EventArgs e)
{
Application.Exit();
}
public event EventHandler LearnClick;
private void btn_lernen_Click(object sender, EventArgs e)
{
if (this.LearnClick != null)
this.LearnClick(this, e);
}
}
Now, visual studio marks the "mnlibControl.OnLearnClick ..." line as wrong. "mnlibControl" would not be found, maybe a missing using directive etc. .
All this code and both forms are located in the same project file.
I tried around and googled like hell but just can't find a solution for my problem.
In the UserControl form there is a button - when it's clicket it shall trigger the newWindow method in the mainForm and open up another window.
My source for this solution of my problem is: How do I make an Event in the Usercontrol and Have it Handeled in the Main Form?
There is no OnLearnClick in your component. You need to subscribe to LearnClick. You also need to subscribe in function block. You also should use concrete type (mnlib.mnlibControl), not UserControl:
public mainForm()
{
InitializeComponent();
mnlib.mnlibControl menuView = new mnlib.mnlibControl();
menuView.LearnClick += new EventHandler(ButtonClick);
newWindow(menuView);
}
Your code mnlibControl.OnLearnClick += new EventHandler(ButtonClick); must be within any of functional block (i.e. method, property, ...).
You have to place this line inside an actual method:
mnlibControl.LearnClick += new EventHandler(ButtonClick);
Like this:
public mainForm()
{
InitializeComponent();
UserControl menuView = new mnlib.mnlibControl();
newWindow(menuView);
mnlibControl.OnLearnClick += new EventHandler(ButtonClick);
}
Related
I have a userControl and I've a button there, I'd like to call event when I'm clicking on the button in my main form from userControl. I do this:
UserControl
public UserControlerConstructor()
{
_button.Click += new EventHandler(OnButtonClicked);
}
public delegate void ButtonClickedEventHandler(object sender, EventArgs e);
public event ButtonClickedEventHandler OnUserControlButtonClicked;
private void OnButtonClicked(object sender, EventArgs e)
{
// Delegate the event to the caller
if (OnUserControlButtonClicked != null)
OnUserControlButtonClicked(this, e);
}
Form
public Form1()
{
userControlInstance.OnUserControlButtonClicked += new EventHandler(OnUCButtonClicked);
}
private void OnUCButtonClicked(object sender, EventArgs e)
{
throw new NotImplementedException();
}
It doesn't work because when I click in the form do nothing in the form code, but it does in userControl code. But I'd like to do in form code. I don't know how to call event from userControl to the form.
Well now I don't know if you're explicity want to use the delegate, no? If not, why don't you just do:
public Form1()
{
userControlInstance._button.Click += OnUCButtonClicked;
}
private void OnUCButtonClicked(object sender, EventArgs e)
{
throw new NotImplementedException();
}
up to now your code does not compile. You are using the wrong event handler type. It should show the following compiler error:
EventHandler cannot be converted to ButtonClickedEventHandler
Do the following steps:
1) put the declaration of the delegate outside of the class UserControlerConstructor:
public delegate void ButtonClickedEventHandler(object sender, EventArgs e);
public partial class UserControlerConstructor: UserControl
{
1) then change the type of the handler when registering the event in Form:
public Form1()
{
userControlInstance.OnUserControlButtonClicked += new ButtonClickedEventHandler(OnUCButtonClicked);
}
This way it should work
I've tried to use event from an userControl to a form, but when I'm creating it in a form constructor I've an issue. I don't know where is a fail. There is my code.
UserControl
public GameField()
{
InitializeComponent();
button.Click += Button_Clicked;
}
public event EventHandler ButtonClicked;
private void Button_Clicked(object sender, EventArgs e)
{
if (this.ButtonClicked != null) this.ButtonClicked(sender, e);
}
Form
GameField gameField = new GameField(); //Instance of the derived class UserControl
public Form1()
{
InitializeComponent();
gameField.ButtonClicked += new EventHandler(this.btn_Click);
}
private void btn_Click(object sender, EventArgs e)
{
throw new NotImplementedException();
}
There is an issue
enter image description here
I think you wanted to subscribe to Button_Clicked instead of ButtonClicked in GameField.
button.Click += Button_Clicked;
Edit
I see, I have a hunch that you have two instances of GameField. The one that you added through the forms designer, probably named gameField1 and the one that you added in code to your form called gameField.
If you open Form1.Designer.cs can you see a gameField1 in there (or whatever name you gave when you added it through the designer)?
Can you try the following:
gameField1.ButtonClicked += new EventHandler(btn_Clicked); // name can be other than gameField1, gameField1 is just the automatically generated name by VS
I am working with windowsFrom in c#. I am trying to call mainfrom method in one of the from in user control.
I have mainfrom like this
namespace Project
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
public void TempCommand()
{
StartTemp();
}
}
}
I have the button click in the user control. When i click that button then it will open another form. I have the code like this in the user control.
private TempCalib _tempCalib = new TempCalib();
private void calibBtn_Click(object sender, EventArgs e)
{
_tempCalib.Show();
}
it will open another from and i have one button in that from. I need to call mainfrom method when i click "Ok" button in this from.
namespace Project
{
public partial class TempCalib : Form
{
public TempCalib()
{
InitializeComponent();
}
private void OkButton_Click(object sender, EventArgs e)
{
// I need to call the mainfrom "TempCommand" method here.
this.Hide();
}
}
}
Can anyone help me how to do this.
Thanks.
Quick answer
Just add a reference to the primary form in your secondary form:
public partial class TempCalib : Form
{
private MainForm _main
public TempCalib(MainForm main) : this()
{
_main = main;
}
/// Other stuffs
}
Then assign value when you construct your secondary form:
private TempCalib _tempCalib;
private void calibBtn_Click(object sender, EventArgs e)
{
if (_tempCalib == null)
_tempCalib = new TempCalib(this);
_tempCalib.Show();
}
If calibBtn_Click isn't inside MainForm (but it's inside a UserControl on it) then you can replace _tempCalib initialization with:
_tempCalib = new TempCalib((MainWindow)FindForm());
You'll be then able to call the primary form:
private void OkButton_Click(object sender, EventArgs e)
{
_main.TempCommand();
this.Hide();
}
Notes: this is just one option, you may create a property to hold MainForm reference (so secondary form can be reused and it'll be more designer friendly) moreover TempCalib is not an UserControl but a Form (pretty raw but for an UserControl you may just check its parent Form and cast it to proper type).
Improvements
Such kind of references are often an alert. Usually UI components shouldn't not be so coupled and a public Form's method to perform something very often is the signal that you have too much logic in your Form. How to improve this?
1. DECOUPLE CONTROLS. Well a first step may be to decouple them a little bit, just add an event in TempCalib and make MainForm its receiver:
public partial class TempCalib : Form
{
public event EventHandler SomethingMustBeDone;
private void OkButton_Click(object sender, EventArgs e)
{
OnSomethingMustBeDone(EventArgs.Empty); / TO DO
this.Hide();
}
}
Then in MainForm:
private TempCalib _tempCalib;
private void calibBtn_Click(object sender, EventArgs e)
{
if (_tempCalib == null)
{
_tempCalib = new TempCalib();
_tempCalib.SomethingMustBeDone += _tempCalib_SomethingMustBeDone;
// In _tempCalib_SomethingMustBeDone you'll invoke proper member
// and possibly hide _tempCalib (remove it from OkButton_Click)
}
_tempCalib.Show();
}
2. DECOUPLE LOGIC FROM CONTROLS. UI changes pretty often, logic not (and when it changes probably isn't in parallel with UI). This is just the first step (now TempCalib isn't aware of who will use it). Next step (to be performed when too much things happen inside your form) is to remove this kind of logic from the form itself. Little example (very raw), keep TempCalib as before (with the event) and change MainForm to be passive:
public partial class MainForm : Form
{
public event EventHandler Calibrate;
protected virtual void OnCalibrate(EventArgs e)
{
// TODO
}
}
Now let's create a class to control the flow and logic:
public class MyTaskController
{
private MainForm _main;
private TempCalib _tempCalib;
public void Start()
{
_main = new MainForm();
_main.Calibrate += OnCalibrationRequested;
_main.Show(); // Or whatever else
}
private void OnCalibrationRequested(object sender, EventArgs e)
{
if (_tempCalib == null)
{
_tempCalib = new TempCalib();
_tempCalib.SomethingMustBeDone += OnSomethingMustBeDone();
}
_tempCalib.Show();
}
private OnSomethingMustBeDone(object sender, EventArgs e)
{
// Perform the task here then hide calibration window
_tempCalib.Hide();
}
}
Yes, you'll need to write much more code but this will decouple logic (what to do as response to an action, for example) from UI itself. When program grows up this will help you to change UI as needed keeping logic unaware of that (and in one well defined place). I don't even mention that this will allow you to use different resources (people) to write logic and UI (or to reuse logic for different UI, WinForms and WPF, for example). Anyway IMO the most obvious and well repaid benefit is...readability: you'll always know where logic is and where UI management is, no search, no confusion, no mistakes.
3. DECOUPLE LOGIC FROM IMPLEMENTATION. Again you have more steps to perform (when needed). Your controller is still aware of concrete types (MainForm and TempCalib). In case you need to select a different form at run-time (for example to have a complex interface and a simplified one or to use dependency injection) then you have to decouple controller using interfaces. Just an example:
public interface IUiWindow
{
void Show();
void Hide();
}
public interface IMainWindow : IUiWindow
{
event EventHandler Calibrate;
}
public interface ICalibrationWindow : IUiWindow
{
event EventHandler SomethingMustBeDone;
}
You could use a custom event that is declared in your UserControl. Then your form needs to handle this event and call the method you want to call. If you let the UserControl access your form, you are hard-linking both with each other which decreases reusability of your UserControl.
For example, in TempCalib:
public delegate void OkClickedHandler(object sender, EventArgs e);
public event OkClickedHandler OkClicked;
private void OkButton_Click(object sender, EventArgs e)
{
// Make sure someone is listening to event
if (OkClicked == null) return;
OkClicked(sender, e);
this.Hide();
}
in your mainform:
private void Mainform_Load(object sender, EventArgs e)
{
_tempCalib.OkClicked += CalibOkClicked;
}
private void CalibOkClicked(Object sender, EventArgs e)
{
StartTemp();
}
You create an event in your usercontrol and subscribe to this in the mainform.
That is the usual way.
Form1 Code:
UserControl1 myusercontrol = new UserControl1();
public void TabClose(Object sender,EventArgs e)
{
int i = 0;
i = tabControl1.SelectedIndex;
tabControl1.TabPages.RemoveAt(i);
}
private void Form1_Load(object sender, EventArgs e)
{
myusercontrol.Dock = DockStyle.Fill;
TabPage myTabPage = new TabPage();
myTabPage.Text = "Student";
myTabPage.Controls.Add(myusercontrol);
tabControl1.TabPages.Add(myTabPage);
myusercontrol.OkClick += TabClose;
}
UserControl1 Code:
public delegate void OkClickedHandler(Object sender, EventArgs e);
public partial class UserControl1 : UserControl
{
public event OkClickedHandler OkClick;
public UserControl1()
{
InitializeComponent();
}
private void button3_Click(object sender, EventArgs e)
{
if (OkClick == null) return;
OkClick(sender, e);
}
}
Try this:
From user control try this:
MainForm form = this.TopLevelControl as MainForm;
form.TempCommand();
Had a look on here and found several examples but they don't seem to fit my exact problem and through experimentation I can't work it out.
Current code for form...
public partial class Form1 : Form
{
DualCombo dc = new DualCombo();
public Form1()
{
InitializeComponent();
this.Controls.Add(dc);
}
private void MyMethod()
{
MessageBox.Show(dc.c1.Text + dc.c2.Text);
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
The control that contains 2 of my custom combos...
public class DualCombo : UserControl
{
public CustomCombo c1 = new CustomCombo();
public CustomCombo c2 = new CustomCombo();
public DualCombo()
{
c1.Items.Add("One");
c1.Items.Add("Two");
c1.Items.Add("Three");
c2.Left = c1.Right;
c2.Items.Add("One");
c2.Items.Add("Two");
c2.Items.Add("Three");
this.Controls.Add(c1);
this.Controls.Add(c2);
}
}
I have left the code from the custom combo blank to keep the example simple...
public class CustomCombo : ComboBox
{
}
I would like my custom combo OnSelectedIndex changed to trigger the following method that is in the form...
private void MyMethod()
{
MessageBox.Show(dc.c1.Text + dc.c2.Text);
}
The easiest solution is to subscribe to that event directly:
public Form1()
{
InitializeComponent();
this.Controls.Add(dc);
dc.c1.SelectionChangedEvent += (s, e) => MyMethod();
}
But this is bad idea to make c1 public, read further.
UserControl is encapsulated control with some functionality. If you need to inform someone who is using that UserControl about something simple create an event:
Add event to UserControl
public EventHandler SomeEvent;
protected void OnSomeEvent() => SomeEvent?.Invoke(this, EventArgs.Empty);
Fire it when selection is changed from within UserControl
protected CustomCombo c1 = new CustomCombo();
public DualCombo()
{
c1.SelectedIndexChanged += (s, e) => OnSomeEvent();
...
}
Now you can subscribe to that event in your form (where this UserControl is used):
DualCombo dc = new DualCombo();
public Form1()
{
InitializeComponent();
this.Controls.Add(dc);
dc.SomeEvent += (s, e) => MyMethod(); // call your method
}
Tips: do not make controls inside UserControl public. Think about UserControl as a black box.
I'd like to add a UserControl (UserControl2) to a panel on another form (MainForm) when the user clicks a button on the UserControl that is currently in the panel (UserControl1). UserControl2 should replace UserControl1 as the contents of the panel when this button is clicked.
I've tried figuring out how to use an event to communicate between UserControl1 and MainForm, but I just don't know where to start, as I can't find an example that is easily adaptable to my particular situation (Adding a control to a panel).
THIS question is similar, but doesn't quite fit what I'm trying to do (or at least I just don't know how to adapt it to what I'm trying to do)
I've tried this method, and get no errors, but my button doesn't do anything:
MainForm:
private void SubscribeToEvent(MyUserControl1 Button_Click)
{
MyUserControl1 CurrentClass = new MyUserControl1();
CurrentClass.Button.Click += new EventHandler(this.ButtonClickHandler);
}
private void ButtonClickHandler(object sender, EventArgs e)
{
MyUserControl2 MainPanelControls = new MyUserControl2();
MainPanel.SuspendLayout();
MainPanel.Controls.Clear();
MainPanel.Controls.Add(MainPanelControls);
MainPanel.ResumeLayout();
}
UserControl1:
public void Button_Click(object sender, EventArgs e)
{
//... Not sure what I'm missing here
}
I've also tried this method, this time trying to implement something similar to the method described in the link near the top of my question. I know these are obviously wrong (otherwise I wouldn't need to ask the question), but I don't have enough knowledge on the subject to figure it out on my own:
UserControl1:
public MyUserControl1()
{
InitializeComponent();
}
private MainForm mainForm = null;
public MyUserControl1(Form callingForm)
{
mainForm = callingForm as MainForm;
InitializeComponent();
}
//...
private void Button_Click(object sender, EventArgs e)
{
MyUserControl2 MainPanelControls = new MyUserControl2();
mainForm.MainPanel.SuspendLayout();
mainForm.MainPanel.Controls.Clear();
mainForm.MainPanel.Controls.Add(MainPanelControls);
mainForm.MainPanel.ResumeLayout();
}
Now when I click Button I get an "Object reference not set to an instance of an object" error at mainForm.MainPanel.SuspendLayout(); (Or anything past that point).
I've also tried modifying Joh's answer, but end up with the same Null Reference Exception, this time on my Button_Click event at ButtonClickedToMainForm(this, e); I'm not sure if I need to create an instance of ButtonClickedToMainForm, or how to do that properly (if it's not something else, that is).
I have a feeling I've likely just placed some of this code in the wrong place, so I'm hoping someone more experienced may be able to help me sort that out.
UPDATE
This is my attempt at implementing Joh's answer, being so new to this, I'm not quite sure where I've messed up:
MainForm:
namespace MyProject
{
public delegate void ButtonClickToMainForm(object sender, EventArgs e);
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
UserControl1 userControl1 = new UserControl1();
userControl1.Button1Clicked += userControl1_Button1Clicked;
}
private void userControl1_Button1Clicked(object sender, EventArgs e)
{
try
{
UserControl2 MainPanelControls = new UserControl2();
MainPanel.SuspendLayout();
MainPanel.Controls.Clear();
MainPanel.Controls.Add(MainPanelControls);
MainPanel.ResumeLayout();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
UserControl1:
public partial class UserControl1: UserControl
{
public event ButtonClickToMainForm Button1Clicked;
public UserControl1()
{
InitializeComponent();
}
private void OnButton1Clicked(object sender, EventArgs e)
{
Button1Clicked?.Invoke(this, e);
}
private void Button1_Click(object sender, EventArgs e)
{
Button1Clicked(this, e); //"Object reference not set to an instance of an object."
}
}
I'm sure it's something simple I'm missing on UserControl1, I'm just not sure what.
Here is an easy example for a delegate event.When you implement it in all your usercontrols it should work for your problem.
UserControl:
//Create a delegate
public delegate void ButtonClickToMainForm(object sender, EventArgs e);
public partial class UserControl1 : UserControl
{
//Your own event based on created delegate
public event ButtonClickToMainForm ButtonClickedToMainForm;
public UserControl1()
{
InitializeComponent();
}
//This method will invoke your event
private void OnButtonClickedToMainForm(object sender, EventArgs e)
{
ButtonClickedToMainForm?.Invoke(this, e);
}
private void button1_Click(object sender, EventArgs e)
{
//On button1_Click this will fire the event on mainform
OnButtonClickedToMainForm(this, e);
}
and the mainForm :
public Form1()
{
InitializeComponent();
//Subscribe to event from your usercontrol
userControl11.ButtonClickedToMainForm += UserControl11_ButtonClickedToMainForm;
}
//Button on userControl1 has been clicked
private void UserControl11_ButtonClickedToMainForm(object sender, EventArgs e)
{
//do your things here...
}
Hope this helps.