Windows Forms - pass argument between form and control - c#

In my C# Windows Forms project I have:
mycontrol (it's keyboard)
myform (it's layout for textbox, and mycontrol )
I would like to run some code which is in myform, but by pressing button in mycontrol )
For example:
When I press Backspace button in mycontrol I just use Button Event
SendKeys.Send("{BACKSPACE}");
and myform textbox know that I press Backspace.
But I have some custom buttons (functional) and those buttons should be define in myform
for example in myform I have:
private void btnOK_Click(object sender, EventArgs e)
{
DoSomething();
}
The whole problem is - how to Run myform btnOK_Click or DoSomething from mycontrol
UPDATE #2:
Nothing happens while pressing D0, NullReference when I delete checking if it's null.
internal partial class myForm : BaseForms
{
public myForm() {
InitializeComponent();
ShowMyControl();
}
private void ShowMyControl(){
KeyboardControl myControl = new KeyboardControl();
myControl.KeyboardKeyPressed += new Action<string>(OnMyControlKeyPressed);
this.Controls.Add(myControl);
}
private void OnMyControlKeyPressed(string key)
{
switch (key)
{
case "D0":
MessageBox.Show("A");
break;
case "D1":
MessageBox.Show("B");
break;
default:
MessageBox.Show("C");
break;
}
}
...
}
and
public partial class KeyboardControl : UserControl
{
public event Action<string> KeyboardKeyPressed;
...
private void HandlingMouseClick1(Point PressedItem)
{
...
case Keys.D0:
if (KeyboardKeyPressed != null)
KeyboardKeyPressed("D0");
break;
}
}

In MyForm_Load, or anywhere else if you dynamically initialize your MyControl, you can add something like MyControl.Click += new System.KeyEventHandler(this.MyControl_Click) and place a method called
private void MyControl_Click(sender object, KeyEventArgs e)
{
... //Find out which key was pressed, proceed.
}
in MyForm. The method will be called when the Click event is raised.

Try out the below approach
inside MyForm
public class MyForm : Form
{
//.ctor
MyForm() { }
private void ShowMyControl(){
MyControl myControl = new MyControl();
myControl.KeyboardKeyPressed += new Action<string>(OnMyControlKeyPressed);
this.Controls.Add(myControl);
}
private void OnMyControlKeyPressed(string key)
{
switch(key)
{
case "D0" :
DoSomething();
break;
case "D1" :
DoSomethingElse();
break;
default :
SendKeys(key);
break;
}
}
}
/*MyControl*/
namespace Keyboards
{
public class MyControl : Control
{
public event Action<string> KeyboardKeyPressed;
private void HandlingMouseClick(Point PressedItem)
{
if(KeyboardKeyPressed != null)
KeyboardKeyPressed(PressedItem.ToString());
}
}
}

Use a delegate and pass it to the constructor of your second form.
public delegate void MyDelegate(Object SomeData);
You'll need to create a delegate according to the method's signature you want to use.
MyDelegate del = form1.DoSomething;
Form2 form2 = new Form2(..., del);
Then you can encapsulate a method from your form1 and pass it to form2 and then invoke it in form2.
public Form2(..., MyDelegate del){ }
del.Invoke();
Look here for delegates.

Your control should expose an event that the form can then subscribe to and act upon.
Give your event a meaningful name. If your control contains a Search button and the user clicks it, let your control fire an event called SearchButtonClicked, for instance.
Then the form can contain a method that subscribes to the SearchButtonClicked event, and that code will then be executed whenever the user clicks the Search button.
See also the Events Tutorial on MSDN.

Add a reference of MyForm in your MyControl class
class MyControl
{
MyForm _form=null;
MyControl(MyForm form)
{
_form=form;
}
void DoSth()
{
if(_form!=null)
_form.DoSomething();
}
}

Related

Is there a way to activate a Button that exists within another class?

I am using C# and Xamarin. I have two separate classes. One class is essentially the user interface and another class is acting as a custom built generic entry for users to input data and search for results by clicking a button.
Main UI Class:
Class MainPage
{
public MainPage
{
Content = new StackLayout
{
Children =
{
new InputClass // This is my custom built user entry class
{
}.Invoke(ic => ic.Clicked += WhenButtonPressedMethod) // The problem is here, I can't figure out how to call the button within the input class to fire a clicked event.
}
}
}
}
public async void WhenButtonPressedMethod (object sender, EventArgs e)
{
// Supposed to do stuff when the button is pressed
}
InputClass:
public class InputClass : Grid
{
public delegate void OnClickedHandler(object sender, EventArgs e);
public event OnClickHandler Clicked;
public InputClass
{
Children.Add(
new Button {}
.Invoke(button => button.Clicked += Button_Clicked)
)
}
private void Button_Clicked(object sender, EventArgs e)
{
Clicked?.Invoke(this, e);
}
}
The "InputClass" is a grid that holds a title text label, an entry and a button that a user can press to submit and search data. The button in this class is what I'm trying to actually access to invoke/cause a click event so that the method in the main UI class can be called. But, when I try to invoke a click event on the "InputClass" I can't access the button inside of it, I can only access "InputClass" itself which is just a grid with no useful event properties.
Any solutions or ideas?
If you are running into the same problem as mentioned here, follow the code on this page and read through the comments, it covers enough to be able to piece it together. My mistake was attaching Invokes to the wrong objects.
Don't know why fluent Invoke didn't work correctly.
Add the event handlers this way:
public MainPage
{
var ic = new InputClass();
ic.Clicked += WhenButtonPressedMethod;
Content = new StackLayout
{
Children = { ic }
}
}
public InputClass
{
var button = new Button;
button.Clicked += Button_Clicked;
Children.Add(button);
}

c# winform delegates for n subforms

I have a C# main form that opens up C# sub forms in separate tabs. Each tab is simply an instance of the same sub form so the tab code is like:
SubForm sf = new SubForm();
TabPage tp = new TabPage();
tp.Controls.Add(sf);
tabControl.TabPages.Add(tp);
There can be n tabs and subforms. Then each new subform instance has a delegate to handle external event updates, like so:
public partial class SubForm : Form
{
... form setup ...
internal void DoStuff(value v)
{
if (InvokeRequired)
{
// Generic Action delegate
Invoke(new Action<string, string>(DoStuff), value);
return;
}
myLabel.Text = value;
Show();
}
}
Click the subscribe button and there's a Geode registration to specific keys in the subform, and the delegate is passed as an event handler:
private void button_Click(object sender, EventArgs e)
{
new Geode().RegisterMyListener(cache, key, DoStuff);
}
When the Geode key value is updated then the update is handled.
This is working fine for the 1st subform. Then with a 2nd or 3rd subform all the Geode subscriptions to each subform's keys are updating, but all the updates are being handled only by the most recently instantiated subform's delegate. I had not expected that to happen because doesn't each new subform instance have its own stack with a new delegate?
UPDATE: Once a 2nd key is registered with Geode RegisterMyListener like this:
region.GetSubscriptionService().RegisterKeys(s);
region.AttributesMutator.SetCacheListener(new Listener<string, string>(DoStuff));
then every event update references the latest DoStuff delegate and never a previous one. So is a Geode listener a static register? I am looking to be able to subscribe to separate keys with many instances of the same Listener. Is that possible? Or am I going to need multiple listeners for multiple subforms?
You can do it with extension method like this:
public static class Extensions
{
public static void InvokeAction(this Control control, Action action)
{
if (control.InvokeRequired)
{
control.Invoke(new MethodInvoker(() => { action(); }));
}
else
{
action();
}
}
}
Usage:
public partial class SubForm : Form
{
public void SetExampleText(string text)
{
this.InvokeAction(() => { this.ExampleTextBox.Text = text; })
}
}

Event keeps null value

I am trying to fire event from one control to another, but it keeps null value. I have declared a delegate and event, and created method that checks if event is not null on 2nd control. That method is called on button click and supposed to notify 1st control to do some action (refreshing grid data)
//Child form
public partial class InventuraForm
{
SqlClient client = null;
public delegate void NekiDelegat();
public event NekiDelegat MojEvent;
public void SendEvent()
{
if (MojEvent != null) MojEvent(); //MojEvent keeps showing null
}
//Saving data and exiting the control
public void tsbSpremiZatvori_Click(object sender, EventArgs e)
{
//some code
SendEvent();
}
}
//Parent form
public partial class InventuraFormPregled
{
InventuraForm _inv = null;
public InventuraFormPregled()
{
InitializeComponent();
_inv = new InventuraForm();
_inv.MojEvent += new InventuraForm.NekiDelegat(LoadGridData);
}
//refresing the data on grid
private void LoadGridData()
{
//some code
}
}
I appretiate any help, thx.
open the childform by _inv .Show() method.

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;

What is the recommended way to assign and change an event for button on a form

I have a form which I want to be 'resusable' for a variety of situations. Mostly display and print information. The form has 2 buttons and a listbox
I want to be able to pass an object to the form that tells the form what the buttons are to do when pressed(for example show a MessageBox, Print out the contents of the listbox or close the form)
I am using an if statement to figure out what event to assign to my button...is there a better way to do this?
Ideally I would like to set the event from the initial calling code instead fo using an enum called 'Action'
==========calling code=================
var information = new Information();
information.Action = Action.Print;
var frmInformation = new frmInformation(information);
frmInformation.Show(this);
====================information class======================
public class Information
{
public delegate void OkButtonDelegate();
public IList<string> information{ get; set; }
public Information()
{
information = new BindingList<string>();
}
===============information form======================
public partial class frmInformation : Form
{
private readonly Information _information;
public Information.OkButtonDelegate _delegate;
public frmInformation(Information information)
{
_information = information;
InitializeComponent();
SetupForm();
}
private void SetupForm()
{
if (_information.Action== Action.Print)
_delegate = new Information.OkButtonDelegate(Print);
else if (_information.Action == Action.Close)
_delegate = new Information.OkButtonDelegate(Close);
}
private void ShowMessageBox()
{
MessageBox.Show("lalalalalala");
}
public static void Print()
{
//take the contente out of listbox and send it to the printer
}
private void btnSend_Click(object sender, EventArgs e)
{
_delegate();
}
You may change it this way
switch (_information.Action) {
case Action.Print:
btnSend.Click += (s,e) => Print();
break;
case Action.Close:
btnSend.Click += (s,e) => Close();
break;
}
You won't need delegate type, generated Click handler and _delegate variable.

Categories