WPF intercept clicks outside a modal window - c#

Is it possible to check when the user has clicked outside a modal window? I'd like to somehow circumvent the modal logic because if the window isn't displayed as modal, it will not be shown on top of the active window, and, for now, this is the only way to display it correctly. I haven't found a proper way to do just that (since the "deactivate" event will no longer be triggered..)

Even if it's a modal window (displayed with ShowDialog() calls), one can add some even handlers to the window's class and make it check for the mouse clicks outside the window like this:
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (posX < 0 || posX > this.Width || posY < 0 || posY > this.Height)
this.Close();
}
private void Window_MouseMove(object sender, MouseEventArgs e)
{
Point p = e.GetPosition(this);
posX = p.X; // private double posX is a class member
posY = p.Y; // private double posY is a class member
}
private void Window_Activated(object sender, EventArgs e)
{
System.Windows.Input.Mouse.Capture(this, System.Windows.Input.CaptureMode.SubTree);
}
This did the job for me, in a difficult context: mingled MFC, WindowsForms mammoth of an app - no interop, no other complicated stuff. Hope it helps others facing this odd behavior.

Well one way is to hook up the event handler on your main app and respond to it when you have that window open:
EventManager.RegisterClassHandler(typeof(Window), Mouse.MouseDownEvent, new MouseButtonEventHandler(OnMousepDown), true);
or
EventManager.RegisterClassHandler(typeof(yourAppClassName), Mouse.PreviewMouseDownEvent, new MouseButtonEventHandler(OnMousepDown), true);
//this is just a sample..
private void OnMousepDown(object sender, MouseButtonEventArgs e)
{
if (thatWindowThatYourTalkingAbout.IsOpen)
..do something
}

Related

WinForm Update during dragging using ReleaseCapture

I want to update some graphical rendering based on the form's position as I drag the form with the mouse.
Since the form is border-less, I am using ReleaseCapture and SendMessage to move it.
However, I am not able to do my calls during the dragging operation.
I suppose it is possible to spawn a thread that runs until SendMessage returns a value, however I wanted to ask if there is a native method for this particular problem?
public void MoveWindow()
{
ReleaseCapture();
SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
....
}
Best regards
You can use events of form which raise at start, during and at the end of movement:
Use Move event of form to do something during form movement.
Use ResizeBegin event of the form to do a job at start of movement.
Use ResizeEnd event of the form to do a job at the end of movement.
Example
private void Form1_Move(object sender, EventArgs e)
{
this.BackColor = this.Left/10 % 2 == 0 ? Color.Red : Color.Blue;
}
private void Form1_ResizeBegin(object sender, EventArgs e)
{
this.Text = this.Location.ToString();
}
private void Form1_ResizeEnd(object sender, EventArgs e)
{
this.Text = this.Location.ToString();
}

Proper way of activating my MainForm from a NotifyIcon if it wasn't focused already

I just want to replace the task bar button of my winforms application by a tray notification icon. That means, if the user left-clicks the icon, the form should be activated if it wasn't focused, otherwise minimized or hidden.
I read lots and lots of articles about properly using a NotifyIcon, and it seems I have to accept a hackish solution. So, what's the most proper way?
I got it mostly to run, but now I'm stuck at detecting if my form was active already - because when clicking the icon, the form looses focus, so I cannot check the Focused property.
The following code does not yet solve this, so if the form was just hidden by other windows, you have to click 2 times, because the first click minimizes.
How can it be improved?
private void FormMain_Resize(object sender, EventArgs e)
{
if (WindowState == FormWindowState.Minimized)
Hide();
}
private void notifyIcon_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
if (WindowState == FormWindowState.Minimized) {
WindowState = FormWindowState.Normal;
Show();
Activate();
}
else {
Hide();
WindowState = FormWindowState.Minimized;
}
}
(I also don't understand why the Click event fires on right-clicking, which already opens the context menu in my case...)
(And of course it would be nice to have a proper minimize animation, but there are other questions here, where this wasn't really solved)
(I know I said Focused, but if the form was already fully visible (but maybe not focused), and the user clicks the tray icon, he most likely wants to hide it)
#LarsTech suggested a hack using a timer, and this works, thanks, but I still hope for better solutions or improvements.
Maybe interesting: I also hack-solved part of the animation problem - when there is no taskbar button, the animation originates from where the minimized form is located. You can make it visible by calling Show() after minimizing. It sits in the lower left of the screen and it is not possible to move it by setting i.e. the Left property. So I used winapi directly and it works!
Unfortunately only for the restore animation, because I don't know how to set the minimized position before minimizing. Hide() disables the animation anyway, so I would have to delay it...
That all is so disappointing! Why is there no nice solution? I can never be sure if it will work in every scenario. For example on one machine it worked well with a 100ms timer, but on another I needed 200ms. So I suggest to have a least 500ms.
[DllImport("user32.dll", SetLastError = true)]
static extern bool MoveWindow(IntPtr hWnd,
int X, int Y, int nWidth, int nHeight, bool bRepaint);
private void FormMain_Resize(object sender, EventArgs e)
{
if (!ShowInTaskbar && WindowState == FormWindowState.Minimized) {
Hide();
//Move the invisible minimized window near the tray notification area
if (!MoveWindow(Handle, Screen.PrimaryScreen.WorkingArea.Left
+ Screen.PrimaryScreen.WorkingArea.Width - Width - 100,
Top, Width, Height, false))
throw new Win32Exception();
}
}
private void notifyIcon_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
if (WindowState == FormWindowState.Minimized || !timer.Enabled) {
Show();
WindowState = FormWindowState.Normal;
Activate();
}
else {
WindowState = FormWindowState.Minimized;
//FormMain_Resize will be called after this
}
}
private void FormMain_Deactivate(object sender, EventArgs e)
{
timer.Start();
}
private void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
}
//other free goodies not mentioned before...
private void taskbarToolStripMenuItem_Click(object sender, EventArgs e)
{
ShowInTaskbar = !ShowInTaskbar;
taskbarToolStripMenuItem.Checked = ShowInTaskbar;
}
private void priorityToolStripMenuItem_Click(object sender, EventArgs e)
{
//Set the process priority from ToolStripMenuItem.Tag
//Normal = 32, Idle = 64, High = 128, BelowNormal = 16384, AboveNormal = 32768
Process.GetCurrentProcess().PriorityClass
= (ProcessPriorityClass)int.Parse((sender as ToolStripMenuItem).Tag.ToString());
foreach (ToolStripMenuItem item in contextMenuStrip.Items)
if (item.Tag != null)
item.Checked = item.Equals(sender);
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Close();
}

