c# Exceptions Not Stopping at Location of exception Debugging Problem - c#

my c# forms app (Vs 2022) in debug mode is just now beginning to show errors in weird places like String.Manipulation.cs. I know the error occurred in my app. But it shows it in some predefined code. I tried to clean and re-open the project, and recompile. But the debug behavior persists. The code below represents a section of the predefined code where it points.
The specific line is
"throw new ArgumentOutOfRangeException(paramName,message);"
the error details;
"System.ArgumentOutOfRangeException HResult=0x80131502
Message=startIndex cannot be larger than length of string. (Parameter
'startIndex') Source=System.Private.CoreLib StackTrace: at
System.String.ThrowSubstringArgumentOutOfRange(Int32 startIndex, Int32
length) in
//src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs:line
1878 at System.String.Substring(Int32 startIndex) in
//src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs:line
1837 at cabinetry.frmMain.ProcessScan() in
C:\Users\doug\source\cabinetry\cabinetry\frmMain.cs:line 487 at
cabinetry.frmMain.ScannerTime_Tick(Object sender, EventArgs e) in
C:\Users\doug\source\cabinetry\cabinetry\frmMain.cs:line 558 at
System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr
wparam, IntPtr lparam) at Interop.User32.DispatchMessageW(MSG& msg)
at
System.Windows.Forms.Application.ComponentManager.Interop.Mso.IMsoComponentManager.FPushMessageLoop(UIntPtr
dwComponentID, msoloop uReason, Void* pvLoopData) at
System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(msoloop
reason, ApplicationContext context) at
System.Windows.Forms.Application.ThreadContext.RunMessageLoop(msoloop
reason, ApplicationContext context) at cabinetry.Program.Main() in
C:\Users\doug\source\cabinetry\cabinetry\Program.cs:line 14
[DoesNotReturn]
private void ThrowSubstringArgumentOutOfRange(int startIndex, int length)
{
(string paramName, string message) =
startIndex < 0 ? (nameof(startIndex), SR.ArgumentOutOfRange_StartIndex) :
startIndex > Length ? (nameof(startIndex), SR.ArgumentOutOfRange_StartIndexLargerThanLength) :
length < 0 ? (nameof(length), SR.ArgumentOutOfRange_NegativeLength) :
(nameof(length), SR.ArgumentOutOfRange_IndexLength);
throw new ArgumentOutOfRangeException(paramName, message);
}
private string InternalSubString(int startIndex, int length)
{
Debug.Assert(startIndex >= 0 && startIndex <= this.Length, "StartIndex is out of range!");
Debug.Assert(length >= 0 && startIndex <= this.Length - length, "length is out of range!");
string result = FastAllocateString(length);
Buffer.Memmove(
elementCount: (uint)result.Length, // derefing Length now allows JIT to prove 'result' not null below
destination: ref result._firstChar,
source: ref Unsafe.Add(ref _firstChar, (nint)(uint)startIndex /* force zero-extension */));
return result;

I can reproduce your situation:
Form1.cs
namespace WinFormsApp2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
AddControls();
}
}
}
Form1.Designer.cs
using System.Windows.Forms;
namespace WinFormsApp2
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void AddControls()
{
// Create text box
TextBox textBox = new TextBox();
textBox.Location = new Point(50, 50);
this.Controls.Add(textBox);
// create button
Button button = new Button();
button.Text = "add input";
button.Location = new Point(50, 80);
button.Click += (sender, e) =>
{
textBox.Text = "xxx"; // something
this.Controls.Add(button);
int argumentValue = -1;
int minimumValue = 0;
int maximumValue = 100;
if (argumentValue < minimumValue || argumentValue > maximumValue)
{
throw new ArgumentOutOfRangeException("argumentValue", argumentValue,
$"Parameter must between {minimumValue} and {maximumValue}.");
}
};
this.Controls.Add(button);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Text = "Form1";
}
#endregion
}
}
If I start debugging and click the button I created, click step into, I will jump in the external code(Not my code):
As Alexei metioned, you just need to select "Enable Just My Code" in Tools -> Options -> Debugging -> General:
By the way, "Enable Just My Code" is the default settings, so next time if you encounter some situations that something is normal before but become strange now, you can consider to reset the entire settings via these(In the way, you don't need to know which setting cause the issue, just reset entire settings.):
Reset environment settings:
turn off synchronized settings on a particular computer(This step is to prevent settings override):
]8

