Windows Forms unusual mouse manipulation - c#

I have a Windows Form in which I'm rendering a map that I can zoom in and out of. I have it set so that if you hold the left mouse button, it pans across the map, and if you use the mouse wheel, it zooms in and out on the spot where the mouse cursor currently points. But some of my user base uses Mac hardware, and may not have a mouse with a wheel. I want to be able to hold the right mouse button and move the mouse forward and backward to zoom in and out, but to do this, I need to lock the cursor in place while the right mouse button is clicked. Any thoughts on how to do this?
I've tried Cursor.Position = Point(...) but that doesn't work immediately and causes some bizarre behavior.

I recommend you to Hide the cursor on MouseDown, then Show it in MouseUp. Till then you can draw the cursor manually if you want to show it.
private Point? downPoint;
protected override void OnMouseDown(MouseEventArgs e)
{
downPoint = this.PointToClient(MousePosition);
Cursor.Hide();
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (downPoint.HasValue)
{
Cursor.Show();
}
downPoint = null;
base.OnMouseUp(e);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (downPoint.HasValue)
{
Cursor.Draw(e.Graphics, new Rectangle(downPoint.Value, Cursor.Size));
}
}

I don't know why setting the Cursor.Position method doesn't work in your case. I have taken a simple windows form without placing any controls I have overided following methods and set the Cursor.Position property OnMouseMove method. Its working.
Just paste the following code in your form and run it.
Point p = Point.Empty;
protected override void OnMouseDown(MouseEventArgs e)
{
p = this.PointToScreen(e.Location);
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
p = Point.Empty;
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (p != Point.Empty)
Cursor.Position = p;
base.OnMouseMove(e);
}

Related

How to detect a full mouse click (both down and up) on a control?

In WPF with MVVM (and no code-behind), say I have a Rectangle. If the user clicks on the rectangle, I want to perform an action. I effectively want the action to occur on mouse up. However, I only want the action triggered if they performed a full click (i.e. both mouse down and mouse up) on the rectangle. If they clicked down over some other control, held the mouse down, moved it over the rectangle, and released the mouse, I do not want to trigger the action.
There is the MouseDown event and the MouseUp event, but what I effectively want is a "MouseClick" event (like in WinForms). Is there any built-in mouse click event/trigger/something functionality? If not, what would be the best way to approach this? Custom trigger? Attached behavior?
Well, it isn't perfect, but I created a custom trigger to solve the problem. I'm still open to any better solutions if anyone has one.
public class MouseClickTrigger : TriggerBase<UIElement>
{
private bool _isMouseDown;
protected override void OnAttached()
{
this.AssociatedObject.MouseDown += this.AssociatedObject_MouseDown;
this.AssociatedObject.MouseUp += this.AssociatedObject_MouseUp;
this.AssociatedObject.MouseLeave += this.AssociatedObject_MouseLeave;
}
protected override void OnDetaching()
{
this.AssociatedObject.MouseDown -= this.AssociatedObject_MouseDown;
this.AssociatedObject.MouseUp -= this.AssociatedObject_MouseUp;
this.AssociatedObject.MouseLeave -= this.AssociatedObject_MouseLeave;
}
private void AssociatedObject_MouseDown(object sender, MouseButtonEventArgs e)
{
this._isMouseDown = true;
}
private void AssociatedObject_MouseUp(object sender, MouseButtonEventArgs e)
{
bool fullClick = this._isMouseDown;
this._isMouseDown = false;
if (fullClick)
{
this.InvokeActions(e);
}
}
private void AssociatedObject_MouseLeave(object sender, MouseEventArgs e)
{
this._isMouseDown = false;
}
}
Whaaaaat??? I dont your problem... Why cant you do something like this:
private bool isMouseDownInRect = false;
<Rectangle Width="100" Height="100" MouseUp="UIElement_OnMouseUp" MouseDown="UIElement_OnMouseDown" Fill="Black"></Rectangle>
private void UIElement_OnMouseUp(object sender, MouseButtonEventArgs e)
{
if (isMouseDownInRect)
{
// My code
}
isMouseDownInRect = false;
}
private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
isMouseDownInRect = true;
}
I hope this is what you asking for.
EDIT:
Dont forget to handle the mouse_enter/leave event...

How can I make a window act like a Context Menu?

