I have a Canvas which is present in a UserControl, I have attached the DoubleClick event to this UserControl like this
mainWindow.CanvasLayout.MouseDoubleClick +=
new MouseButtonEventHandler(CanvasLayout_MouseDoubleClick);
I am using this event handler to achieve full screen functionality.
Now, Canvas can have various controls placed inside it. Drag and drop functionality is implemented for these controls similar to this codeproject article.
Basically, I handle these events for a control -
this._dragSource.PreviewMouseLeftButtonDown +=
new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonDown);
this._dragSource.PreviewMouseMove +=
new System.Windows.Input.MouseEventHandler(DragSource_PreviewMouseMove);
this._dragSource.PreviewMouseLeftButtonUp +=
new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonUp);
Now, when user DoubleClicks on a control(present in canvas) both DoubleClick(Full Screen) and single Click(drag & drop) operations are performed, i.e. if user double clicks on control and change its mouse position quickly, control position is changed(its dragged and dropped to new position).
Is there any way I can prevent drag and drop operation when user double clicks on a control?
Got it, Instead of handling MouseDoubleClick event, I used PreviewMouseLeftButtonDown -
mainWindow.CanvasLayout.PreviewMouseLeftButtonDown
+= new MouseButtonEventHandler(CanvasLayout_PreviewMouseLeftButtonDown);
and
void CanvasLayout_PreviewMouseLeftButtonDown(object s, MouseButtonEventArgs e)
{
if (e.ClickCount > 1)
{
// Do double-click code
// Code for FullScreen
e.Handled = true;
}
}
What you need to do is, on mouse up start a timer the time should be retrieved from SystemInformation.DoubleClickTime, and do the click action on the timer tick (only if you didn't detect the double click in the mean time, obviously).
Likewise use SystemInformation.DragSize to prevent accidental drag&drop.
Note, the SystemInformation class is part of WinForms so you need to add a reference to System.Windows.Forms, this class will work just fine in WPF apps.
Related
I have a ListView control on my form. I have set its display mode to LargeImageList. I need to handle the items inside this control. So I have written code for its click event. But I see now that this event is not triggered when I click in an empty area inside it.
How can I make my ListView aware of the clicks on its area regardless.
To capture mouse clicks on the "white space" around the ListView items, you will need to use the MouseDown/MouseUp events.
This will also capture clicks to the items as well.
I've used the Global Mouse Hook for similar issues. You can use it to detect Mouse Clicks anywhere on the screen, then just check the click was within the listview control bounds.
Grab the code from Global Mouse Key Hook
IKeyboardMouseEvents m_GolbalHook = Hook.GlobalEvents();
m_GolbalHook.MouseClick += m_GolbalHook_MouseClick;
private void m_GolbalHook_MouseClick(object sender, MouseEventArgs e)
{
if (listView.Bounds.Contains(e.Location)) && (e.Button == System.Windows.Forms.MouseButtons.Left))
{
//Do Stuff
}
}
I am currently working on my first User Control, and are now experiencing some minor problems.
The control is a custom PictureBox with a Button to clear the picture. I am overriding the OnPaint(PaintEventArgs e) method and invalidating the control on MouseEnter and MouseLeave to draw new borders to the control based on mouse state.
This all work as it should. The problem occurs when I want to add a button in the upper right corner (relative to the PictureBox).
1.
The button will not have the correct location relative to the PictureBox. It is placed at the 0th Y-coordinate which is fine, but the X-coordinate is only at position 100, where it should be 160. I am using the User Controls Width property when placing the button, but it seems as it is getting a default value of 100 instead of the 160 that is assigned creating the PictureBox on the form.
CustomPictureBox cpic = new CustomPictureBox();
cpic.Location = new Point(20, 20);
cpic.Height = 80;
cpic.Width = 160;
this.Controls.Add(cpic);
And the button is added like this:
btnClear = new Button();
btnClear.Width = 20;
btnClear.Height = 20;
btnClear.Location = new Point(this.Width - btnClear.Width, 0);
btnClear.Text = "X";
this.Controls.Add(btnClear);
Where does this DefaultValue come from and how can it prevent the control from using it and instead following the width that is specified when initializing a new instance of the control?
2.
Another thing is that I can not get the events for the button to work. I have been googling a lot and tried everything that I can possibly think of, but the events are still not fireing :(
It must be possible to hook on to the buttons MouseClick, MouseEnter and MouseLeave events directly from the User Control?
If you want to see the complete code, you can find it here:
http://pastebin.com/vL14Q5CX
Thanks!
The button is not aligning as expected because:
The button's location is being set in the constructor of the user control
The user control's width is then set in the form
The user control's Resize event is not being handled
In your user control wire up the Resize event and update the location of the button, i.e.:
private void CustomPictureBox1_Resize(object sender, EventArgs e)
{
btnClear.Location = new Point(this.Width - btnClear.Width, 0);
}
The reason the button click events aren't working is because they only bubble up to the parent user control; they will not be received by the form that is hosting the control. You need to create a new custom event handler on your user control that you fire from within the button click event of your user control. You then handle that custom event handler on your form.
HTH
I'm having some troubles getting DragDrop.DoDragDrop to work along nicely with a left click event.
My control has several links which can either be dragged around or left clicked to visit.
I currently subscribe to the preview mouse move event which is where i launch the drag and drop event if the left mouse button is pressed.
In another call back i handle the mouse left button down and up event to check for a click. Is there anyway to check if DragDrop actually had a drag drop event take place?
See this link drag drop in wpf explained end to end and scroll down a little to the section "Detecting Drag & Drop"
Code inserted here encase the blog goes missing...
From [http://msdn2.microsoft.com/en-us/library/aa289508(vs.71).aspx] Here is the sequence of events in a typical drag-and-drop operation:
Dragging is initiated by calling the DoDragDrop method for the source control.
The DoDragDrop method takes two parameters:
data, specifying the data to pass allowedEffects, specifying which operations (copying and/or moving) are allowed
A new DataObject object is automatically created.
This in turn raises the GiveFeedback event. In most cases you do not need to worry about the GiveFeedback event, but if you wanted to display a custom mouse pointer during the drag, this is where you would add your code.
Any control with its AllowDrop property set to True is a potential drop target. The AllowDrop property can be set in the Properties window at design time, or programmatically in the Form_Load event.
As the mouse passes over each control, the DragEnter event for that control is raised. The GetDataPresent method is used to make sure that the format of the data is appropriate to the target control, and the Effect property is used to display the appropriate mouse pointer.
If the user releases the mouse button over a valid drop target, the DragDrop event is raised. Code in the DragDrop event handler extracts the data from the DataObject object and displays it in the target control.
Detecting Drag & Drop
Before the DoDragDrop is called, we must detect a mouse Drag operation on the source... A mouse drag is usually a MouseLeftButtonDown + a MouseMove (before MouseLeftButton goes up).
So, our drag & drop source control needs to subscribe to these two events:
void Window1_Loaded(object sender, RoutedEventArgs e)
{
this.DragSource.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonDown);
this.DragSource.PreviewMouseMove += new MouseEventHandler(DragSource_PreviewMouseMove);
}
To prevent from starting a false drag & drop operation where the user accidentally drags, you can use
SystemParameters.MinimumHorizontalDragDistance and SystemParameters.MinimumVerticalDragDistance
One way to do this is on MouseLeftButtonDown, record the starting position and onMouseMove check if the mouse has moved far enough..
void DragSource_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && !IsDragging)
{
Point position = e.GetPosition(null);
if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
{
StartDrag(e);
}
}
}
void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_startPoint = e.GetPosition(null);
}
Its a Drag .. now what?
The data! You need to find out what is under the mouse when dragging.
I will omit take the easy way out and assume that whoever is triggering the MouseMove is what I want to drag .. so look at MouseEventArgs.OriginalSource.. [or you could do some 2D HitTesting using VisualTreeHelper .. In Part3 of this write up will try to walk you through hit testing the listbox -which is the other common scenario I encounter-.
Once you have the object to drag, you will need to package what you are a sending into a DataObject that describes the data you are passing around.
DataObject is a wrapper to push generic data (identified with extensible formats) into drag/drop.. As long as both the source and destination understand the format, you will be set. As such, DataObject has a couple interesting methods:
SetData ( Type format, object data ) /// format is the "format" of the day you are passing ( e.g. Formats.Text, Formats.Image, etc.. ) you can pass any custom types.
GetDataPresent ( Type format ) /// is what the drop target will use to inquire and extract the data .. if it is a type it can handle, it will call GetData () and handle it ..
Not much interesting stuff here.. In the sample I just hard-coded my data to be of type string... this makes it easier to paste into external containers (for example Word, which you can use to test this part of the write-up). I do have to stress that drag & dropping should be about the data ...
Providing visual feedback during the drag & drop operation..
Before we call DoDragDrop () we have a few 'choices' to make around the feedback we want to provide and the 'scope' of the d&d.
Do we want a custom cursor to display while we are doing the Drag operation ? If we want a cursor, what should it be?
How far do we want to drag? within the app or across windows apps?
Simplest scenario: No custom cursor and we want it to drag across apps:
If you don't want a fancy cursor, you are done!! You can call DoDragDrop directly ...
private void StartDrag(MouseEventArgs e)
{
IsDragging = true;
DataObject data = new DataObject(System.Windows.DataFormats.Text.ToString(), "abcd");
DragDropEffects de = DragDrop.DoDragDrop(this.DragSource, data, DragDropEffects.Move);
IsDragging = false;
}
Note: this code allows you to drag & drop across processes, it uses the default operating system feedback ( e.g. + for copy)..
there are Drag Over/ Enter / Leave Attached events
you can subscribe methods to these (or one) events on your dragged UIElement and see if the Dragging occurs .
I am working on a C# piano. I have already built the music keyboard and the staff. Everytime a user presses a key, it is displayed on the staff in its relevant position.
The music note displayed on the staff is stored in an array of pictureboxes, as shown below.
public void addPictureBox(int x, int y, Image image)
{
picBox[cnt] = new PictureBox();
picBox[cnt].Image = image;
picBox[cnt].Location = new Point(x, y);
picBox[cnt].BackColor = Color.Transparent;
panel3.Controls.Add(picBox[cnt]);
picBox[cnt].BringToFront();
picBox[cnt].MouseDown += new MouseEventHandler(Pic_MouseDown);
picBox[cnt].MouseUp += new MouseEventHandler(Pic_MouseUp);
cnt++;
}
The Pic_MouseDown and Pic_MouseUp events allow the user to play the note by clicking on it from the staff.
What I want to do now is to create an event on picBox[cnt] for dragging. However, picBox[cnt].MouseDown and picBox[cnt].MouseUp have already been registered to Pic_MouseDown and Pic_MouseUp event handlers.
How can I do an event to handle dragging since MouseDown and MouseUp have already been registered to other event handlers?
Thanks :)
The great thing about event handlers is that you can have as many attached handlers as you want. The += (operator overload) means you are attaching a new event handler to the existing handlers. You can add as many event handlers as you desire.
Event Handler Overview
If you create a isDragging boolean instance field, which you set to true in the mouse down and false in mouse up, then you can use the mouse move event to detect whether the object should be moved or not.
You'll need to use a combination of MouseDown, MouseMove and MouseUp events. In MouseDown, you do little more than set a flag to indicate that the mouse was pressed, and record where it was pressed. In MouseMove, you check to see if the button is still down and the cursor has moved further than SystemInformation.DragSize, which indicates that the user is dragging rather than clicking, and start the drag operation if needed. In MouseUp, you either complete the drag or perform the click action.
I believe the conventional wisdom for Drag-n-drop is
Call DoDragDrop in the (source control's) MouseDown handler
set AllowDrop = true in the targe control's AllowDrop property
There's a whole series of drag events to fine tune behavior
One does not seem to have to worry about any latent MouseUp or MouseClick events. I'm assuming this is because the physical actions of button press and release are done over different controls and/or the mouse physically moved "far enough".
How to capture mouse wheel on panel in C#?
I'm using WinForms
EDIT:
I try to do it on PictureBox now.
My code:
this.pictureBox1.MouseClick += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseClick);
this.pictureBox1.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseClick);
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
MessageBox.Show("Click");
}
Clicking works. Wheelling doesn't.
Why?
If you can't see the "MouseWheel" event on a component, then you need to create it manually. Also, we need to focus that component, otherwise the "MouseWheel" event will not work for that component. I will show you how to create a "MouseWheel" event for "pictureBox1" and how it works.
INSIDE THE CONSTRUCTOR, create a mousewheel event on that component.
InitializeComponent();
this.pictureBox1.MouseWheel += pictureBox1_MouseWheel;
CREATE THE FUNCTION manually. According to my example, call it "pictureBox1_MouseWheel"
private void pictureBox1_MouseWheel(object sender, MouseEventArgs e)
{
//you can do anything here
}
CREATE a MouseHover event on that component (Go to properties in PicureBox1, select event, locate "MouseHover" and double-click the "MouseHover" event).
CALL "Focus()"; method inside that MouseHover event.
pictureBox1.Focus();
Now run the program.
Windows sends the WM_MOUSEWHEEL message to the control that has the focus. That won't be Panel, it is not a control that can get the focus. As soon as you put a control on the panel, say a button, then the button gets the focus and the message.
The button however has no use for the message, it's got nothing to scroll. Windows notices this and sends the message to the parent. That's the panel, now it will scroll.
You'll find code for a custom panel that can get the focus in this answer.
UPDATE: note that this behavior has changed in Windows 10. The new "Scroll inactive windows when I hover over them" option is turned on by default. The makes the mouse wheel behavior more consistent with the way it works in a browser or, say, an Office program. In this specific case the picturebox now will get the event. Watch out for this.
To wire it up manually...
this.panel1.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseWheel);
private void panel1_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
{
///process mouse event
}
Easier method is in visual studio click on panel, goto properties viewpanel, select events, locate and double click the "mousewheel" event.
In Winforms, this is achieved using the Control.MouseWheel event
Getting mousewheel events is tricky. The easiest way is using
this.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseWheel);
instead of
this.panel1.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseWheel);
This way the form gets the event instead of control. This way is easy but has one problem: you can use only one mousewheel event in your form.
If you have more than one control to get mousewheel event the best way is This answer by "Shehan Silva - weltZ"