Move a window with custom borders using the mouse

I'm working on a C# project using WPF. In my MainWindow I just got rid of the default windows border and made an own border instead with a png file. I just set this as the background of the MainWindow.
Now I made some kinda tricky way to make my window move:
private void Window_MouseDown(object sender, MouseButtonEventArgs e) {
mouseDown = true;
lastX = (int)e.GetPosition(this).X;
lastY = (int)e.GetPosition(this).Y;
}
private void Window_MouseUp(object sender, MouseButtonEventArgs e) {
mouseDown = false;
}
private void Window_MouseMove(object sender, MouseEventArgs e) {
if (mouseDown) {
int xDiff = (int)e.GetPosition(this).X - lastX;
int yDiff = (int)e.GetPosition(this).Y - lastY;
this.Left += xDiff;
this.Top += yDiff;
}
}
private void Window_MouseLeave(object sender, MouseEventArgs e) {
mouseDown = false;
}
Now this doesn't really work properly. This way the user can move the window at all clear spaces, even on Labels and TextBlocks. Is there a way to give the background or a border these kind of events? Or is there a better way to integrate borders?
Thanks in advance!
As you said, behavior is erratic when done manually but there's a fix for it.
This is the method in the framework that is specially for doing that :
http://msdn.microsoft.com/en-us/library/system.windows.window.dragmove(v=vs.110).aspx

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.

Listbox events firing strangely

I'm confused. I am basically trying to tell when the user has clicked something in the listbox, held the button, and left the listbox. Here is a somewhat dumbed down version of what I am doing:
private bool itemHeld;
private void listOriginal_MouseDown(object sender, MouseEventArgs e)
{
itemHeld = true;
}
private void listOriginal_MouseUp(object sender, MouseEventArgs e)
{
itemHeld = false;
}
private void listOriginal_MouseLeave(object sender, EventArgs e)
{
if (itemHeld)
MessageBox.Show("OHH YEAH");
}
To me that seems like it should turn itemHeld true when you press the mousebutton, turn it false only if you lift it, and display ohh yeah if the value is true. If I break on the mouse down event to check the value, it is true and if I continue from there it displays the message. If I do not break, it does nothing. Is there something else at work here?
Edit:
Brief description: It would be difficult to explain what I am really trying to accomplish but imagine something almost like dragging a file off of a window. I need to simply be able to recognize when the user clicks inside of the listbox and then drags out of the listbox if that makes sense
You can not debug windows events by break point because when the Visual Studio get active to debug, the mouse leave event will be fired for the hovered control.
You can use Debug.WriteLine which writes information about the debug to the trace listeners.
private void button1_MouseLeave(object sender, EventArgs e)
{
Debug.WriteLine("Mouse leave");
}
private void button1_MouseEnter(object sender, EventArgs e)
{
Debug.WriteLine("Mouse enter");
}
private void button1_MouseHover(object sender, EventArgs e)
{
Debug.WriteLine("Mouse hover");
}
what about this?
private void listBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.X > listBox1.Width - 1 || e.Y > listBox1.Height - 1 || e.X < 0 || e.Y < 0)
{
Console.WriteLine("drag out");
}
else
Console.WriteLine("mouse move {0}/{1}", e.X, e.Y);
}
it uses the fact that the Control is not left before the mousebutton is released ... but be aware that the drag out part will occur more than once so you probably will want to have a flag set the first time ... and have that flag cleared on mouse up or leave
For every mouse click, your MouseDown event will fire AND your MouseUp event will fire, so the sequence of operations is equivalent to
itemHeld = true;
itemHeld = false;
if(itemHeld)
MessageBox.Show("yay");
If you press the mouse button on the listbox and move the cursor out without releasing the button, switching focus to another window (e.g. Visual Studio) is what triggers the MouseLeave event to fire. This is why you're seeing the message box pop up when you're debugging.
I'm not sure what you're trying to accomplish, so I can't recommend another solution.

Categories