I am trying to build a total new window acts as Context Menu.
the only problem i have is: when I am pressing the mouse buttons outside the window (ContextMenu), the window does not close. I can't find the event that can catch this action.
this is the code i am using now:
public partial class ContextMenu : Window
{
public ContextMenu()
{
InitializeComponent();
this.ShowInTaskbar = false;
this.Deactivated += new EventHandler(ContextMenu_Deactivated);
}
void ContextMenu_Deactivated(object sender, EventArgs e)
{
this.Hide();
}
protected override void OnDeactivated(EventArgs e)
{
base.OnDeactivated(e);
this.Hide();
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
this.Hide();
}
protected override void OnKeyDown(System.Windows.Input.KeyEventArgs e)
{
base.OnKeyDown(e);
this.Hide();
}
protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
this.Hide();
}
}
non of the functions above catches the mouse press outside the window (ContextMenu).
I have tried to use http://www.hardcodet.net/taskbar, but the examples I found are not something like what i am looking for.
Looks like you need processing of global mouse hooks.
Here is nice solution to this issue
http://www.codeproject.com/Articles/7294/Processing-Global-Mouse-and-Keyboard-Hooks-in-C
A control cannot detect mouse clicks that are outside their bounding Rectangles. However, the Window can detect a mouse click anywhere within its border. Therefore, all you need to do is to handle a PreviewMouseDown event in the MainWindow.xaml.cs file and then pass a message to the relevant control each time the event is raised.
I believe you'll want to use Mouse.Capture to detect a click away from your window.
This question+answer may lead you in the right direction:
How do I use CaptureMouse or Mouse.Capture in my C# WPF application?

how to create multi selection

On a form I have multiple usercontrols which are created dynamically at every button click. I want an user to be able to select them in order to copy delete,etc. Like we select with mouse, icons and then delete them. To do this,I created another usercontrol,that is created at mouse position. I don't know how can I draw that usercontrol. My code untill now:
//method that creates usercontrols
private void _butttnAddControls_Click(object sender, EventArgs e)
{
TControl tcontrol = new TControl();
tcontrol.BringToFront();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
SelectPanel pselect = new SelectPanel();//pselect is the control used to create the rectangle for selection
pselect.Visible = true;
Point p = PointToClient(Cursor.Position);
pselect.Location = p;
pselect.SelectionPanel = true;
this.Controls.Add(pselect);
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
pselect.Visible = false;
}
If you are using WinForms you can use the Control's DrawToBitmap() method to get an image of your usercontrol youv'e created dynamically.
Check this link for more information Control.DrawToBitmap Method
Then you can draw your all control array into a picture box, and use you Mouse Events there.

Disable focus cues on a SplitContainer

How can I disable the focus cues on a SplitContainer?
I ask because I'd rather draw them myself using OnPaint in order to make it look somewhat smoother.
I tried this:
protected override bool ShowFocusCues
{
get
{
return false;
}
}
And this is my control:
public class cSplitContainer : SplitContainer
{
private bool IsDragging;
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (!IsSplitterFixed) IsDragging = true;
Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (IsDragging)
{
IsDragging = false;
IsSplitterFixed = false;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (IsDragging)
{
IsSplitterFixed = true;
if (e.Button == MouseButtons.Left)
{
if (Orientation == Orientation.Vertical)
{
if (e.X > 0 && e.X < Width) SplitterDistance = e.X;
}
else
{
if (e.Y > 0 && e.Y < Height) SplitterDistance = e.Y;
}
}
else
{
IsDragging = false;
IsSplitterFixed = false;
}
}
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
base.OnPaint(e);
if (IsDragging)
{
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(127, 0, 0, 0)), Orientation == Orientation.Horizontal ? new Rectangle(0, SplitterDistance, Width, SplitterWidth) : new Rectangle(SplitterDistance, 0, SplitterWidth, Height));
}
}
}
but it didn't work. I also tried some other methods mentioned before, but I'm still getting focus cues.
I don't think what you are seeing is the FocusCue so much as a floating window that is used to move the slider.
If keyboard access isn't important, you can try making it unselectable:
public class MySplit : SplitContainer {
public MySplit() {
this.SetStyle(ControlStyles.Selectable, false);
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.Clear(Color.Red);
}
}
This prevents the SplitContainer from getting focus, but your mouse can still interact with it.
The code of SplitContainer is like:
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
if (Focused) {
DrawFocus(e.Graphics,SplitterRectangle);
}
}
DrawFocus is not virtual. So you can't override it.
Focused is virtual. Maybe you can set it to false while calling base.OnPaint(...) in your OnPaint override.
So you could add following code (I did not tested if it works):
private bool _painting;
public override bool Focused
{
get { return _painting ? false : base.Focused; }
}
protected override void OnPaint(PaintEventArgs e)
{
_painting = true;
try
{
base.OnPaint(e);
}
finally
{
_painting = false;
}
}
That is more a hack than a clean solution.
I was googling for this issue and this question came up on the top.
There is a solution and interesting discussion on a Microsoft forum regarding the splitter stealing focus for no good reason. The following comment is spot on:
The focus issue you mentioned is by design, however to get the performance you want, you can use the following workaround: ....
It may be "by design", but it is not a very good one. What spitters have you ever seen in any Microsoft production application that even temporarily take the focus from the panes they split? I also added the code you suggest, and it does keep me from permanently losing the focus to the splitter, but I still don't like the fact that my panes hide and show their selections during splitter manipulation.
This distracting selection flash just is not present in most professional applications. It is just good enough that it probably won't be worth my time to fix for a while, but not what most people really want. If you respected the TabStop property or even added a AcceptsFocus property, most people would want this off. I think you should add this option to the design in a future version.
--Brendan
Simple solution: give away focus immediately when receiving it!
Three steps:
Create a GotFocus handler for the SplitContainer
Forward the focus to another control with AnotherControl.Focus().
Set TabStop to False
That's all. The ugly focus cue is never shown.
Now, one subtlety: Which other control to give the focus to? It's up to you. Just take the first control by tab order, or a upper-left focusable control in the right pane of the SplitContainer (TextBox in the ASCII diagram below). The perfect solution would be the previous control which had focus, but sadly this is not easy to find out: Find out the control with last focus, but IMHO the upper-left focusable control is a very good response.
left pane right pane
------------------------------------------------
: :: :
: :: [TextBox] [Button] :
: :: :
: :: [Combobox V] :
: :: :
------------------------------------------------
it is a kind a similar question being asked on stackoveflow one solution is sugested as being used by you also along with overriding showfocuscues property you need to override paint method as well.