Related

Why aren't the generated buttons showing in the FlowLayoutPanel?

So, I'm trying to create an accordion with dynamically loaded buttons. In the future, the title of the buttons will change depending on the details I've retrieved from somewhere. For now, what I'm trying to do is to load buttons to look like these:
I've tried doing the following below:
// Forms1.cs
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
int buttonCount = 3;
var buttons = new FontAwesome.Sharp.IconButton[buttonCount];
for (int i = 0; i < buttonCount; i++)
{
var btn = new FontAwesome.Sharp.IconButton
{
Text = "Button " + i,
TextAlign = ContentAlignment.MiddleLeft,
IconChar = FontAwesome.Sharp.IconChar.Book,
IconColor = ColorTranslator.FromHtml("#6A6A73"),
IconSize = 20,
IconFont = FontAwesome.Sharp.IconFont.Auto,
TextImageRelation = TextImageRelation.ImageBeforeText,
FlatStyle = FlatStyle.Flat
};
btn.FlatAppearance.BorderSize = 0;
btn.ForeColor = ColorTranslator.FromHtml("#6A6A73");
btn.BackColor = ColorTranslator.FromHtml("#FDFEFF");
btn.Dock = DockStyle.Top;
buttons[i] = btn;
}
flowLayoutPanel1.Controls.AddRange(buttons);
}
}
}
// Forms1.Designer.cs
namespace WindowsFormsApp1
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
this.SuspendLayout();
//
// flowLayoutPanel1
//
this.flowLayoutPanel1.BackColor = System.Drawing.SystemColors.ControlLight;
this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Left;
this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
this.flowLayoutPanel1.Size = new System.Drawing.Size(200, 450);
this.flowLayoutPanel1.TabIndex = 0;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.flowLayoutPanel1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
}
}
Here's what it looks like after building and running the application:
What am I doing wrong?
Form1_Load method is not subscribed to Load event of your form. Body of InitializeComponent is missing following line of code.
this.Load += new System.EventHandler(this.Form1_Load);
Insert this line before this.ResumeLayout(false);. You can fix it in designer as well.

Prevent parent container from scrolling by the mouse wheel if a child (graphic) control is already scrolling

