Here is my class with event handler:
public delegate void MyKeyEventHandler(object sender, KeyEventArgs e);
public class KeyEvent
{
public event MyKeyEventHandler keyEvent;
public string key = "";
protected virtual void KeyPressed(KeyEventArgs e)
{
MyKeyEventHandler handler = keyEvent;
if (handler != null)
handler(this, e);
}
public KeyEvent()
{
keyEvent += new MyKeyEventHandler(keyPressed);
}
private void keyPressed(object sender, KeyEventArgs e)
{
key = e.KeyCode.ToString();
}
}
And in my Form1 I have this code: (EDITED)
KeyEvent ke = new KeyEvent();
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(ke.key);
}
When I run form and press key on keyboard and then click on button it pop up empty MessageBox. What I want is everytime I press a key on keyboard a MessageBox to pop up and show me which key I pressed. This code is just for testing, I don't want just to pop up message box, I need the key for other things.
Note: To handle the events on Form1 is not a solution, I need to handle the events in class. I making a libray.
It will not be possible for you to create a class that is capable of just automatically listening for events generated by its parent. How would that work? What would happen if you created a new KeyEvent class using something that wasn't a Form and didn't raise events?
You are going to need to give your KeyEvent class a reference to whatever thing that you want to monitor key events for. I realize that your intention is to create some third-party library, so you'd like to keep things as generic as possible in order to maximize its reuse. Since you are dependent on the KeyDown event, using Control is the most generic you can be. This is because it is where the KeyDown event is actually defined.
Let me show you what I mean...
1) Modify the constructor of your KeyEvent class to accept an instance of Control
public class KeyEvent
{
// You don't need to declare an internal event
public string key = "";
private Control _control;
public KeyEvent(Control control)
{
// Here is where we save the reference of Control that was passed to the class.
// This will enable you to access the instance of the Form or whatever else
// you want to use, which is helpful for unregistering events during cleanup
_control = control;
// Wire up your event handler to this SPECIFIC instance of Control.
// This will cause your `keyPressed` method to execute whenever
// the control raises the KeyDown event. All of the middleman
// event handling you are doing is unnecessary.
_control.KeyDown += keyPressed;
}
private void keyPressed(object sender, KeyEventArgs e)
{
key = e.KeyCode.ToString();
}
}
2) Modify the parent in which you are creating this KeyEvent to pass itself into the KeyEvent
// Now you pass 'this' into the constructor. This can be a Form, or any other
// Winforms control that may inherit from Control (which is all of them)
KeyEvent ke = new KeyEvent(this);
private void button1_Click(object sender, EventArgs e)
{
// At this point, ke.Key will always display the last
// key pressed in the Form when the button is clicked.
MessageBox.Show(ke.key);
}
You can do something like this:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form1 f = new Form1();
f.KeyPreview = true;
f.KeyDown += f_KeyDown;
Application.Run(f);
}
static void f_KeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show(e.KeyValue.ToString());
}
}
If you enable KeyPreview you will receive all keyDowns even of the controls placed on the Form. If you set it to false you only get the KeyDown when the Form has focus.
As you can see in the code you can connect any KeyDownEventHandler method in any class to the Form.
As there where questions left:
public class FormKeyListener
{
private Form formToListen = null;
public void SetFormToListen(Form form)
{
if (formToListen != null)
formToListen.KeyDown -= formToListen_KeyDown; // Unregister from old form if present
formToListen = form;
formToListen.KeyDown += formToListen_KeyDown; // Attach to new form
}
void formToListen_KeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show(e.KeyValue.ToString());
}
}
Which can be call this way somewhere in your code:
FormKeyListener fkl = new FormKeyListener();
Form1 f = new Form1();
f.KeyPreview = true;
fkl.SetFormToListen(f);
Related
I'm currently working on a little game as a WinForms project. The gamefield is build out of a grid of Tiles a Player (a separate class) can move on. I want the player to move with the arrow keys and have set up an event handler for it, but it doesn't seem to be ever called.
(minimalized) player class, which implements the System.Windows.Forms packages for the KeyEventArgs:
public class Player : MovableObject
{
public Player(Playtile position) : base(position)
{
EntityColor = ElementColors.PlayerColor;
PostConstructor(position);
}
/// <summary>
/// Reads keypress via eventhandler KeyEventArgs (key down).
/// </summary>
private void ReadKeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up:
// move up.
break;
case Keys.Right:
// move right.
break;
case Keys.Down:
// move down.
break;
case Keys.Left:
// move left.
break;
}
}
}
Currently, the ReadKeyDown never gets called. How do I actually assign / bind that KeyEventDown event to the player, so it actually calls that method on key down?
KeyEventArgs in C# seems to simply say this.KeyDown += ReadKeyDown;, but it doesn't recognize KeyDown as anything, nor does it give me a possible missing assembly reference.
KeyDown is a member of Control so in your case you could subscribe to that event in your Form.
Like the following:
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
textBox1.KeyDown += TextBox1_KeyDown;
}
private void TextBox1_KeyDown(object sender, KeyEventArgs e)
{
}
}
See the official documentation for a complete example:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.keydown?view=netframework-4.7.2
Now what you could do is invoke a method in your target class once you've received such event:
public class MyClass
{
public void OnKeyDown(KeyEventArgs e)
{
// ...
}
}
public partial class Form2 : Form
{
MyClass _instance = new MyClass();
public Form2()
{
InitializeComponent();
textBox1.KeyDown += TextBox1_KeyDown;
}
private void TextBox1_KeyDown(object sender, KeyEventArgs e)
{
_instance.OnKeyDown(e);
}
}
You could subscribe to the KeyDown event directly in your class:
public Player(Playtile position, Form form) : base(position) // <--- form parameter
{
EntityColor = ElementColors.PlayerColor;
PostConstructor(position);
form.KeyDown += ReadKeyDown; // <--- subscribing to the event
}
There is an issue though. Now the form holds a reference to your Player object, and the object will not be garbage collected until the form is closed. This is not a problem if the Player's lifespan is the same with the form's lifespan. But if you create many short-lived players then you should unsubscribe from the form's event when you no longer need them, by adding to the Player a method like this:
public void Dispose(Form form)
{
form.KeyDown -= ReadKeyDown;
}
...and calling Dispose whenever you dispose a player object.
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();
I have a form with two controls A and B
, A has a key-down event and i show a message box when 'S' is pressed on A control.
And i want to show another message when 'S' is pressed in any other control.
how can I do this ?
Simply what I want is : I should be able to handle a Key-down event after all child controls.
and I should be able to know whether the Event is handle in a child control in Form-level Key-down.
I tried enabling Key-preview but when Key-preview is enabled Form-level event get's fired before child control events. I want child controls first, Then Form level one
I want form level Event to be fired after focused control's key down event is fired.
and I want to check in Form-level event whether the event is handled in focused controls key-down event.
What methods can I use for this ?
Please enlighten me.
you can do the following
here a sample code
Add new project call it EventSample
Add a UserControl call it AControl
Add a UserControl call it BControl
make the AControl BackColor Blue and BControl Red in order to
distinguish from the form
//Form1 code
namespace EventSample
{
public delegate void AfterChildEventHandler(Control control,Keys key);
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
aControl1.OnChildFireEvent += OnChildFireEvent;
bControl1.OnChildFireEvent += OnChildFireEvent;
}
void OnChildFireEvent(Control control, Keys key)
{
MessageBox.Show("Form fired event from " + control.GetType().Name);
}
}
}
//AControl code
namespace EventSample
{
public partial class AControl : UserControl
{
public event AfterChildEventHandler OnChildFireEvent;
public AControl()
{
InitializeComponent();
}
private void AControl_KeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show("A Control fired Key down");
if (OnChildFireEvent != null)
OnChildFireEvent(this, e.KeyCode);
}
}
}
//BControl code
public partial class BControl : UserControl
{
public event AfterChildEventHandler OnChildFireEvent;
public BControl()
{
InitializeComponent();
}
private void BControl_KeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show("B Control fired Key down");
if (OnChildFireEvent != null)
OnChildFireEvent(this, e.KeyCode);
}
}
EDITED
another solution you can make it with less code
define a static event
handle this event inside the form
let each control to invoke this event
namespace EventSample
{
public delegate void AfterChildEventHandler(Control control, Keys key);
public class GlobalEvent
{
public static event AfterChildEventHandler OnChildEventFire;
public static void Invoke(Control control, Keys key)
{
if (OnChildEventFire != null)
OnChildEventFire(control, key);
}
}
}
changes in the A Control
namespace EventSample
{
public partial class AControl : UserControl
{
public AControl()
{
InitializeComponent();
}
private void AControl_KeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show("A Control fired Key down");
GlobalEvent.Invoke(this, e.KeyCode);
}
}
}
changes in the B Control
namespace EventSample
{
public partial class BControl : UserControl
{
public BControl()
{
InitializeComponent();
}
private void BControl_KeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show("B Control fired Key down");
GlobalEvent.Invoke(this, e.KeyCode);
}
}
}
Build the sample and run and try to press any key on A or B then you will find that A will fire then the form
hope it will help you
In mdimain_MdiChildActivate the application logic is defined for all child forms
related to GridControl mouseDoubleClick event .
It is working fine for all grid containing child forms but in some case where grid mouseDoubleClick is defined internal for the Child Form.
So, the event is fired twice one from MdiParent and for internal part.
Is there any way such that MdiParent Parent Control event does not fire's for this mouseDoubleClick case comparing/validating the ifexist case for Child Form without changing the code in MDI form.
sample example :
private void MDIMain_MdiChildActivate(object sender, EventArgs e)
{
// code should not work
}
private void MainGridControl_MouseDoubleClick(object sender, MouseEventArgs e)
{
/// Child Form : code should work
}
This approach detects WM_NCHITTEST message sent to your MainGridControl before MdiChildActivate is fired. This can only detect if your mouse is used (Click, DoubleClick) on the MainGridControl but I think it suits in your case.
public class Form1 : Form {
public Form1(){
InitializeComponent();
Load += (s,e) => {
gridProc.AssignHandle(MainGridControl.Handle);
};
}
MainGridProc gridProc = new MainGridProc();
private void MDIMain_MdiChildActivate(object sender, EventArgs e)
{
if(gridProc.HitTestOn) { gridProc.HitTestOn = false; return; }
//code is still run if HitTestOn = false
//.......
}
public class MainGridProc : NativeWindow {
protected override void WndProc(ref Message m){
if(m.Msg == 0x84)//WM_NCHITTEST
{
HitTestOn = true;
}
base.WndProc(ref m);
}
public bool HitTestOn {get;set;}
}
}
I am really new to programming and currently working on a C# Windows Forms application.
The problem is the following:
I have a Form with different objects and controls like: tabpages, textboxes, timers, etc .
I also have a UserControl form which I load into one of the main Form's tabpages.
I would like to write a code into the UserControl , how can I manipulate element properties of the main Form.
For example: when I click on a button on the UserControl form It sets the main Form's timer.Enabled control to true.
It is possible to do this, but having the user control access and manipulate the form isn't the cleanest way - it would be better to have the user control raise an event and have the hosting form deal with the event. (e.g. on handling the button click, the form could enable/disable the timer, etc.)
That way you could use the user control in different ways for different forms if need be; and it makes it more obvious what is going on.
Update:
Within your user control, you can declare an event - In the button click, you raise the event:
namespace WindowsFormsApplication1
{
public partial class UserControl1 : UserControl
{
public event EventHandler OnButtonClicked;
public UserControl1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
EventHandler handler = OnButtonClicked;
// if something is listening for this event, let let them know it has occurred
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
}
Then within your form, add the user control. You can then hook into the event:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
userControl11.OnButtonClicked += userControl11_OnButtonClicked;
}
void userControl11_OnButtonClicked(object sender, EventArgs e)
{
MessageBox.Show("got here");
}
}
}
You may want to rethink what it is you are trying to accomplish. However, to answer your question, it can be done.
The best way to do it is to make a property in your UserControl called MainForm:
public Control MainForm {
get;
set;
}
Then, in your MainForm's Load event, set the property to itself:
userControl1.MainForm = this;
Finally, in your user control, set the MainForm's timer:
protected button_click(object sender, EventArgs e)
{
timerName = "timer1";
EnableTimer(timerName);
}
private void EnableTimer(timerName)
{
var timer = MainForm.Controls.FirstOrDefault(z => z.Name.ToLower().Equals(timerName.ToLower());
if (timer != null)
{
((Timer)timer).Enabled = true;
} else {
// Timer was not found
}
}
This is very simple. It's called events. On the user control you would expose an event with a EventHandler for the form to subscribe to.
public partial class MyUserControl : UserControl
{
/// You can name the event anything you want.
public event EventHandler ButtonSelected;
/// This bubbles the button selected event up to the form.
private void Button1_Clicked(object sender, EventArgs e)
{
if (this.ButtonSelected != null)
{
// You could pass your own custom arguments back to the form here.
this.ButtonSelected(this, e)
}
}
}
Now that we have the user control code we'll implement it in the form code. Probably in the constructor of the form you'll have some code like below.
MyUserControl ctrl = new MyUserControl();
ctrl.ButtonSelected += this.ButtonSelected_OnClick;
Finally in the form code you'll have a method that subscribed to the event like the below code that will set the Timer enabled to true.
private void ButtonSelected_OnClick(object sender, EventArgs e)
{
this.Timer1.Enabled = true;
}
And that's how you allow an event on a user control on a form set an object on the form.
You can set the timer1.Modifiers property to "internal" and access it with an instance to Form1:
form1.timer1.Enabled = true;
You need to have an instance of your class Form1, not the class itself. For example:
// INVALID
Form1.timer1.Enabled = true;
// VALID
var form1 = Form1.ActiveForm;
form1.timer1.Enabled = true;
But this is not a very clean way to do this, you would rather use events as described in NDJ's answer.
You need to put the below code,
(`userControl11.OnButtonClicked += userControl11_OnButtonClicked;`)
in a separate file in Visual Studio. The other file is called 'Form1.Designer.cs', and can be found in the Solution Explorer pane under
Form1 >> Form1.cs >> Form1.Designer.cs.
Hope this helps!