Mouse events not fired

I'm making a C# WinForms application. The MouseMove and MouseClick events of the form aren't getting fired for some reason. (I'm probably going to feel like an idiot when I find out why.)
It is a transparent form (TransparencyKey is set to the background colour) with a semi-transparent animated gif in a Picture Box. I am making a screensaver.
Any suggestions?
EDIT:
MainScreensaver.cs
Random randGen = new Random();
public MainScreensaver(Rectangle bounds)
{
InitializeComponent();
this.Bounds = Bounds;
}
private void timer1_Tick(object sender, EventArgs e)
{
Rectangle screen = Screen.PrimaryScreen.Bounds;
Point position = new Point(randGen.Next(0,screen.Width-this.Width)+screen.Left,randGen.Next(0,screen.Height-this.Height)+screen.Top);
this.Location = position;
}
private void MainScreensaver_Load(object sender, EventArgs e)
{
Cursor.Hide();
TopMost = true;
}
private Point mouseLocation;
private void MainScreensaver_MouseMove(object sender, MouseEventArgs e)
{
if (!mouseLocation.IsEmpty)
{
// Terminate if mouse is moved a significant distance
if (Math.Abs(mouseLocation.X - e.X) > 5 ||
Math.Abs(mouseLocation.Y - e.Y) > 5)
Application.Exit();
}
// Update current mouse location
mouseLocation = e.Location;
}
private void MainScreensaver_KeyPress(object sender, KeyPressEventArgs e)
{
Application.Exit();
}
private void MainScreensaver_Deactive(object sender, EventArgs e)
{
Application.Exit();
}
private void MainScreensaver_MouseClick(object sender, MouseEventArgs e)
{
Application.Exit();
}
Excerpt from MainScreensaver.Designer.cs InitialiseComponent()
this.MouseClick += new System.Windows.Forms.MouseEventHandler(this.MainScreensaver_MouseClick);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.MainScreensaver_MouseMove);
This isn't an answer to you question, but I'm leaving this answer in case anyone else stumbles upon this question while trying to debug this same issue (which is how I got here)
In my case, I had a class that was derived from Form.
I was also using TransparencyKey.
Some things I noticed
The events will not fire on the transparent parts of the form.
The events will not fire if the mouse cursor is over another control on the form.
The events will not fire if you override WndProc and set the result of a WM_NCHITTEST message. Windows doesn't even send out the corresponding mouse messages that would cause the .NET events.
My Solution
In my constructor, I had forgotten to call InitializeComponent().
Which was where the event handlers were being bound to my controls.
Events were not firing because the handlers were not being bound.
Are you sure that your form has focus? If your form does not have focus, the mouse events will not be fired.

Categories