How to prevent the parent container from scrolling by the mouse wheel if the child (graphic) control is already focused and is scrolling?
I have a GMap.NET WinForms control embedded in a WinForms. Whenever that control is focused and scrolled via the mouse wheel, the parent form will also be scrolled. I just want the parent form to stay put while I'm scrolling the GMap.NET control via the mouse wheel. And when I want to scroll the parent form, I can always make GMap.NET control lose focus.
How to do what I want?
Here's my code:
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.gMapControl = new GMap.NET.WindowsForms.GMapControl();
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
this.SuspendLayout();
//
// gMapControl
//
this.gMapControl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.gMapControl.Bearing = 0F;
this.gMapControl.CanDragMap = true;
this.gMapControl.EmptyTileColor = System.Drawing.Color.Navy;
this.gMapControl.GrayScaleMode = false;
this.gMapControl.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow;
this.gMapControl.LevelsKeepInMemmory = 5;
this.gMapControl.Location = new System.Drawing.Point(525, 1);
this.gMapControl.MarkersEnabled = true;
this.gMapControl.MaxZoom = 2;
this.gMapControl.MinZoom = 2;
this.gMapControl.MouseWheelZoomEnabled = true;
this.gMapControl.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter;
this.gMapControl.Name = "gMapControl";
this.gMapControl.NegativeMode = false;
this.gMapControl.PolygonsEnabled = true;
this.gMapControl.RetryLoadTile = 0;
this.gMapControl.RoutesEnabled = true;
this.gMapControl.ScaleMode = GMap.NET.WindowsForms.ScaleModes.Integer;
this.gMapControl.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225)))));
this.gMapControl.ShowTileGridLines = false;
this.gMapControl.Size = new System.Drawing.Size(198, 378);
this.gMapControl.TabIndex = 0;
this.gMapControl.Zoom = 0D;
//
// flowLayoutPanel1
//
this.flowLayoutPanel1.BackColor = System.Drawing.Color.Black;
this.flowLayoutPanel1.Location = new System.Drawing.Point(1, 1);
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
this.flowLayoutPanel1.Size = new System.Drawing.Size(489, 885);
this.flowLayoutPanel1.TabIndex = 6;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoScroll = true;
this.ClientSize = new System.Drawing.Size(745, 533);
this.Controls.Add(this.flowLayoutPanel1);
this.Controls.Add(this.gMapControl);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
}
#endregion
private GMap.NET.WindowsForms.GMapControl gMapControl;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
gMapControl.MapProvider = GMapProviders.GoogleTerrainMap;
gMapControl.MinZoom = 5;
gMapControl.MaxZoom = 12;
gMapControl.Zoom = 7.5;
gMapControl.ShowCenter = true;
gMapControl.DragButton = MouseButtons.Middle;
}
}
This is not just a problem of the GMap.NET for sure, I've tested with other (proprietary) graphic control and it behaves all the same.
I'm not sure if there's a better way around this but here's a hacky workaround to stop the WM_MOUSEWHEEL message from reaching the control and still execute the zooming logic.
Add the following class to your project:
public class MyGMapControl : GMapControl, IMessageFilter
{
public MyGMapControl()
{
Application.AddMessageFilter(this);
}
protected override void Dispose(bool disposing)
{
if (disposing) Application.RemoveMessageFilter(this);
base.Dispose(disposing);
}
public bool PreFilterMessage(ref Message m)
{
const int WM_MOUSEWHEEL = 0x020A;
if (m.HWnd == this.Handle && m.Msg == WM_MOUSEWHEEL)
{
Point posOnScreen = new Point(m.LParam.ToInt32());
Point pos = PointToClient(posOnScreen);
int delta = m.WParam.ToInt32();
var args = new MouseEventArgs(MouseButtons.None, 0, pos.X, pos.Y, delta);
this.OnMouseWheel(args);
return true;
}
return false;
}
}
Then, use MyGMapControl instead of GMapControl.
Here, we're creating a MessageFilter to intercept the WM_MOUSEWHEEL message and return true in PreFilterMessage to stop the message from reaching the control. Now, the scrolling won't occur but neither will the zooming logic because it's implemented inside OnMouseWheel() which is triggered by the WM_MOUSEWHEEL message. So, we manually call the OnMouseWheel() method before returning true to ensure that the zooming occurs.
As Jimi suggested in the comments, you could override WndProc() of the control's parent container, check for WM_MOUSEWHEEL, and return if the mouse cursor is over the GMapControl:
protected override void WndProc(ref Message m)
{
const int WM_MOUSEWHEEL = 0x020A;
if (m.Msg == WM_MOUSEWHEEL)
{
if (gMapControl.Bounds.Contains(PointToClient(MousePosition))) return;
}
base.WndProc(ref m);
}

Using User32.dll SendMessage to handle the download popup

