What is the easiest way to determine if my current cursor position is over a particular (WinForm) control?
I can - of course - calculate the control's position and then check whether the cursor position is within this rectangel. But maybe there is something already existing for this....
I cannot use OnMouseOver event for this, because the decisions must take place within (another) event. In order to further explain here is what I do (in pseudo code). The problem is, when I start the drag event, then move to another control (outside this), release the mouse to finish the drop, the context menu is displayed on the "wrong" control (the drop target). This is what I want to avoid....
private void TableControlMouseDown(object sender, MouseEventArgs e)
{
...
// this is a User control with some sub controls
// when selected start drag and drop
if (SOMEConditions)
{
// start drag and drop operation
DragAndDropWrapper dragAndDropWrapper = new DragAndDropWrapper(this.ObjectsToDrag, this);
this._subControl.DoDragDrop(dragAndDropWrapper, DragDropEffects.Copy);
}
// context menu
// check should go here
// something like "is pt still over "this" or over the drag target ...
Point pt = this._subControl.PointToClient(Control.MousePosition);
this._myContextMenu.Show(this._subControl, pt);
}
-- as of today --
See below for the extension method I am using at the moment ...
You can declare:
bool insideMyControl = false;
Then trap MouseEnter (and set insideMyControl = true) and MouseLeave events (and set insideMyControl = false) on the particular control.
Then in your event look insideMyControl value.
This is the best solution I have found so far. Actually it is pretty easy (as always once you know how to do it ;-) since PointToClient gives me relative coordinates which drastically reduces the effort .... As an extension method it also is easy to use with all controls.
/// <summary>
/// Is the mouse pointer over the control?
/// </summary>
/// <param name = "control"></param>
/// <returns></returns>
public static bool IsMouseOverControl(this Control control)
{
if (control == null) throw new ArgumentNullException("control");
Contract.EndContractBlock();
Point pt = control.PointToClient(Control.MousePosition);
return (pt.X >= 0 && pt.Y >= 0 && pt.X <= control.Width && pt.Y <= control.Height);
}
Did you tried GetCursorPos WIN32 API function
Try this.
Point p = new Point();
GetCursorPos(ref p);
Related
Note: I have found a solution to my problem so I am posting this for reference purposes, although I would be happy to be educated with a better solution.
I'm trying to provide double click functionality on a Silverlight DataGrid by hooking into the UIElement.MouseLeftButtonDown but when I subscribe to the DataGrid.MouseLeftButtonDown using XAML or the DataGrid.MouseLeftButtonDown += syntax, my event handler is not called when I click on the rows within the DataGrid. If I click on the Header, the event is raised.
If I subscribe to the same event at the parent UserControl level, the event handler is called successfully as you would expect based on Silverlight RoutedEvents but then I have to detect whether the click occurred on the DataGrid or somewhere else.
If I subscribe to the event using this UIElement.AddHandler syntax, as shown below, then it works as expected based on the handledEventsToo: true parameter.
dataGrid.AddHandler(UIElement.MouseLeftButtonDownEvent,
new MouseButtonEventHandler(dataGrid_MouseLeftButtonDown)
, handledEventsToo: true);
It seems that the DataGrid implementation is marking these events as handled, prevent event bubbling, by default in one of the child UIElements, which is not what I expected initially. With more thought I can see that the click behaviour drives all sorts of things (select item, edit field etc.) so perhaps the implementation makes sense.
I had the same problem and I used MouseLeftButtonUp which fires the event but the clickcount value is always 1.
Here is the fix for that:
private const int MOUSE_SENSITIVITY = 300;
private DateTime _previousClick;
private void exceptionsDataGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
DataGrid dg = (sender as DataGrid);
DateTime current=DateTime.Now;
LoggerService.Exception exception = (LoggerService.Exception)dg.SelectedItem;
if (_previousClick != null)
{
TimeSpan clickSpan = current - _previousClick;
if (clickSpan.TotalMilliseconds < MOUSE_SENSITIVITY)
{
MessageBox.Show("You double clicked!");
}
}
_previousClick = current;
}
The Slider control in WPF doesn't work properly for what I'm looking for.
I need to slide 2 different controls (Slider) at the same time (with one finger each).
When I touch the first Slider, it gets all the focus and I cannot touch anything else with my second touch device.
So I need to create my own Slider (MySlider) that inherit from Slider.
I've made 4 methods:
protected override void OnTouchDown(TouchEventArgs e)
protected override void OnTouchUp(TouchEventArgs e)
protected override void OnTouchLeave(TouchEventArgs e)
protected override void OnTouchMove(TouchEventArgs e)
But is there a way to move the Slider exactly like with the mouse? Or I need to calcule each time my touch device moved something like:
protected override void OnTouchMove(TouchEventArgs e)
{
base.OnTouchMove(e);
if (this.Value <= this.Maximum && this.Value >= this.Minimum)
{
Point newPoint = e.GetTouchPoint(this).Position;
this.Value += (this.lastPoint.Y - newPoint.Y);
lastPoint = newPoint;
}
e.Handled = true;
}
And in this case the movement doesn't move at the same speed as the finger...
You might want to check out the Surface 2.0 SDK as it contains a class called SurfaceSlider, which I believe will allow for two or more sliders to be updated simultaneously. This SDK can be used to target applications built for Windows 7.
I'm not familiar with multi-touch events in WPF so will not be able to help you with that. However, for moving the mouse to the same location as your touching then you can look at this answer here.
Your problem that you're assuming that the width of the control is equivalent to the maximum value. You need to take out the factor the actual width relative to the difference between the max and min values.
This can only be done via events since no routed event or DPs for mouse position.
Have a look at this code, I believe it solved CA2000 but I want to make sure I'm not overlooking something. Basically this code loads a new Control based on what is selected in a TreeView. That Control is then displayed and is visible/usable until another Node in the TreeView is selected.
private void Something(object sender, TreeViewEventArgs e)
{
ProjectTreeNode node = (e.Node as ProjectTreeNode);
foreach (Control c in optionsPlaceholderPanel.Controls)
c.Dispose();
optionsPlaceholderPanel.Controls.Clear();
if (node != null)
{
//ProjectOptions inherits from Control and is therefore IDisposable
ProjectOptions options = new ProjectOptions(node.Project);
ShowOptionsPanel(options);
}
}
private void ShowOptionsPanel(Control control)
{
optionsPlaceholderPanel.Controls.Add(control);
control.Dock = DockStyle.Fill;
}
So basically the Control is in scope always, until a new Control is loaded in place of it. When I do that, I'm disposing the prior-loaded Control so I think it's safe to ignore CA2000 in this case. Also, when the Form finally closes and optionsPlaceholderPanel is disposed, this will also dispose the child controls, right?
foreach (Control c in optionsPlaceholderPanel.Controls)
c.Dispose();
No, this code has a bug. Which in itself is triggered by a bug in the ControlCollection class. Your foreach loop is modifying the panel's Controls collection. This normally produces an InvalidOperationException, "Collection was modified, enumeration operation may not execute", but the class forgets to do this.
The Dispose() call on the control removes it from the collection. In effect, you'll only dispose every other control. This should have a side-effect, they remain visible on the panel. Ymmv. Fix:
for (int ix = optionsPlaceholderPanel.Controls.Count - 1; ix >= 0; --ix)
optionsPlaceholderPanel.Controls[ix].Dispose();
Or less efficient, although you'd never see the difference:
while (optionsPlaceholderPanel.Controls.Count > 0)
optionsPlaceholderPanel.Controls[0].Dispose();
Otherwise the code is okay, CA2000 tends to produce false warnings.
I have a silverlight control in which i am dynamically creating shapes on button click
These shapes are draggable across the form. Now i want to get top and left postion of a control on dragging (mousemove). Please help
Taking a look at your question history I can think of two approaches. I suspect that the shape is simply being placed on a Canvas and the MouseMove of which you speak refers to an event handler you've attached to the Canvas. On that basis then.
void Canvas_MouseMove(object sender, MouseEventArgs e)
{
Type currentType = e.OriginalSource.GetType();
// Make decisions based on value of currentType here
DependencyObject source = (DependencyObject)e.OriginalSource;
Point p = new Point(Canvas.GetLeft(source), Canvas.GetTop(source));
}
The more general solution is to use the TransformToVisual approach. Something like:-
var transform = ((UIElement)e.OriginalSource).TransformToVisual(MyCanvas);
Point p = transform.Transform(Point(0,0));
During a drag in Wpf, how can the mouse cursor (or perhaps using an adorner) be changed to indicate that the droptarget will not accept the dragged item?
I've tried to set e.Effects = DragDropEffects.None during the DragEnter event but this isn't working and I suspect that I've misunderstood what that feature should be used for. I've tried using the GiveFeedback event but don't see how the droptarget can influence it.
Just setting the DragDropEffects in DragEnter of the drop target should work. Is your DragEnter even getting called. Have you set AllowDrop on the drop target control?
This is the sequence of events during a drag & drop in WPF (taken from MSDN) which might help work out what's going on...
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.
I had a similar problem because I changed the cursor in the GiveFeedback handler.
This cursor was used even the drop target did reject the data.
After switching back to the default cursor (e.UseDefaultCursors = true) the cursor shape did change propely to "not allowed".
You didn't say if you use the DragOver even. Maybe you're setting e.Effect = DragDropEffects.All; in this even and it will be fired repeatedly after you enter the target control instead of DragEnter that will be fired just once.
private void arbol_DragOver(object sender, DragEventArgs e)
{
if (some_reason)
e.Effect = DragDropEffects.None;
else
e.Effect = DragDropEffects.All;
}
If you didn't use this event or didn't modify e.Effect within, then it's hard to say. Code is needed.
If you want the app to take your change in account,
you should set Handled property to true:
private void OnDragOver(object sender, DragEventArgs e)
{
if (your_test)
e.Effects = DragDropEffects.Link;//or other effect you want
else
e.Effects = DragDropEffects.None;
e.Handled = true;
}