I have a Panel on a Form with a MouseClick event. The problem is that the MouseClick event isn’t fired every time I click the panel. It’s very random when it skips a click.
I guess I could use MouseDown and MouseUp event instead, but I’d like to know why this is happening...(I have the same problem when I use "Click" event)
I’ve tested this outside my project with a very simple test project and it’s still doing it:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var panel = new Panel
{
Location = new Point(10, 10),
Size = new Size(200, 200),
BackColor = Color.Red
};
panel.MouseClick += panel_MouseClick;
//panel.Click += panel_Click; --Also skips clicks.
this.Controls.Add(panel);
}
void panel_MouseClick(object sender, MouseEventArgs e)
{
Console.WriteLine("Click");
}
}
The Panel class has ControlStyles.StandardDoubleClick turned on. So you are quite likely to generate MouseDoubleClick events, you don't see them since you didn't subscribe the event.
More than one way to fix that beyond turning off the style flag and subscribing that event as well, the MouseDown event is an alternative for example. Turning off the style requires deriving your own class from Panel with just a constructor that calls SetStyle().
Related
I have a problem with User form Click that I am trying to make in C# using a usercontrol.
It consists of a picturebox and a label. I want to call the click event but the picturebox and the label don't do anything when I click them. Only the background area of the usercontrol does what I want it to do. Any ideas?
here's my code
for (int i = 0; i < listitems2.Length; i++)
{
listitems2[i] = new declined();
//adding sample data to each dynamic user
listitems2[i].dicon = dicon[i];
listitems2[i].did = did[i];
listitems2[i].dname = dname[i];
//adding data to flow layout panel
flowLayoutPanel2.Controls.Add(listitems2[i]);
// below line will assing this (usercontrolclick) event to every user control created dynamically
listitems2[i].Click += new System.EventHandler(this.UserControl_Click);
}
for click function
void UserControl_Click(object sender, EventArgs e)
{
string ctrlName = ((UserControl)sender).Name;
applicable obj = (applicable)sender;
studid.Text = obj.id;
studname.Text = obj.name;
pictureBox1.Image = obj.icon;
}
You added OnClick handler to your UserControl, so only clicks made on UserControl will be registered. To enable Click event for all the controls on your form, you need to add your click handler to all other controls.
But, according to your code, you're adding click handling on form where your control is located. In this case, you cannot register clicks on control from "outside" (you can by making Label and PictureBox controls public and adding handlers for their OnClick events but that is wrong way to do it)
My suggestion is to make custom event on your form and raise it on Form, Label and PictureBox click, and then make handler for that event on form which holds your UserControl.
Something like this:
//make custom eventHandler on your UserControl
[Browsable(true)]
public event EventHandler UserControlClicked;
//constructor
public UserControl1()
{
InitializeComponent();
//after intialize compoment add same handler for all three controls
this.Click += ControlClicked;
this.pictureBox1.Click += ControlClicked;
this.label1.Click += ControlClicked;
}
//this method will "catch" all clicks
private void ControlClicked(object sender, EventArgs e)
{
//raise event
UserControlClicked?.Invoke(this, e);
}
and on form where your custom control is, add handler for UserControlClicked (maybe with cast, I don't know what listItems2[i] contains):
listitems2[i].UserControlClicked+= new System.EventHandler(this.UserControl_Click);
or maybe like this (with casting)
(listitems2[i] as UserControl1).UserControlClicked+= new System.EventHandler(this.UserControl_Click);
and handle the rest in your UserControl_Click method like before
I have a System.Windows.Forms.PictureBox inside System.Windows.Forms.Panel. The Panel has:
fixed dimensions
AutoScroll=true
event handler is subscribed to MouseWheel which is used to zoom-in or zoom-out
On zoom-in/zoom-out I change the dims of the PictureBox and if it exceeds the Panel dimenstions the vertical and/or horizontal scrolls are shown since AutoScroll=true.
Now, on Windows 7 (I have Enterprise edition), once any or both scrollers appear and I continue zooming in with the mouse wheel, the subscribed event handler to MouseWheel continue to be called and the image gets bigger.
But, on Windows 10 (I have Home edition), if any of the scrollers appear, the event handler stopps to be called and the scrollers take over. Meaning the image is scrolled up/down or left/right.
The OP has confirmed in the comments that disabling the Win10 mouse setting "Scroll inactive windows when I hover over them" resolves the issue, however I believe that this can also be handled by preventing the MouseWheel event from bubbling up to the containing Panel control. Asking users to change their preferred settings to make your code function is never a desirable situation.
The following code demonstrates preventing this event bubbling. Just create a new Winform project and replace the Form1 code with this. The code creates a TextBox, and a PictureBox contained in a Panel. The purpose of the TextBox is to just to show its loss of focus when you click on the PictureBox. For Win7, click on the PictureBox to activate it and then use the mousewheel to increase/decrease the PictureBox size.
public partial class Form1 : Form
{
PictureBox pictureBox1;
Panel panel1;
public Form1()
{
InitializeComponent();
Size = new Size(500, 500);
Controls.Add(new TextBox() { TabIndex = 0, Location = new Point(350, 5)});
panel1 = new Panel() {Size = new Size(300, 300), Location = new Point(5, 5), BorderStyle = BorderStyle.FixedSingle,Parent = this, AutoScroll = true};
pictureBox1 = new PictureBox() {Size = new Size(200, 200) , Location = new Point(5,5), BorderStyle = BorderStyle.FixedSingle, Parent = panel1};
pictureBox1.Click += pictureBox1_Click;
pictureBox1.MouseWheel += pictureBox1_MouseWheel;
panel1.MouseWheel += panel1_MouseWheel;
}
private void pictureBox1_Click(object sender, EventArgs e)
{
// On Win10 with "Scroll inactive windows when I hover over them" turned on,
// this would not be needed for pictureBox1 to receive MouseWheel events
pictureBox1.Select(); // activate the control
// this makes pictureBox1 the form's ActiveControl
// you could also use:
// this.ActiveControl = pictureBox1;
}
private void pictureBox1_MouseWheel(object sender, MouseEventArgs e)
{
Rectangle r = pictureBox1.Bounds;
int sizeStep = Math.Sign(e.Delta) * 10;
r.Inflate(sizeStep, sizeStep);
r.Location = pictureBox1.Location;
pictureBox1.Bounds = r;
// e is an instance of HandledMouseEventArgs
HandledMouseEventArgs hme = (HandledMouseEventArgs)e;
// setting to true prevents the bubbling of the event to the containing control (panel1)
hme.Handled = true;
// comment out the above line to observe panel1_MouseWheel
// being called
}
private void panel1_MouseWheel(object sender, MouseEventArgs e)
{
System.Diagnostics.Debug.Print("bubbled wheel event");
}
}
reference: HandledMouseEventArgs Class
Refer to this previous question I asked where I couldn't get the click event for a UserControl on my form to fire off.
The way I have my control set up is I have the control itself sized to 50, 20. I then a have panel sized 25, 20 set to dock on the right side. In code within the UserControl itself, anytime the background of the control or the panel that acts as the "switch" are clicked, it fires off this code:
private void toggleClick(object sender, EventArgs e) {
if (toggleStatus) { // set to "on"
this.BackColor = Color.Red;
this.pnlSwitch.Dock = DockStyle.Left;
toggleStatus = false;
} else { // set to "off"
this.BackColor = Color.Green;
this.pnlSwitch.Dock = DockStyle.Right;
toggleStatus = true;
}
}
This works great and goes off every time. However, I put my UserControl in a form and tried to tie a click event to this method:
private void toggleSoundClick(object sender, EventArgs e) {
MessageBox.Show("Test");
}
When I click on the background of the control, this fires off and everything works like it should. However, if I click on the panel that acts as the "switch", the click event in my form doesn't fire (but the click event in the UserControl itself does, which isn't the issue). I can kind of understand this being something like z-indexing in CSS, but it still baffles me why a click event inside the control wouldn't cause it to fire off.
My question is, how can I get around this type of behavior?
edit I also can't integrate the behavior from the click event inside the form into the click event inside the UserControl. I have several of these controls on my form, and all have different behavior depending on which is clicked.
After taking Sinatr's suggestion I googled a bit more and found this post on here. What I ended up doing was unsubscribing all click events for the "switch" panel, then putting this code inside my UserControl.
public new event EventHandler Click {
add {
base.Click += value;
foreach (Control control in Controls) {
control.Click += value;
}
}
remove {
base.Click -= value;
foreach (Control control in Controls) {
control.Click -= value;
}
}
}
This made it so that when I subscribed the click event for the control itself to the toggleClick method, it also registered it with the "switch" panel (which is why I unregistered all other click events, otherwise it would fire off twice), and it also caused the toggleSoundClick method to be subscribed to the "switch" panel as well when I subscribed it to the control itself inside my form.
edit For my purposes I wanted to add the click events recursively to all controls, no matter the depth, so I changed the code to this
public new event EventHandler Click {
add {
subscribeToEvent(this, value);
}
remove {
unsubscribeFromEvent(this, value);
}
}
private void subscribeToEvent(Control control, EventHandler eventHandler) {
control.Click += eventHandler;
foreach (Control child in control.Controls) {
subscribeToEvent(child, eventHandler);
}
}
private void unsubscribeFromEvent(Control control, EventHandler eventHandler) {
control.Click -= eventHandler;
foreach (Control child in control.Controls) {
unsubscribeFromEvent(child, eventHandler);
}
}
In the UserControl code that is firing correctly, add
e.Handled = false;
By setting the EventArgs' Handled property to false, the click event is passed through and hopefully caught (again) by the listener in your form method.
enter code hereI've built a winform MDI application based on two baseforms, a listform with a grid (_ListForm) and a dataform to display the child data (_DataForm).
When I load an inherited dataform into the MDI, the activated event from the base-form (_DataForm) is triggered and some properties are automatically set. When I load an inherited listform (_ListForm) into the MDI, the activated event is not triggered.
My childform doesn't have an (overrided) activated event, there is no significant difference in the two forms, just one triggers the event, one doesn't.
I've added the eventhandler in code and/or with the designer : no
trigger
I've added a new activated event in the childform and called
base.onActivated(e) : no trigger
For now I moved some of the code into the textchanged event (which also occurs once), but why is this Activated-event not being triggered?
Can add tons of code-samples if needed, but not sure what to post.
EDIT: Forgot to mention, it's the same with the LoadEvent - doesn't trigger in any way.
EDIT: Sources as per request:
_BaseForm : font and general background (no events)
public partial class _BaseForm : Form
{
public _BaseForm()
{
InitializeComponent();
}
_ListForm : grid and buttons
public partial class _ListForm : _BaseForm
{
public _ListForm()
{
InitializeComponent();
this.Activated += new EventHandler(_ListForm_Activated);
this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.grid1.UltraGrid.DisplayLayout.Bands[0].Columns.ClearUnbound();
this.grid1.UltraGrid.DisplayLayout.Override.AllowAddNew = Infragistics.Win.UltraWinGrid.AllowAddNew.No;
this.grid1.UltraGrid.DisplayLayout.Override.AllowDelete = Infragistics.Win.DefaultableBoolean.False;
this.grid1.UltraGrid.DisplayLayout.Override.AllowUpdate = Infragistics.Win.DefaultableBoolean.False;
this.grid1.UltraGrid.DisplayLayout.Override.CellClickAction = Infragistics.Win.UltraWinGrid.CellClickAction.RowSelect;
this.grid1.PopulateGridColumns += new FB.Windows.Controls.Grid.PopulateGridColumnsEventHandler(grid1_PopulateGridColumns);
this.grid1.UltraGrid.InitializeLayout += new Infragistics.Win.UltraWinGrid.InitializeLayoutEventHandler(UltraGrid_InitializeLayout);
this.grid1.RowSelected += new FB.Windows.Controls.Grid.RowSelectedEventHandler(grid1_RowSelected);
}
_ListForm event (either in designer or code from ctor):
this.Activated += new System.EventHandler(this._ListForm_Activated);
this.Load += new System.EventHandler(this._ListForm_Load);
The event itself:
private void _ListForm_Activated(object sender, EventArgs e)
{
if (AutoSearchOnOpen)
{
button1_Click(sender, e);
this.grid1.Focus();
this.ActiveControl = this.grid1;
}
else
{
this.textBox1.Focus();
this.ActiveControl = this.textBox1;
}
}
I know the Click inside the activated event will fire each time it's activated, but for now that doesn't matter: the main problem is: the whole event wont fire. Although the event in the _DataForm (also inherited from _BaseForm) is fired.
In this case the grid (3rd party control) overloads (hijacks) the activated and loaded events for the form and don't trigger the base event after the original event.
I had written an event handler for MouseMove for my form
but When I add a panel to form, this handler does NOT run while mouse moves on panel.
I added event handler to panel and this works but I had several panels on the form,
is there an easier solution?
Unfortunately, WinForms doesn't support event bubbling. But you can write some code to ease the task of hooking up events.
public void AssignMouseMoveEvent(Form form)
{
foreach(Control control in form.Controls)
{
if(! (control is Panel))
continue;
control.MouseMove += PanelMouseMove;
}
}
You should call the above code passing it your current form and it will assign PanelMouseMove as event handler for MouseMove event of all the panels.
I think you should be able to "propagate" the handlers, so you don't have to re-write the code in each one. Just remember that the MouseMove event has control-relative coordinates, so if you pass the event from your panel to your form, you'll have to translate the X & Y values in the event to the form coordinates (something like subtracting panel.location.X from event.X, etc).
This code worked for me (assumes you have a form with a panel and a label. The label is named "MouseCoords"
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void ShowCoords(int x, int y)
{
this.MouseCoords.Text = string.Format("({0}, {1})", x, y);
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
this.ShowCoords(e.X, e.Y);
}
protected override void OnControlAdded(ControlEventArgs e)
{
// hook the mouse move of any control that is added to the form
base.OnControlAdded(e);
e.Control.MouseMove += new MouseEventHandler(Control_MouseMove);
}
private void Control_MouseMove(object sender, MouseEventArgs e)
{
// convert the mouse coords from control codes to screen coords
// and then to form coords
System.Windows.Forms.Control ctrl = (System.Windows.Forms.Control)sender;
Point pt = this.PointToClient(ctrl.PointToScreen(e.Location));
this.ShowCoords(pt.X, pt.Y);
}
private void Form1_Load(object sender, EventArgs e)
{
this.MouseMove += this.Form1_MouseMove;
}
}
}
You could Implement IMessageFilter to pre-process messages that are going to your controls.
http://blogs.msdn.com/csharpfaq/archive/2004/10/20/245412.aspx
However, I don't think this is a very clean way to do things from a design perspective.
No there is no simpler way, and you should assign event handler for each control where you need to receive MouseMove events.
If you set the Capture property of the form to true, it will receive all mouse input, regardless of which control that is under the mouse. It will lose the mouse capture at certain operations (I am not sure exactly when, though). Also, according to the documentation for the property, shortcut keys should not work while the mouse is captured. So, depending on what you want to achieve, this might not be the preferred way to go.
Assuming that mouse starts moving over the form rather than over the panel - which is a big assumption - you'll get a MouseLeave event when it enters a sub control. You could check the cursor location and call the mouse move code if it's still within the bounds of the form.
This doesn't work if the mouse move event starts on a control.
I found another solution :) "Raise events in controls which hide events"
I catch the event in panel and rise the Mouse move event of the form by calling onMouseMove