I'm navigating through a webapp using WatiN and I needed to download a document. However, I've found out that WatiN doesn't support the download with Internet Explorer 11. That's why I'm trying to do it using the method described here :
How to enable automatic downloads in IE11 (the top answer)
Using User32.dll SendMessage To Send Keys With ALT Modifier
Basically I'm calling user32.dll to handle the small popup DL window (F6 to select it, tab, enter, etc.)
However nothing happens, the popup isn't responding to my commands.
Is this the right way to do it ?
My code looks like this :
//Before my method
[DllImport("user32.dll")]
public static extern IntPtr SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, uint wParam, uint lParam);
private void button1_Click(object sender, EventArgs e)
{
//some code....
IE myPopup = IE.AttachTo<IE>(Find.ByUrl("http://abcd.do"));
FileDownloadHandler fileDownloadHandler = new FileDownloadHandler("test");
myPopup.AddDialogHandler(fileDownloadHandler);
myPopup.Link(Find.ByUrl("httpabcdefg.do")).ClickNoWait();
ushort action = (ushort)260; //WM_SYSKEYDOWN
System.Threading.Thread.Sleep(2000);
ushort key = (ushort)System.Windows.Forms.Keys.F6;
ushort key2 = (ushort)System.Windows.Forms.Keys.Tab;
ushort key3 = (ushort)System.Windows.Forms.Keys.Enter;
ushort key4 = (ushort)System.Windows.Forms.Keys.Right;
SendMessage(myPopup.hWnd, action, key, 0);
SendMessage(myPopup.hWnd, action, key2, 0);
SendMessage(myPopup.hWnd, action, key3, 0);
SendMessage(myPopup.hWnd, action, key4, 0);
SendMessage(myPopup.hWnd, action, key4, 0);
SendMessage(myPopup.hWnd, action, key3, 0);
}
I know the popup (myPopup) is handled correctly, I can click on it using watin.
A couple of things are unclear to me, and I put the action WM_SYSKEYDOWN as default, to follow the example cited above.
Thanks in advance for any kind of help !
--------------EDIT-----------------
I managed to do it, I know it's not optimal at all but I dropped the Dllimport and the SendMessage all together and used SendKeys.Send instead. Just like that :
private void button1_Click(object sender, EventArgs e)
{
//some code....
IE myPopup = IE.AttachTo<IE>(Find.ByUrl("http://abcd.do"));
FileDownloadHandler fileDownloadHandler = new FileDownloadHandler("test");
myPopup.AddDialogHandler(fileDownloadHandler);
myPopup.Link(Find.ByUrl("httpabcdefg.do")).ClickNoWait();
SendKeys.Send("{F6}");
SendKeys.Send("{ENTER}");
SendKeys.Send("{RIGHT}");
SendKeys.Send("{RIGHT}");
SendKeys.Send("{ENTER}");
}
The code that you are using is not a proper way to automate and WatiN does not completely interact with Windows controls and help in windows is very little. I had the same problem in handling the windows controls as we had multiple versions of IE. IE 9.0 and higher have different file handling when compared to <= IE 8.0 version. The below code works fine for IE 9.0 and higher. Please make sure proper references are added (refer using's).
Refer below code and modify it as per you requirement.
using System.Threading;
using System.Windows.Automation;
using WatiN.Core;
using WatiN.Core.Native.Windows;
namespace TestFramework.Util
{
public static class WindowsHelper
{
#region Public Methods
/// <summary>
/// Download IE file.
/// </summary>
/// <param name="action">Action can be Save/Save As/Open/Cancel.</param>
/// <param name="path">Path where file needs to be saved (for Save As function).</param>
public static void DownloadIEFile(string action, string path = "", string regexPatternToMatch = "")
{
Browser browser = null;
if (Utility.Browser != null) // Utility.Browser is my WatiN browser instance.
{
if (string.IsNullOrEmpty(regexPatternToMatch))
{
browser = Utility.Browser;
}
else
{
Utility.Wait(() => (browser = Browser.AttachTo<IE>(Find.ByUrl(new System.Text.RegularExpressions.Regex(regexPatternToMatch)))) != null);
}
}
else
{
return;
}
// If doesn't work try to increase sleep interval or write your own waitUntill method
Thread.Sleep(3000);
// See information here (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633515(v=vs.85).aspx)
Window windowMain = null;
Utility.Wait(() => (windowMain = new Window(NativeMethods.GetWindow(browser.hWnd, 5))).ProcessID != 0);
TreeWalker trw = new TreeWalker(Condition.TrueCondition);
AutomationElement mainWindow = trw.GetParent(AutomationElement.FromHandle(browser.hWnd));
Window windowDialog = null;
Utility.Wait(() => (windowDialog = new Window(NativeMethods.GetWindow(windowMain.Hwnd, 5))).ProcessID != 0);
windowDialog.SetActivate();
AutomationElementCollection amc = null;
Utility.Wait(() => (amc = AutomationElement.FromHandle(windowDialog.Hwnd).FindAll(TreeScope.Children, Condition.TrueCondition)).Count > 1);
foreach (AutomationElement element in amc)
{
// You can use "Save ", "Open", ''Cancel', or "Close" to find necessary button Or write your own enum
if (element.Current.Name.Equals(action))
{
// If doesn't work try to increase sleep interval or write your own waitUntil method
// WaitUntilButtonExsist(element,100);
Thread.Sleep(1000);
AutomationPattern[] pats = element.GetSupportedPatterns();
// Replace this for each if you need 'Save as' with code bellow
foreach (AutomationPattern pat in pats)
{
// '10000' button click event id
if (pat.Id == 10000)
{
InvokePattern click = (InvokePattern)element.GetCurrentPattern(pat);
click.Invoke();
}
}
}
else if (element.Current.Name.Equals("Save") && action == "Save As")
{
AutomationElementCollection bmc = element.FindAll(TreeScope.Children, Automation.ControlViewCondition);
InvokePattern click1 = (InvokePattern)bmc[0].GetCurrentPattern(AutomationPattern.LookupById(10000));
click1.Invoke();
Thread.Sleep(1000);
AutomationElementCollection main = mainWindow.FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement el in main)
{
if (el.Current.LocalizedControlType == "menu")
{
// First array element 'Save', second array element 'Save as', third second array element 'Save and open'
InvokePattern clickMenu = (InvokePattern)
el.FindAll(TreeScope.Children, Condition.TrueCondition)[1].GetCurrentPattern(AutomationPattern.LookupById(10000));
clickMenu.Invoke();
Thread.Sleep(1000);
ControlSaveDialog(mainWindow, path);
break;
}
}
}
}
}
/// <summary>
/// Control for save dialog.
/// </summary>
/// <param name="mainWindow">Main window.</param>
/// <param name="path">Path.</param>
private static void ControlSaveDialog(AutomationElement mainWindow, string path)
{
// Obtain the save as dialog
var saveAsDialog = mainWindow
.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, "Save As"));
// Get the file name box
var saveAsText = saveAsDialog
.FindFirst(TreeScope.Descendants,
new AndCondition(
new PropertyCondition(AutomationElement.NameProperty, "File name:"),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit)))
.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
// Fill the filename box
saveAsText.SetValue(path);
Thread.Sleep(500);
Utility.PressKey("LEFT");
Utility.PressKey("LEFT");
Thread.Sleep(1000);
// Find the save button
var saveButton =
saveAsDialog.FindFirst(TreeScope.Descendants,
new AndCondition(
new PropertyCondition(AutomationElement.NameProperty, "Save"),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)));
// Invoke the button
var pattern = saveButton.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
pattern.Invoke();
}
#endregion
}
}
public static class Utility
{
public static IE Browser { get; set; }
// Wait specified number of seconds
public static void Wait(int seconds)
{
System.Threading.Thread.Sleep(seconds * 1000);
}
// Wait for condition to evaluate true, timeout after 30 seconds
public static void Wait(Func<bool> condition)
{
int count = 0;
while (!condition() && count < 30)
{
System.Threading.Thread.Sleep(1000);
count++;
}
}
//Send tab key press to browser
public static void PressTab()
{
System.Windows.Forms.SendKeys.SendWait("{TAB}");
System.Threading.Thread.Sleep(300);
}
//Send specified key press to browser
public static void PressKey(string keyname)
{
System.Windows.Forms.SendKeys.SendWait("{" + keyname.ToUpper() + "}");
System.Threading.Thread.Sleep(300);
}
}

