(C#) Showing where to click instead of automatic clicking - c#

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...

Related

Avoid TextBox flickering while appending text

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.

How to restrict form movement to horizontal?

I have a standard form with standard title bar that user may grab and move the form around. In certain situations I want to restrict this movement to horizontal only, so no matter how the mouse actually moves, the form remains on same Y coordinate.
To do this, I catch move event and when I detect deviation from Y, I move the form back to the original Y. Like that:
private void TemplateSlide_Move(object sender, EventArgs e)
{
int y = SlideSettings.LastLocation.Y;
if (y != this.Location.Y)
{
SlideSettings.LastLocation = new Point(this.Location.X, y);
this.Location=Settings.LastLocation;
}
}
But this causes a lot of flicker. Also because form actually moves for a brief moment away from desired Y this causes other issues specific to my program.
Is there a way to prevent form from moving away from desired Y coordinate?
Trap WM_MOVING and modify the RECT structure in LPARAM accordingly.
Something like:
public partial class Form1 : Form
{
public const int WM_MOVING = 0x216;
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
private int OriginalY = 0;
private int OriginalHeight = 0;
private bool HorizontalMovementOnly = true;
public Form1()
{
InitializeComponent();
this.Shown += new EventHandler(Form1_Shown);
this.SizeChanged += new EventHandler(Form1_SizeChanged);
this.Move += new EventHandler(Form1_Move);
}
void Form1_Move(object sender, EventArgs e)
{
this.SaveValues();
}
void Form1_SizeChanged(object sender, EventArgs e)
{
this.SaveValues();
}
void Form1_Shown(object sender, EventArgs e)
{
this.SaveValues();
}
private void SaveValues()
{
this.OriginalY = this.Location.Y;
this.OriginalHeight = this.Size.Height;
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_MOVING:
if (this.HorizontalMovementOnly)
{
RECT rect = (RECT)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, typeof(RECT));
rect.Top = this.OriginalY;
rect.Bottom = rect.Top + this.OriginalHeight;
System.Runtime.InteropServices.Marshal.StructureToPtr(rect, m.LParam, false);
}
break;
}
base.WndProc(ref m);
}
}
When it's appropriate, just use the Mouse's Y coordinate, substituting the X coordinate with your static value. e.g.
... // e.g Mouse Down
originalX = Mouse.X; // Or whatever static X value you have.
... // e.g Mouse Move
// Y is dynamically updated while X remains static
YourObject.Location = new Point(originalX, Mouse.Y);

TableLayoutPanel responds very slowly to events

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);
}
}

Notification Window - Preventing the window from ever getting focus

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;
}
}

WinForms: finding the size of a minimized form without going to FormWindowState.Normal

Is there an easy way to determine the size of a Form it does have in WindowState=Normal, without actually changing the Form state?
Here is what I do now (C# code):
public class MyForm: Form
{
public void MyMethod()
{
// ...
FormWindowState oldState = this.WindowState;
this.WindowState = FormWindowState.Normal;
Point windowLocation = this.Location;
Size windowSize = this.Size;
this.WindowState = oldState;
// ...
}
}
This is what I would like the code to look like:
public class MyForm: Form
{
public void MyMethod()
{
// no state change here
Point windowLocation = this.NormalStateLocation;
Size windowSize = this.NormalStateSize;
}
}
In fact there are no NormalStateLocation or NormalStateSize properties in Windows Forms.
Since .NET 2.0, there is a property RestoreBounds that contains values you need.
I wanted to do the exact same thing and as nobody actually answered your question I'm posting my solution now even though you were already satisfied. Maybe somebody needs it sometime.
class NativeMethods
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
/// <summary>
/// See MSDN RECT Structure http://msdn.microsoft.com/en-us/library/dd162897(v=VS.85).aspx
/// </summary>
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
/// <summary>
/// See MSDN WINDOWPLACEMENT Structure http://msdn.microsoft.com/en-us/library/ms632611(v=VS.85).aspx
/// </summary>
private struct WINDOWPLACEMENT
{
public int length;
public int flags;
public int showCmd;
public Point ptMinPosition;
public Point ptMaxPosition;
public RECT rcNormalPosition;
}
/// <summary>
/// Gets the window placement of the specified window in Normal state.
/// </summary>
/// <param name="handle">The handle.</param>
/// <returns></returns>
public static Rectangle GetPlacement(IntPtr handle)
{
WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
placement.length = System.Runtime.InteropServices.Marshal.SizeOf(placement);
GetWindowPlacement(handle, ref placement);
var rect = placement.rcNormalPosition;
return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
}
}
The Load event is the earliest possible moment to find out what the "normal" size of the form will be. At that point, any scaling induced by AutoSizeMode and any user overrides to border and caption sizes have been applied. However, that size will only be valid if the WindowState is not set to Minimized in the designer. You'll have to wait until the form is at least shown once in the Normal state.
Also beware that the size you'll get is faked if you run with Aero fat borders enabled. Vista and Win7 intentionally return a window size that would be reported if earlier operating systems ran your program. If this really matters, you'll have to use Editbin.exe to mark your program Vista compatible.
Bear traps out there, try to avoid having to ask the question. You can always change the window location or size when you get the Resize event.
If this is only to memorize the form location and size then you'll want to use Settings. Use Project + Properties, Settings tab to add them. You cannot use the automatic ApplicationSettings binding because you don't want to save the location and size of the form when it is minimized or maximized. Make it look like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
protected override void OnLoad(EventArgs e) {
if (Properties.Settings.Default.Form1Size.Width > 0) { // Valid?
this.Size = Properties.Settings.Default.Form1Size;
this.Location = Properties.Settings.Default.Form1Location;
}
base.OnLoad(e);
}
protected override void OnLocationChanged(EventArgs e) {
if (this.WindowState == FormWindowState.Normal)
Properties.Settings.Default.Form1Location = this.Location;
base.OnLocationChanged(e);
}
protected override void OnResize(EventArgs e) {
if (this.WindowState == FormWindowState.Normal)
Properties.Settings.Default.Form1Size = this.Size;
base.OnResize(e);
}
protected override void OnFormClosed(FormClosedEventArgs e) {
Properties.Settings.Default.Save();
base.OnFormClosed(e);
}
}
try this
private Size _normalSize;
private Point _location;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.LocationChanged += new EventHandler(Form1_LocationChanged);
this.SizeChanged += new EventHandler(Form1_SizeChanged);
}
void Form1_SizeChanged(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Normal)
{
this._normalSize = this.Size;
}
}
void Form1_LocationChanged(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Normal)
{
this._location = this.Location;
}
}
Why not just save off the size and location of the form when the form is first loaded? Of course, it would have to start out normal:
form.Shown += (s, e1) =>
{
// Save off the size and location
form.SaveSizeAndLocation();
};

Categories