Stop Event popagation in Winforms - c#

I was looking for a way to stop MouseDown event fired by an ObjectListView from propagation. Almost all suggestions in the net sad: use event.Handled = true;
However, Handled property is not there. I tried Cancel method but is not there too.
Basically, I have an ObjectListView defined as:
public class ObjectListView : Control
{
//...
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
//...
}
}
and I used this object in one of my forms to catch mouse click event in the ObjectListView. So I added an event handler in the form:
partial class Form1
{
private void InitializeComponent()
{
//...
this.ObjectListView.MouseDown +=
new System.Windows.Forms.MouseEventHandler(this.ObjectListView_MouseDown);
}
}
and defined within Form1 a delegate:
public partial class Form1
{
private void ObjectListView_MouseDown(object sender, MouseEventArgs e)
{
// Stop event propagation
}
}
So when I click on the ObjectListView in Form1, the Form1.ObjectListView_MouseDown method was called first and after that ObjectListView.OnMouseDown method.
I'm using .Net Framework 4.

Related

Understanding C# Event Handler: why no event?

Trying to get an event handler going, but more than that, understand the syntax behind these things. The following code compiles, I can see my DataGrid "SemesterView" inside Form1 but no event is triggered when the control SemesterView is entered, when I click on a cell, nuthin. What I am doing wrong? I've played around with other events in case I'm misunderstanding "Enter" but still nothing.
Within the form1 code
SemesterView semesterView;
semesterView = new SemesterView();
this.Controls.Add(semesterView);
semesterView.Enter += new SemesterView.EventHandler(semesterView.MyEvent1);
and in the SemesterView class
class SemesterView : DataGridView
{
public delegate void EventHandler(string p);
public event EventHandler Enter;
public SemesterView()
{// code to create columns rows etc}
public void MyEvent1(string p)
{ MessageBox.Show("event 1 works");}
}
You want to create a method that handles the event in SemesterView and tie the event to the handler:
class SemesterView : DataGridView
{
public void MyEvent(object sender, EventArgs args)
{
}
}
Then in Form1
SemesterView semesterView;
semesterView = new SemesterView();
this.Controls.Add(semesterView);
semesterView.Enter += semesterView.MyEvent;

How to call mainform method in another form in usercontrol C#

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();

Accessing a Parent form button from a Child form button

I have a program that has a parent form which then creates a child form. Upon clicking the updateButton within the child form, I want the searchButton within the parent form to fire.
However I get an error for protection reasons. I have tried setting everything Public just to see, still wont work for me.
Error 1 'SalesSystem.SystemForm.searchButton' is inaccessible due to
its protection level SalesSystem\UpdateForm.cs 111 20 SalesSystem
This is what I have so far.
Parent Code
namespace SalesSystem
{
public partial class SystemForm : Form
{
public SystemForm()
{
InitializeComponent();
}
protected void searchButton_Click(object sender, EventArgs e)
{
//search code
}
private void updateButton_Click(object sender, EventArgs e)
{
try
{
UpdateForm upForm = new UpdateForm(resultBox.SelectedItems[0].Text, dbdirec, dbfname);
upForm.ShowDialog(this);
}
catch (Exception)
{
//
}
}
}
Child Code
namespace SalesSystem
{
public partial class UpdateForm : Form
{
public UpdateForm(string selectedPerson, string dbdirec, string dbfname)
{
InitializeComponent();
}
private void updateButton_Click(object sender, EventArgs e)
{
//do stuff
SystemForm parent = (SystemForm)this.Owner;
parent.searchButton.PerformClick();
this.Close();
}
}
}
Your searchButton button control is set to private by default in WinForm. You've said you set everything to public but I assume you mean you've set everything in the code you've posted to public. There are a few ways to fix this. The direct fix would be to simply go to Visual Studio designer, select the button, and set its Modifier property to internal or public.
However, it seems you're closing your form straight after so I'd just have my parent form subscribe to the FormClosing event of the form.
UpdateForm upForm = new UpdateForm(resultBox.SelectedItems[0].Text, dbdirec, dbfname);
upForm.FormClosing += (s, o) =>
{
//your code for what the parent class should do
};
upForm.ShowDialog(this);
If you're not closing the form then you can create your own event handler that your parent form subscribes to.
You have 2 options:
create a public void search() method in your parent form. Then, instead of accessing the the button on the parent form and invoking its click event, you run the search code directly. The new method is not tied to a GUI element and accessing it from a different form is no problem.
The better solution is to create a delegate. A delegate is an execution target that will be assigned at run time. The parent form still has a public void search() method. And when it creates the child form, it will pass the name of that function as parameter. The child form has no knowledge about the parent form (as opposed to the first option where the child MUST know that there is a method called search()). When it is time to inform whoever created the child form, the delegate is called. This is a small example:
public partial class SystemForm : Form
{
public delegate void dSearch();
public SystemForm()
{
InitializeComponent();
}
protected void searchButton_Click(object sender, EventArgs e)
{
search();
}
private void search()
{
//search code
}
private void updateButton_Click(object sender, EventArgs e)
{
try
{
UpdateForm upForm = new UpdateForm(resultBox.SelectedItems[0].Text, dbdirec, dbfname, search);
upForm.ShowDialog(this);
}
catch (Exception)
{
//
}
}
}
And the child form:
public partial class UpdateForm : Form
{
private SystemForm.dSearch _target;
public UpdateForm(string selectedPerson, string dbdirec, string dbfname, SystemForm.dSearch target)
{
_target = target;
InitializeComponent();
}
private void updateButton_Click(object sender, EventArgs e)
{
//do stuff
_target();
this.Close();
}
}
You should use the "Model View Controller" or "Model View Presenter" pattern to approach this kind of thing.
Each form should only be concerned with displaying its contents to the user. When it comes to responding to UI events such as button clicks, each form (i.e. each "View") should simply raise an event which informs the controller/presenter that something has happened.
The controller/presenter should then respond appropriately. Then the logic that wires together different forms (such as the parent and child forms in your example) is encapsulated in the Controller class. Such logic does not really belong in either of the forms.
I wrote an example that demonstrates a simple design to do this sort of thing in another answer some time ago. Rather than copy/paste it all here, I'll just give you a link to it:
How to make Form1 label.text change when checkbox on form2 is checked?
You'll have to scroll down to see my answer. It's broadly similar to what you're doing; hopefully it will make sense to you! Follow the instructions to make a test application and run it to see what happens.
I'm tired and might be missing something but that is correct behaviour.
Your child form does not directly inherit from your parent form.
Your parent form has a protected level, so only it and classes that extend it can access the method.
2 solutions:
Change your child form to:
public partial class UpdateForm : SystemForm
Change method to public
public void searchButton_Click(object sender, EventArgs e)
You could expose a Search Event from your UpdateForm and subscribe to this event in the SystemForm
namespace SalesSystem
{
public partial class SystemForm : Form
{
public SystemForm()
{
InitializeComponent();
}
protected void searchButton_Click(object sender, EventArgs e)
{
//search code
}
private void updateButton_Click(object sender, EventArgs e)
{
try
{
UpdateForm upForm = new UpdateForm(resultBox.SelectedItems[0].Text, dbdirec, dbfname);
upForm.OnSearch += Search;
upForm.ShowDialog(this);
}
catch (Exception)
{
//
}
}
private void Search(string searchParameter)
{
....
}
}
namespace SalesSystem
{
public delegate void SearchEventHandler(string searchParameter);
public partial class UpdateForm : Form
{
public event SearchEventHandler OnSearch;
public UpdateForm(string selectedPerson, string dbdirec, string dbfname)
{
InitializeComponent();
}
private void updateButton_Click(object sender, EventArgs e)
{
//do stuff
OnSearch("SearchThis");
this.Close();
}
}
}

C#, How to create an event and listen for it in another class?

I can't figure out how to do this, heres sample code. Of what I wish to do.
public Class MainForm : Form
{
MyUserControl MyControl = new MyUserControl;
private void Button_Click(object sender, EventArgs e)
{
//Create MyEvent
}
}
public Class MyUserControl : UserControl
{
//listen for MyEvent from MainForm, and perform MyMethod
public void MyMethod()
{
//Do Stuff here
}
}
Step 1) Expose an event on MainForm... say..
public event Action simpleEvent
Step 2) Give MyUserControl a constructor that takes an instance of MainForm and bind an action to that event
public MyUserControl(MainForm form) {
form += () => Console.WriteLine("We're doing something!")
}
Step 3) raise the event in MainForm.Button_Click
if(simpleEvent != null) simpleEvent();
Note: You could register your own delegates and work with something other than lambda expressions. See http://msdn.microsoft.com/en-us/library/17sde2xt.aspx for a more thorough explanation
Your end result would look like...
public Class MainForm : Form
{
public event Action MyEvent;
MyUserControl MyControl = new MyUserControl(this);
private void Button_Click(object sender, EventArgs e)
{
if(simpleEvent != null) simpleEvent();
}
}
public Class MyUserControl : UserControl
{
//listen for MyEvent from MainForm, and perform MyMethod
public MyUserControl(MainForm form) {
simpleEvent += () => MyMethod();
}
public void MyMethod()
{
//Do Stuff here
}
}
This is how to delegate to an event of a private member, so the outside can listen to it.
public event EventHandlerType EventHandlerName
{
add
{
this._privateControl.EventHandlerName += value;
}
remove
{
this._privateControl.EventHandlerName -= value;
}
}
Another option would be to have an event in your form class:
public event EventHandler MyEvent;
And listen to the private member's event:
this._customControl.SomeEvent += this.SomeEventHandler;
With this:
private void SomeEventHandler(object sender, EventArgs e)
{
if (this.MyEvent != null)
{
this.MyEvent(this, e);
}
}
The usage from the outside in both cases will be the same:
var form = new Form1();
form1.MyEvent += (o, e) => { Console.WriteLine("Event called!"); };
The bottom line is the you must implement functionality inside your form to allow the outside subscribe/listen to inner events.
//listen for MyEvent from MainForm, and perform MyMethod
That's the wrong way around. Publishing an event in control is useful, the control cannot possibly guess how it is going to get used. It however most certainly should not know anything about an event that may or may not be available in the form that it gets dropped on. That has the nasty habit of blowing up when the form just doesn't (yet) have the event. The bad kind too, a crash at design time that puts up the White Screen of Darn and prevents you from fixing the problem.
A form doesn't have to guess, it knows exactly what controls it has. So where ever in the form you might want to raise the event, just call the control's MyMethod method directly. And if that's wrong for some reason, like removing the control but not the call, then you just get a compile error that's easy to fix.