WM_NCLBUTTONUP message not sent at the end of dragging a form, how to do so?

EDIT: tl;dr go to the first comment.
This question stems from another question of mine Get MouseDown event when mouse goes down on Form border?
In that question I needed to have a form fire an event when the user pushed the left mouse button down on the form border (preparing to drag), which works perfectly. The problem is when the user has finished this action, by letting go of the left mouse button, I would also like to have an event fired.
To do so I produced this code to be placed in a "base form" class that other forms would be derived from. I have removed the FireMouseButton...() methods for succinctness; they fire custom events.
const int WM_NCLBUTTONUP = 0xA2;
const int WM_NCLBUTTONDWN = 0xA1;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_NCLBUTTONUP)
FireMouseButtonUp();
if (m.Msg == WM_NCLBUTTONDWN)
FireMouseButtonDown();
base.WndProc(ref m);
}
The problem with this is that the WM_NCLBUTTONUP message is not sent as I expected it. After reviewing the description for WM_NCLBUTTONUP I can see why though,
[WM_NCLBUTTONUP is] posted when the user releases the left mouse button while the cursor
is within the nonclient area of a window. This message is posted to
the window that contains the cursor. If a window has captured the
mouse, this message is not posted.
Because the form has captured the mouse while it is being dragged, it will not receive the WM_NCLBUTTONUP message. (It will if the form is maximized though). This question explains it a little better The curious problem of the missing WM_NCLBUTTONUP message when a window isn't maximised.
The answer to that question is somewhat helpful but causes a lot of confusion for me. In the code below I have a small SSCCE, it implements some code given from the solution to the answer above, checking the WMNCHITTEST message to see if the mouse has been released;
The idea is that the WM_NCHITTEST should be sent when the mouse is moving within the form. So once a drag stops this message should be sent with the mouse position as DragStartPoint in the WndProc message arguments; where DragStartPoint is recorded when the WM_NCLBUTTONDOWN message is received.
The issue with this though is that the WM_NCHITTEST is not always sent after the start of a drag, only when the drag is started on the far sides of the top border (see pic below). The WM_NCLBUTTONDOWN message is always sent when clicking on the top border (never for sides or bottom). So that is fine, but the WM_NCHITTEST and as pointed out WM_NCLBUTTONUP are sent, but only sometimes.
How might I get the WM_NCHITTEST "test" to work in the code below so that I can be notified once the user has stopped dragging the form? (the "test" being checking the DragStartPoint in the if statement for WM_NCHITTEST)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace MouseEventTest
{
public partial class Form1 : Form
{
Random rand = new Random();
public Form1()
{
InitializeComponent();
}
const int WM_NCHITTEST = 0x84;
const int WM_NCLBUTTONUP = 0xA2;
const int WM_NCLBUTTONDWN = 0xA1;
public Point DragStartPoint { get; set; }
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_NCLBUTTONUP)
{
label1.Text = "Mouse Up On Border";
}
if (m.Msg == WM_NCLBUTTONDWN)
{
label1.Text = "Mouse Down On Border";
Point pos = lParamToPoint(m.LParam);
DragStartPoint = this.PointToClient(pos);
Console.Out.WriteLine("DragStartPoint: " + DragStartPoint);
}
if(m.Msg == WM_NCHITTEST)
{
Point pos = lParamToPoint(m.LParam);
Console.Out.WriteLine("HtTestPnt: " + this.PointToClient(pos));
if (DragStartPoint == this.PointToClient(pos))
{
label1.Text = "Mouse Up HitTest";
}
}
base.WndProc(ref m);
}
private Point lParamToPoint(IntPtr lParamIn)
{
int x = lParamIn.ToInt32() & 0x0000FFFF;
int y = (int)((lParamIn.ToInt32() & 0xFFFF0000) >> 16);
return new Point(x, y);
}
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(42, 30);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(91, 13);
this.label1.TabIndex = 0;
this.label1.Text = "99999999999999";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(185, 75);
this.Controls.Add(this.label1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
}
}

