I'm having some issues getting a notification box to behave correctly in c#. Basically I'm showing a boarderless form in the lower right hand side of the screen, which displays a message for a few seconds and then disappears. The problem is that I need it to appear on top of other windows without it ever being able to steal focus. Ideally, I want it to be purely managed code, although looking through similar examples I doubt this will be possible.
At the moment I'm preventing it from stealing focus when calling Form.Show() with an override:
protected override bool ShowWithoutActivation // stops the window from stealing focus
{
get { return true; }
}
and then ignoring mouse clicks with:
private const int WM_MOUSEACTIVATE = 0x0021;
private const int MA_NOACTIVATEANDEAT = 0x0004;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEACTIVATE)
{
m.Result = (IntPtr)MA_NOACTIVATEANDEAT;
return;
}
base.WndProc(ref m);
}
However I find that if I use these in conjunction with TopMost = true (which I need), it gains focus anyway, and if all other windows are minimised, it also gains focus.
So, is there any way to flat out prevent a form from ever gaining focus (whether via mouse click, alt-tab, etc), while still being the top most/second top most form? Even just giving focus immediately back to the window it stole it from would work (although introduce flickering).
Any suggestions would be greatly appreciated, I'm really stuck with this.
EDIT:
Ok, so I finally managed to get this working using:
protected override bool ShowWithoutActivation // stops the window from stealing focus
{
get { return true; }
}
// and
const int WS_EX_NOACTIVATE = 0x08000000;
const int WS_EX_TOPMOST = 0x00000008;
protected override CreateParams CreateParams
{
get
{
CreateParams param = base.CreateParams;
param.ExStyle |= WS_EX_TOPMOST; // make the form topmost
param.ExStyle |= WS_EX_NOACTIVATE; // prevent the form from being activated
return param;
}
}
// and
[DllImport("user32.dll")]
private extern static IntPtr SetActiveWindow(IntPtr handle);
private const int WM_ACTIVATE = 6;
private const int WA_INACTIVE = 0;
private const int WM_MOUSEACTIVATE = 0x0021;
private const int MA_NOACTIVATEANDEAT = 0x0004;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEACTIVATE)
{
m.Result = (IntPtr)MA_NOACTIVATEANDEAT; // prevent the form from being clicked and gaining focus
return;
}
if (m.Msg == WM_ACTIVATE) // if a message gets through to activate the form somehow
{
if (((int)m.WParam & 0xFFFF) != WA_INACTIVE)
{
if (m.LParam != IntPtr.Zero)
{
SetActiveWindow(m.LParam);
}
else
{
// Could not find sender, just in-activate it.
SetActiveWindow(IntPtr.Zero);
}
}
}
I also added Form.Hide() to the GotFocus event so that even if it does somehow get focus, it simply closes and gets out of the users way asap.
Also, if anyone is wondering, the constants for all the window styles etc. can be found in WINUSER.H, its online at http://www.woodmann.com/fravia/sources/WINUSER.H if you can't find it.
However, if anyone can see a more elegant way of doing this, it would be appreciated.
Possibly WS_EX_NOACTIVATE extended window style is what you are looking for. Window with this style is not activated when clicked. For example, Virtual Keyboard window has this style.
To apply this style to window, override CreateParams function and change baseParams.ExStyle.
In WPF try this:
ShowActivated="False"
I'm not looking for points here as the original poster already posted a solution that worked for them but I wanted to share my experience with this problem. Using the solution above (which is at the bottom of the question instead of in answer form) gives me a Win32Exception: Error creating window handle error. when using the WndProc code as it is posted there. The ShowWithoutActivation and CreateParams part works great to prevent activation of a form and still keep it topmost.
I came up with an alternate solution to preventing a form from being clicked using SetWindowLong which makes the form transparent and therefore it can be clicked through and SetLayeredWindowAttributes which sets the transparency back to normal so you can see the form again but still retain the ability to click through the form.
NOTE: You -CANNOT- interact with the form at all in this state and even trying to click the 'X' button will just click whatever is behind the form at that position so you will need to use code to close the form:
public partial class Form1 : Form
{
private enum GWL : int
{
ExStyle = -20
}
private enum WS_EX : int
{
Transparent = 0x20,
Layered = 0x80000
}
public enum LWA : int
{
ColorKey = 0x1,
Alpha = 0x2
}
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
protected override bool ShowWithoutActivation
{
get { return true; }
}
const int WS_EX_NOACTIVATE = 0x08000000;
const int WS_EX_TOPMOST = 0x00000008;
protected override CreateParams CreateParams
{
get
{
CreateParams param = base.CreateParams;
param.ExStyle |= WS_EX_TOPMOST; // make the form topmost
param.ExStyle |= WS_EX_NOACTIVATE; // prevent the form from being activated
return param;
}
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Prevent form from showing up in taskbar which also prevents activation by Alt+Tab
this.ShowInTaskbar = false;
// Allow the form to be clicked through so that the message never physically interferes with work being done on the computer
SetWindowLong(this.Handle, (int)GWL.ExStyle, (int)WS_EX.Layered | (int)WS_EX.Transparent);
// Set the opacity of the form
byte nOpacity = 255; // 0 = invisible, 255 = solid, anything inbetween will show the form with transparency
SetLayeredWindowAttributes(this.Handle, 0, nOpacity, (uint)LWA.Alpha);
}
}
I was also able to get the original approach working by making a small change to the WndProc code above.
NOTE: This also makes the form unclickable but the behavior is different in that you can actually click the min, max and 'X' buttons but nothing happens when you do. The mouse cursor also changes when you are at the edge of the form as if to resize but it doesn't allow resizing:
public partial class Form1 : Form
{
protected override bool ShowWithoutActivation
{
get { return true; }
}
const int WS_EX_NOACTIVATE = 0x08000000;
const int WS_EX_TOPMOST = 0x00000008;
protected override CreateParams CreateParams
{
get
{
CreateParams param = base.CreateParams;
param.ExStyle |= WS_EX_TOPMOST; // make the form topmost
param.ExStyle |= WS_EX_NOACTIVATE; // prevent the form from being activated
return param;
}
}
[DllImport("user32.dll")]
private extern static IntPtr SetActiveWindow(IntPtr handle);
private const int WM_ACTIVATE = 6;
private const int WA_INACTIVE = 0;
private const int WM_MOUSEACTIVATE = 0x0021;
private const int MA_NOACTIVATEANDEAT = 0x0004;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEACTIVATE)
{
m.Result = (IntPtr)MA_NOACTIVATEANDEAT; // prevent the form from being clicked and gaining focus
return;
}
if (m.Msg == WM_ACTIVATE) // if a message gets through to activate the form somehow
{
if (((int)m.WParam & 0xFFFF) != WA_INACTIVE)
{
if (m.LParam != IntPtr.Zero)
{
SetActiveWindow(m.LParam);
}
else
{
// Could not find sender, just in-activate it.
SetActiveWindow(IntPtr.Zero);
}
}
}
else
{
base.WndProc(ref m);
}
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Prevent form from showing up in taskbar which also prevents activation by Alt+Tab
this.ShowInTaskbar = false;
}
}
Related
I have built upon a mouse movement I found, but I don't want the program to automatically click on the things, but instead, to show me where to click. How do I do this?
(Newbie, started C# and C++ some four months ago!)
edit:
To clarify, the program is meant to click on another window, not into itself (a macro of sorts, that is why I want an option to just show, so the user can see what the program was made to do!)
Assuming you're using WinForms, here's a quick example. The "target" in this case happens to be in the same form, but you can pass in an hWnd to an external window and it should work the same. You need to keep a reference to the overlay so you can close it later; this is what the second button does. Obviously you'll have to do this differently in your application, but it's a start:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
OverlayForm overlay = null;
private void button1_Click(object sender, EventArgs e)
{
if (overlay == null)
{
overlay = new OverlayForm(button2.Handle); // <-- pass in an hWnd to some external window
overlay.Show();
}
}
private void button2_Click(object sender, EventArgs e)
{
if (overlay != null)
{
overlay.Close();
overlay = null;
}
}
}
public class OverlayForm : Form
{
private IntPtr ExternalhWnd;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
public OverlayForm(IntPtr ExternalhWnd)
{
this.ExternalhWnd = ExternalhWnd;
this.TopMost = true;
this.ShowInTaskbar = false;
this.FormBorderStyle = FormBorderStyle.None;
this.StartPosition = FormStartPosition.Manual;
this.Opacity = 0.7; // 70% opacity
this.BackColor = Color.Yellow;
}
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= 0x00000020; // WS_EX_TRANSPARENT - Ignore Mouse Clicks
createParams.ExStyle |= 0x80; // WS_EX_TOOLWINDOW - Remove from Alt-Tab List
return createParams;
}
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
if (!this.ExternalhWnd.Equals(IntPtr.Zero))
{
// position our overlay on top of the external window
RECT rct;
GetWindowRect(this.ExternalhWnd, out rct);
this.Location = new Point(rct.Left, rct.Top);
this.Size = new Size(rct.Right - rct.Left, rct.Bottom - rct.Top);
}
}
}
Here's a picture of the form before and after button1 is clicked:
Button2 still reacts to user input and clicks when the yellow overlay is present.
Do ask as many questions as you like to understand the code and implement it in your project. I'll do my best to help you out...
I have a WindowsForm MDI application with a textbox where text stream coming from a serial port is displayed.
This text box by default auto-scroll to the end of the textbox each time I append text to it.
But I added an option to stop autoscroll, here is the code:
namespace MyNs
{
public class MyForm
{
void updateTextbox_timerTick(object sender, EventArgs e)
{
int cursorPos = textbox.SelectionStart;
int selectSize = textbox.SelectionLength;
if(!autoscroll)
textbox.SuspendDrawing();
lock(bufferLock)
{
textbox.AppendText(buffer.ToString());
buffer.Clear();
}
if(autoscroll)
{
textbox.Select(textbox.Text.Length, 0);
textbox.ScrollToCaret();
}
else
{
textbox.Select(cursorPos, selectSize);
textbox.ResumeDrawing();
}
}
}
public static class Utils
{
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);
private const int WM_SETREDRAW = 11;
public static void SuspendDrawing(this Control parent)
{
SendMessage(parent.Handle, WM_SETREDRAW, false, 0);
}
public static void ResumeDrawing(this Control parent)
{
SendMessage(parent.Handle, WM_SETREDRAW, true, 0);
}
}
}
Using my method ResumeDrawing and SuspendDrawing let the textbox stay at his position while appending. But this also add flickering problems. Do you know how I can resolve these problems ?
Thanks for you help :)
Thanks to this question : Flicker free TextBox
The solution is to enable WS_EX_COMPOSITED params on the form or the control.
To do so you just have to add this to your form or derived control class:
private const int WS_EX_COMPOSITED = 0x02000000;
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_COMPOSITED;
return cp;
}
}
Don't forget to also enable DoubleBuffering.
I'm attempting to create a custom AutoCompleteTextBox control in WinForms. The AutoComplete provided by the basic TextBox provides results only with (string).StartsWith as opposed to (string).Contains.
I am displaying the Auto-Complete search results in a ListBox docked in a separate Form. I can prevent the Form from stealing focus initially using via:
protected override bool ShowWithoutActivation
{
get { return true; }
}
Then, I can prevent the Form from gaining focus entirely by overriding the WndProc method via:
protected override void WndProc( ref Message m )
{
base.WndProc( ref m );
switch ( m.Msg )
{
case WM_MOUSEACTIVATE:
m.Result = ( IntPtr ) MA_NOACTIVATEANDEAT;
break;
default:
break;
}
When I do this, the ListBox contained in the Form will receive MouseMoved events, but not MouseClicked events.
Chaning MA_NOACTIVATEANDEAT to just MA_NOACTIVATE will pass the mouse events to the ListBox, but then will cause the clicks on the ListBox to steal focus from the Form the ListBox resides in - passing it to the 'floating' Form the ListBox is in.
Is there any way I can prevent the 'floating' Form from steeling focus from the 'main' Form while still getting MouseClick events within the ListBox?
Convert your floating form into a Usercontrol as below :-
Pull a ListBox on it to & hook onto Click event to simulate your scenario of Focus.
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication3
{
public partial class InactiveForm : UserControl
{
private const int WS_EX_TOOLWINDOW = 0x00000080;
private const int WS_EX_NOACTIVATE = 0x08000000;
private const int WS_EX_TOPMOST = 0x00000008;
[DllImport("user32")]
public static extern int SetParent
(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32")]
public static extern int ShowWindow
(IntPtr hWnd, int nCmdShow);
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
p.ExStyle |= (WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST);
p.Parent = IntPtr.Zero;
return p;
}
}
public InactiveForm()
{
InitializeComponent();
}
public new void Show()
{
if (this.Handle == IntPtr.Zero) base.CreateControl();
SetParent(base.Handle, IntPtr.Zero);
ShowWindow(base.Handle, 1);
}
private void OnListBoxClicked(object sender, EventArgs e)
{
MessageBox.Show("Clicked List Box on floating control");
}
}
}
Code in the MainForm (with a button and its click handler attached):-
This invokes the floating control with ListBox.
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
InactiveForm f = new InactiveForm();
f.Show();
}
}
}
When you show the UserControl (borderless form) as your floating form, you will notice that it wont receive focus even if you clicked (selected) any of its children. In this case, the child is a ListBox on the usercontrol.
Refer here and here.
In addtion to previous answer by Angshuman Agarwal:
If you want the main form does not deactive when the UserControl show as floating form, modify some code to your UserControl:
private Control _mControl;
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
public new void Show(Control c)
{
if (c == null) throw new ArgumentNullException();
_mControl = c;
if (this.Handle == IntPtr.Zero) base.CreateControl();
SetParent(base.Handle, IntPtr.Zero);
ShowWindow(base.Handle, 1);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x86) //WM_NCACTIVATE
{
if (m.WParam != IntPtr.Zero) //activate
{
SendMessage(_mControl.Handle, 0x86, (IntPtr)1, IntPtr.Zero);
}
this.DefWndProc(ref m);
return;
}
base.WndProc(ref m);
}
I've created something similiar and my solution is similar to what lAbstract suggests. The floatin form has an event DoReturnFocus wich the custom TextBox (the AutoCompleteTextBox control in this case) subscribes to, and simply sets focus back to itself. To prevent the main form to appear in front of the floating form I set the main form as the Owner of the floating form, as owned forms are never displayed behind their owner form..
More on Form.Owner Property
In my application I really needed to place a lot of controls (label, textbox, domainupdown) in a nice order. So I went ahead and used some nested TableLayoutPanel. The problem now is, this form responds very slow to most of events (resize, maximize, minimize and ...) it takes really up to 5 seconds for controls within the tables to get resized, redrawed to the new size of form.
I am putting a finger in my eye now! If this form is that slow on my home PC (i7#4GHz and a good graphic card) what it will do tommorow on the old P4 computer at work?
I even tried to use the code below but it does absoloutly nothing, if it is not slowing it down more!
private void FilterForm_ResizeBegin(object sender, EventArgs e)
{
foreach(TableLayoutPanel tlp in panelFilters.Controls)
{
if(tlp != null)
{
tlp.SuspendLayout();
}
}
}
private void FilterForm_ResizeEnd(object sender, EventArgs e)
{
foreach (TableLayoutPanel tlp in panelFilters.Controls)
{
if (tlp != null)
{
tlp.ResumeLayout();
}
}
}
Please let me know if there is a trick to make tablelayoutpanel to work faster...or if you know a better approach to lay down about hundred of controls nicely aligned.
Use this code.
public class CoTableLayoutPanel : TableLayoutPanel
{
protected override void OnCreateControl()
{
base.OnCreateControl();
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.CacheText, true);
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= NativeMethods.WS_EX_COMPOSITED;
return cp;
}
}
public void BeginUpdate()
{
NativeMethods.SendMessage(this.Handle, NativeMethods.WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);
}
public void EndUpdate()
{
NativeMethods.SendMessage(this.Handle, NativeMethods.WM_SETREDRAW, new IntPtr(1), IntPtr.Zero);
Parent.Invalidate(true);
}
}
public static class NativeMethods
{
public static int WM_SETREDRAW = 0x000B; //uint WM_SETREDRAW
public static int WS_EX_COMPOSITED = 0x02000000;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); //UInt32 Msg
}
If you create a new class derived from TableLayoutPanel and set the ControlStyles such that DoubleBuffered is true, your performance will improve dramatically.
public class MyPanel : TableLayoutPanel
{
public MyPanel()
{
this.SetStyle(ControlStyles.DoubleBuffer, true);
}
}
I made a custom button with some panels and pictureboxes. With the MouseEnter and MouseLeave I set the appropriate hover images like normal buttons.
The problem is that if I move the mouse too fast over the control it sometimes doesn't trigger the MouseLeave event. This way the button is "locked" in hover state.
screenshot problem:
http://www.jesconsultancy.nl/images/screens/screen_prblm.png
the button at the right is locked in "hover" state.
How can i solve this?
Thanks.
Holy... That's a mess!
Firstly, UserControls are very buggy. I suggest you make your control inherit from Control instead and draw the image and text yourself.
Secondly, why are you using reflection?
Thirdly, why are there so many controls?
This misses the event because it takes too much to update!
Here is some code for the simples control possible, that will NEVER miss an event:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace lol
{
public class BlackWhiteControl : Control
{
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
this.BackColor = Color.Black;
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
this.BackColor = Color.White;
}
}
}
You should receive the WM_MOUSELEAVE windows message automatically when the mouse leaves the client area and the base class processing of this message will then call the OnMouseLeave method. If this is really not happening you can certainly get around it. Just intercept the WM_MOUSEMOVE directly and then make a Win32 API call that requests you be notified when the mouse leaves your control.
Use the following simple WndProc override...
private bool _mouseOver = false;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case PI.WM_MOUSEMOVE:
if (!_mouseOver)
{
PI.TRACKMOUSEEVENTS tme = new PI.TRACKMOUSEEVENTS();
tme.cbSize = (uint)Marshal.SizeOf(typeof(PI.TRACKMOUSEEVENTS));
tme.dwHoverTime = 100;
tme.dwFlags = (int)(PI.TME_LEAVE);
tme.hWnd = Handle;
PI.TrackMouseEvent(ref tme);
_mouseOver = true;
}
base.WndProc(ref m);
break;
case PI.WM_MOUSELEAVE:
_mouseOver = false;
base.WndProc(ref m);
break;
}
}
And the platform invoke information you need is...
internal const int WM_MOUSEMOVE = 0x0200;
internal const int WM_MOUSELEAVE = 0x02A3;
internal const int TME_LEAVE = 0x0002;
[StructLayout(LayoutKind.Sequential)]
internal struct TRACKMOUSEEVENTS
{
public uint cbSize;
public uint dwFlags;
public IntPtr hWnd;
public uint dwHoverTime;
}
Hope that helps.