How to add a control to a panel on a form from another user control

I have a form1.cs and in that form I have a panel1, in the load event of the form1.cs I am adding a control to the panel1. Now my issue is, I have a control called Numbers.cs, I need to add another control to that panel1 but from this control in a button event. How can I do this?
public partial class Number : UserControl
{
public Number()
{
InitializeComponent();
}
private void btnAcceptWelcome_Click(object sender, EventArgs e)
{
//HERE I NEED TO PASS A CONTROL TO THE PANEL1 IN FORM1.CS
//NOT SURE HOW TO DO THIS.
}
}
MORE INFO
So basically I have a folder called UserControls and in that folder I have
Numbers.cs
Letters.cs
Welcome.cs
All of them user controls, then i have a form
Form1.cs
Form1.cs instantiates Welcome and it is added to a Panel1 on the Form1.cs on form load. Then Welcome.cs has a button, when I click this button I need to swap to Numbers.cs. But I dont know how to do this from Welcome.cs
Another way would be to use a Custom Event raised by Numbers and handled by Form1 to pass the control and add it to your Panel's Control Collection.
This is an example of an Custom Event added to UserControl1
Form1
public partial class Form1 : Form
{
UserControl2 mySecondControl = new UserControl2();
public Form1()
{
InitializeComponent();
userControl11.AddControl+=new EventHandler(SwapControls);
}
private void SwapControls(object sender, EventArgs e)
{
panel1.Controls.Remove(userControl11);
userControl11.AddControl -= new EventHandler(SwapControls);
panel1.Controls.Add(mySecondControl);
}
}
UserControl
public partial class UserControl1 : UserControl
{
public event EventHandler AddControl;
public UserControl1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
this.AddControl(this, new EventArgs());
}
}
Note:
Untested code
Assuming Form1 has (or can get) a reference to Number
Add an event handler to Number:
public partial class Number : UserControl
{
// event handler Form1 will subscribe to
public EventHandler<EventArgs> OnWelcomeAccepted = (o, e) => { };
public Number()
{
InitializeComponent();
}
private void btnAcceptWelcome_Click(object sender, EventArgs e)
{
// raise the event
OnWelcomeAccepted(sender, e);
}
}
...Form1 will have a subscription after InitializeComponent(); note the additional subscription to ControlAdded:
public partial class Form1 : Form {
public Form1()
{
InitializeComponent();
this.ControlAdded += Control_Added;
// subscribe to the event and provide the implementation
Number.OnWelcomAccepted += (o, e) => { Controls.Add(GetControl( )); }
}
private void Control_Added(object sender, System.Windows.Forms.ControlEventArgs e)
{
// process size and placement and show
}
}
No other control should be adding anything directly to Form1. Let Form1 control it's children.
One way is to have a reference to panel1 within numbers since both of them are created within form1 you can pass one as an argument to the other's constructor.
It's not very clear from your description but you can just pass the control you want in the constructor or a property. Since in C# objects are always by reference you will be action on the same control in the Button event. You can always write your own event and have the panel register for it. A more complete code sample would be great.
I'm still a little unsure of what you're doing exactly but that's OK. I think the most flexible approach is to create your own custom event. Outside of any class create a delegate:
public delegate void WelcomeClick(object sender, EventArgs e);
Inside Welcome you need to create the event handler, it can be either static or part of the instance:
public event WelcomeClick OnClick;
Inside the Button Click event in welcome you can just call that event with the same parameters:
if (OnClick != null)
OnClick(sender, e);

Categories

Resources