InvalidateRec with User32.dll , trying to delete all other frames except last frame created

Im actually trying to paint an elipse around my cursor when i moove it, I dont want it to leave a trail like it does actually.
Can someone help with this, I believe it has something to do with the invalidaterec option
Any example son using invalidate rec?
Here is my code, besides the trail it has to work the same way it does now.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace MouseTest
{
public partial class Form1 : Form
{
[DllImport("user32.dll", EntryPoint = "GetDC")]
private static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll", EntryPoint = "ReleaseDC")]
private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[System.Runtime.InteropServices.DllImport("Shell32.dll")]
private static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);
System.Timers.Timer t1 = new System.Timers.Timer(100);
public Form1()
{
t1.Elapsed += new System.Timers.ElapsedEventHandler(t1_Elapsed);
t1.Start();
// System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;
//Mouse.OverrideCursor = System.Windows.Input.Cursors.Hand;
InitializeComponent();
}
void t1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
t1.Stop();
//SolidBrush b = new SolidBrush(Color.Red);
IntPtr desktopDC = GetDC(IntPtr.Zero);
Graphics g = Graphics.FromHdc(desktopDC);
g.FillEllipse(new SolidBrush(Color.BlueViolet), Cursor.Position.X, Cursor.Position.Y, 25, 25);
g.Dispose();
ReleaseDC(IntPtr.Zero, desktopDC);
t1.Start();
}
}
}
Yes, keeping the previous coordinate and calling InvalidateRect on the previous enclosing rectangle before drawing the new new ellipse would probably work. I don't have any code lying around that calls InvalidateRect, but if this is exactly your goal:" to paint an elipse around my cursor when i moove it, I dont want it to leave a trail like it does actually." there are other, more straight forward ways.
One way is to draw the ellipse on a transparent form and then move that form around based on the cursor's movement:
Here, first is the form definition:
namespace MouseForm
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.shapeContainer1 = new Microsoft.VisualBasic.PowerPacks.ShapeContainer();
this.ovalShape1 = new Microsoft.VisualBasic.PowerPacks.OvalShape();
this.timer1 = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout();
//
// shapeContainer1
//
this.shapeContainer1.Location = new System.Drawing.Point(0, 0);
this.shapeContainer1.Margin = new System.Windows.Forms.Padding(0);
this.shapeContainer1.Name = "shapeContainer1";
this.shapeContainer1.Shapes.AddRange(new Microsoft.VisualBasic.PowerPacks.Shape[] {
this.ovalShape1});
this.shapeContainer1.Size = new System.Drawing.Size(74, 74);
this.shapeContainer1.TabIndex = 0;
this.shapeContainer1.TabStop = false;
//
// ovalShape1
//
this.ovalShape1.BorderColor = System.Drawing.Color.Red;
this.ovalShape1.BorderWidth = 3;
this.ovalShape1.FillColor = System.Drawing.SystemColors.Control;
this.ovalShape1.FillStyle = Microsoft.VisualBasic.PowerPacks.FillStyle.Solid;
this.ovalShape1.Location = new System.Drawing.Point(2, 2);
this.ovalShape1.Name = "ovalShape1";
this.ovalShape1.Size = new System.Drawing.Size(70, 70);
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(74, 74);
this.ControlBox = false;
this.Controls.Add(this.shapeContainer1);
this.Enabled = false;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Name = "Form1";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.Text = "Form1";
this.TopMost = true;
this.TransparencyKey = System.Drawing.SystemColors.Control;
this.ResumeLayout(false);
}
#endregion
private Microsoft.VisualBasic.PowerPacks.ShapeContainer shapeContainer1;
private Microsoft.VisualBasic.PowerPacks.OvalShape ovalShape1;
private System.Windows.Forms.Timer timer1;
}
}
And here is the code:
using System;
using System.Windows.Forms;
namespace MouseForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
this.Left = Cursor.Position.X - this.Width / 2;
this.Top = Cursor.Position.Y - this.Height / 2;
}
}
}
I'll leave it as an exercise to the reader to implement a way to close the transparent form :)

Categories