I would like to simulate/route right click on a WPF "control".
To make a long story short. I have an Adorner which should react to left click (so is hit test visible must be true) but at the same time I would like it to be "transparent" for the right clicks. (In another words i would like for a control under it to receive this click - btw right click makes Adorner disappear).
I tried to raise MouseRightButtonUp event on control directly under mouse (after Adorner disappears but it doesn't seem to work). I would like to avoid calling system functions (like mouse_event through P/Invoke). Can it be even done in wpf?
As far as I remember, I had troubles with routing events and changing Adorners IsHitTestVisible property. The main problem was, if I recall it correctly, that adorner and controls are on different branches of visual tree, so routed events spawned on the adorner won't make it to your controls.
I can't say much without you providing the code, but the simplest thing that should work would be to find a control under your mouse position and do
private void myAdorner_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
MouseButtonEventArgs revent = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Right);
revent.RoutedEvent = e.RoutedEvent;
//find you control
control.RaiseEvent(revent);
}
Related
I have a little problem with winforms and mousewheel events.
I have a custom user control representing a slider. Now, I have a couple groups of sliders in which each group is wrapped inside a panel. All the groups are then wrapped in another panel (which has AutoScroll set to true) and this is wrapped in a form. The slider logic is implemented such that the mousewheel can be used to change its value. For this, the slider user control gets focus when the mouse is over the slider. However, when I scroll, also the AutoScroll parent panel scrolls with it.
I've already lost a lot of time on this issue. Anybody knows what is happening here and how I can solve it? I thought the event was bubbling to the parent panel but I don't find a Handled property on the event when handling it in the Slider control (as is possible with WPF).
many thanks
We implemented the Slider as a complete custom user control (inheriting the UserControl class) with own look-and-feel.
You might have noticed that a UserControl doesn't show the MouseWheel event in the Properties window. Hint of trouble there. The WM_MOUSEWHEEL message bubbles. If the control that has the focus doesn't handle it then Windows passes it on to its Parent. Repeatedly, until it finds a parent window that wants to handle it. The Panel in your case.
You'll need to invoke a bit of black magic in your slider control. The actual event args object that get passed to the MouseWheel event is not of the MouseEventArgs type as the event signature suggests, it is HandledMouseEventArgs. Which lets you stop the bubbling. Like this:
protected override void OnMouseWheel(MouseEventArgs e) {
base.OnMouseWheel(e);
// do the slider scrolling
//..
((HandledMouseEventArgs)e).Handled = true;
}
If you are creating event dynamically like
object.event += new EventHandler<EventArgs>(eventfunction);
try un-registering the event after the eventfunction is called like this
object.event -= new EventHandler<EventArgs>(eventfunction);
I am working on a simple C# program that allows the user to use the mouse to move simple controls (Buttons, textareas, etc), similar to a visual designer like visual studio. The controls are contained in a panel and they all work as expected. However, when I call MouseDown() on the panel the controls are contained in, the event only fires when clicking on an empty part of the panel, not when I click on a Control contained within the form.
Here is my MouseDown() Code:
private void splitContainer2_Panel2_MouseDown(object sender, MouseEventArgs e)
{
Console.WriteLine("MOUSE GRABBED");
...
//More code that uses the X and Y co-ords of the mouse to check which
//Control is selected
...
}
As you can see it is very straightforward. The writeLine() is not triggered when I click on a control.
I have looked at questions such as:
ignore mouse event on label inside panel
To no avail.
Any help would be appreciated, even a better method to do what I am trying to acomplish.
Instead of using a MouseDown() event for the panel, why not trying to use the same one for every object? Based on what you are trying to do, add the following code:
Where you create each form element:
nameOfElement.MouseDown += new System.Windows.Forms.MouseEventHandler(this.splitContainer2_Panel2_Objects_MouseDown);
The mouse down method:
Control ctrl = (Control)sender;
Console.WriteLine("Moused down on: " + ctrl.Name);
//Code to manipulate mouse down
Hope this helps!
Use PreviewMouseDown. The WPF hit testing engine will not raise events on parent elements if the child element absorbs the event.
I'm writing a custom control and I'd like the control to switch from an editing state to it's normal state when a user clicks off of the control. I'm handling the LostFocus event and that helps when a user tabs away or if they click on to another control that's Focusable. But if they don't click on something Focusable, it won't switch out of it's editing state. So I have two solutions in mind:
Walk up the tree to the top most element when it goes in to an editing state and add a handler for MouseDownEvent (and handle "handled" events). In the handler I'd kick the control out of it's editing state and remove the handler from the top most element. This seems like a bit of a hack, but it would probably work well.
Example code:
private void RegisterTopMostParentMouseClickEvent()
{
_topMostParent = this.FindLastVisualAncestor<FrameworkElement>();
if ( _topMostParent == null )
return;
_topMostParent.AddHandler( Mouse.MouseDownEvent, new MouseButtonEventHandler( CustomControlMouseDownEvent ), true );
}
private void UnRegisterTopMostParentMouseClickEvent()
{
if ( _topMostParent == null )
return;
_topMostParent.RemoveHandler( Mouse.MouseDownEvent, new MouseButtonEventHandler( CustomControlMouseDownEvent ) );
_topMostParent = null;
}
Use Mouse.PreviewMouseDownOutsideCapturedElement and add a handler to my control. In the handler I'd kick the control out of it's editing state. But I don't seem to get the event to fire. When does the Mouse.PreviewMouseDownOutsideCapturedElement get kicked off?
Example code:
AddHandler( Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler( EditableTextBlockPreviewMouseDownOutsideCapturedElementEvent ), true );
Just to clarify the answer provided about mouse focus - it was useful but I had to do some further digging + mucking about to get something that actually worked:
I was trying to implement something like a combobox and needed similar behaviour - to get the drop down to disapear when clicking on something else, without the control having knowledge of what something else was.
I had the following event for a drop down button:
private void ClickButton(object sender, RoutedEventArgs routedEventArgs)
{
//do stuff (eg activate drop down)
Mouse.Capture(this, CaptureMode.SubTree);
AddHandler();
}
The CaptureMode.SubTree means you only get events that are outside the control and any mouse activity in the control is passed through to things as normal. You dont have the option to provide this Enum in UIElement's CaptureMouse, this means you will get calls to HandleClickOutsideOfControl INSTEAD of calls to any child controls or other handlers within the control. This is the case even if you dont subscribe to the events they are using - full Mouse capture is a bit too much!
private void AddHandler()
{
AddHandler(Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler(HandleClickOutsideOfControl), true);
}
You would also need to hang on to + remove the handler at the appropriate points but I've left that out here for the sake of clarity/brevity.
Finally in the handler you need to release the capture again.
private void HandleClickOutsideOfControl(object sender, MouseButtonEventArgs e)
{
//do stuff (eg close drop down)
ReleaseMouseCapture();
}
Capture the mouse.
When an object captures the mouse, all mouse related events are treated as if the object with mouse capture perform the event, even if the mouse pointer is over another object.
I usually get the parent window and add a preview handler, even if already handled. Sometimes when MouseCapture is not enough, this technique comes handy:
Window.GetWindow(this).AddHandler
(
UIElement.MouseDownEvent,
(MouseButtonEventHandler)TextBox_PreviewMouseDown,
true
);
I would approach this a different way - have the form that contains the control remove focus from the control when a user clicks on another part of the form.
Having the control actually loose focus is far cleaner than attempting to have the control "simulate" focus being lost in certain situations, when in fact it hasn't. Bear in mind that unless the control has really lost focus it will still accept things like keyboard input.
I'm making a calendar From in C# using WinForms.
I've put it together using a two-dimensional array of Panels, and inside them I have a List<> of custom controls which represent appointments.
The user needs to be able to drag appointment controls from one Panel to another (from day to day).
The custom control has a MouseDown and MouseUp event, which passes a message up from the control to the Parent.Parent (custom control -> day panel -> calendar form) and calls public methods StartDragging() and StopDragging() respectively.
Inside these methods, I make a clone of the custom control and add it to the Form, and store it in a global variable in the form which is called DraggedControl.
The Form has an event handler for MouseMove which goes like this:
void Calendar_MouseMove(object sender, MouseEventArgs e)
{
if (DraggedControl == null)
return;
DraggedControl.Location = PointToClient(MousePosition);
Refresh();
}
There are two problems, however:
First of all, the custom control is under everything else. I can see it being added and removed on MouseDown and MouseUp, but it is being added at 0,0 under the panels and day Labels.
Secondly, it does not appear to be moving with the MouseMove at all. I have a feeling this might be because I am moving the mouse with the button pressed, and this would represent a drag action rather than a basic MouseMove.
If I remove the MouseUp code, the control does drag with the mouse, however as soon as the mouse enters the panels (which the control is, sadly, underneath), the drag action stops.
What would you suggest I do?
I suspect there is probably a better way to do what I am trying to do.
custom control is under everything
else
bring it on top:
DraggedControl.BringToFront();
it does not appear to be moving with
the MouseMove at all
Control, which handled MouseDown event, captures mouse input and receives all following MouseMove events until it releases mouse input on MouseUp event, that's why Calendar_MouseMove() is not called. Handle MouseMove event for the same control, which generated MouseDown event.
I have a list of controls that I am displaying via a WrapPanel and it horizontally oriented.
I have implemented a "Click and Drag" scrolling technique so that the user scrolls with the mouse via clicking and dragging.
Like so:
<Canvas x:Name="ParentCanvas" PreviewMouseDown="Canvas_MouseDown" MouseMove="Canvas_MouseMove">
<WrapPanel>
<WrapPanel.RenderTransform>
<TranslateTransform />
</WrapPanel.RenderTransform>
<!-- controls are all in here ... -->
</WrapPanel>
</Canvas>
Then in the code behind:
private Point _mousePosition;
private Point _lastMousePosition;
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
_lastMousePosition = e.GetPosition(ParentCanvas);
e.Handled = true;
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
_mousePosition = e.GetPosition(ParentCanvas);
var delta = _mousePosition - _lastMousePosition;
if(e.LeftButton == MouseButtonState.Pressed && delta.X != 0)
{
var transform = ((TranslateTransform)_wrapPanel.RenderTransform).Clone();
transform.X += delta.X;
_wrapPanel.RenderTransform = transform;
_lastMousePosition = _mousePosition;
}
}
This all works fine
But what I want to do is make it so that when a users clicks to drag, the items within the WrapPanel dont respond (i.e. the user is only browsing), but when the user clicks (as in a full click) then they do respond to the click.
Just like how the iphone works, when you press and drag directly on an app, it does not open the app, but rather scrolls the screen, but when you tap the app, it starts...
I hope this makes sense.
Cheers,
Mark
I believe you'll need to capture the mouse. The problem is you'll be contending with the controls (such as Button) that will also be trying to capture the mouse.
In your MouseDown event (probably PreviewMouseDown actually) you can use e.MouseDevice.Capture(_wrapPanel, CaptureMode.Element). This should direct all mouse input to the _wrapPanel and not any subtree elements.
In your MouseUp event, you'll need to release the capture by calling e.Mousedevice.Capture(null). If no scrolling has taken place you'll want to send a "click" to the control that normally would have received the click which I'm not quite sure about. Perhaps you can use the Automation Peer classes to do this?
The trick is that certain controls will not work properly if you withhold mouse events from them. Consider a slider for example. How would the slider ever be usable inside a panel that works like this?
Another, and in my opinion better, solution is to:
Add a PreviewMouseDown handler in which you set Handled=true and record the parameters including the position and set a "maybeClick" flag (unless your "recursion" flag is set), and sets a timer.
Add a MouseMove handler that clears the "maybeClick" flag if the mouse moves more than an epsilon away from the position recorded for the PreviewMouseDown.
Add a PreviewMouseUp handler that checks the "maybeClick" flag - if true, it sets the "recursion" flag, does an InputManager.ProcessInput to re-raise the original PreviewMouseDown, and then clears the "recursion" flag.
In the timer, do the same thing as for PreviewMouseUp so the click will only be delayed a moment.
The net effect is to delay a PreviewMouseDown event until you have had time to check whether the mouse moved or not.
Some things to note about this solution:
Setting Handled=true on PreviewMouseDownEvent also stops the MouseDownEvent.
The recursive call is ignored in the PreviewMouseDown handler because the recursion flag is set