moving 2 controls (picturebox) moving parallel not sequence - c#

the two virtual mouse image mission is move horizontal movement and click the destination point . in the following code is running successfully in Sequence (one by one ).
i want to edit this code to working parallel (the two image working in the same time ) .
the mouseimg1 refer to => mouse 1 and mouseimg2 refer to => mouse 2 .
public partial class Form1 : Form
{
public delegate void delMouse();
public delMouse delMouse1; enter code here
public delMouse delMouse2;
private Thread t1, t2;
public Form1()
{
InitializeComponent();
delMouse1 = new delMouse(mouse1);
delMouse2 = new delMouse(mouse2);
}
private void button1_Click(object sender, EventArgs e)
{
t1 = new Thread(new ThreadStart(delMouse1));
t1.Start();
}
private void button2_Click(object sender, EventArgs e)
{
t2 = new Thread(new ThreadStart(delMouse2));
t2.Start();
}
void mouse2()
{
if (this.mouseimg2.InvokeRequired)
{
this.Invoke(delMouse2);
}
else
{
int destinval2 = int.Parse(textBox2.Text);
while (mouseimg2.Location.Y != destinval2)
{
if (mouseimg2.Location.Y == 250)
mouseimg2.Location = new Point(mouseimg2.Location.X, 15);
if (mouseimg2.Location.Y < destinval2)
mouseimg2.Location = new Point(mouseimg2.Location.X, mouseimg2.Location.Y + 1);
else
mouseimg2.Location = new Point(mouseimg2.Location.X, mouseimg2.Location.Y - 1);
}
LeftClick(mouseimg2.Location.X, mouseimg2.Location.Y);
}
}
void mouse1()
{
if (this.mouseimg1.InvokeRequired)
{
this.Invoke(delMouse1);
}
else
{
int destinval1 = int.Parse(textBox1.Text);
while (mouseimg1.Location.Y != destinval1)
{
if (mouseimg1.Location.Y < destinval1)
mouseimg1.Location = new Point(mouseimg1.Location.X, mouseimg1.Location.Y + 1);
else
mouseimg1.Location = new Point(mouseimg1.Location.X, mouseimg1.Location.Y - 1);
}
LeftClick(mouseimg1.Location.X, mouseimg1.Location.Y);
}
}
[DllImport("user32.dll")]
static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
[Flags]
public enum MouseEventFlags
{
LEFTDOWN = 0x00000002,
LEFTUP = 0x00000004,
MIDDLEDOWN = 0x00000020,
MIDDLEUP = 0x00000040,
MOVE = 0x00000001,
ABSOLUTE = 0x00008000,
RIGHTDOWN = 0x00000008,
RIGHTUP = 0x00000010
}
public static void LeftClick(int x, int y)
{
Cursor.Position = new System.Drawing.Point(x, y);
mouse_event((int)(MouseEventFlags.LEFTDOWN), 0, 0, 0, 0);
mouse_event((int)(MouseEventFlags.LEFTUP), 0, 0, 0, 0);
}
}
screenshot

In general in Windows you should not access a window from a thread other than the one which created it. WinForms enforces this rule, as you've found.
I'm not really clear what you want to do, but creating two threads is almost certainly the wrong way to go about it.
Behaviour that looks a bit like multithreading but isn't is often accomplished using timers, if that's at all helpful to you.

Related

Trying to match pixel colour then click [C#]

I need help getting my program to match the "stored" colour with the current one in the same location then click the mouse if it's the same. The grabbing of the colour works great so far in my code just unsure how to match a colour and a point, etc.
Also a start/stop button for the loop would be nice.
My code so far:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Pixel_detection_test_3
{
public partial class PixelDetectionForm : Form
{
private const UInt32 MOUSEEVENTF_LEFTDOWN = 0x0002;
private const UInt32 MOUSEEVENTF_LEFTUP = 0x0004;
[DllImport("user32.dll")]
private static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, uint dwExtraInf);
private int pixelY;
private int pixelX;
private Point pixelYX;
private static Color currentColour;
private static Color storedColour;
public PixelDetectionForm()
{
InitializeComponent();
}
static Color GetPixel(Point position)
{
using (var bitmap = new Bitmap(1, 1))
{
using (var graphics = Graphics.FromImage(bitmap))
{
graphics.CopyFromScreen(position, new Point(0, 0), new Size(1, 1));
}
return bitmap.GetPixel(0, 0);
}
}
private void PixelDetectionForm_KeyDown(object sender, KeyEventArgs e)
{
// Get Cursor Pixel Position
if (e.KeyCode == Keys.F1 || e.KeyCode == Keys.F2)
{
pixelY = Cursor.Position.Y;
pixelX = Cursor.Position.X;
pixelYX = Cursor.Position;
textBoxYPos.Text = pixelY.ToString();
textBoxXPos.Text = pixelX.ToString();
e.Handled = true;
}
// Get Cursor Pixel Colour
if (e.KeyCode == Keys.F1 || e.KeyCode == Keys.F3)
{
storedColour = GetPixel(Cursor.Position);
textBoxColour.Text = storedColour.ToString().Remove(0, 14).TrimEnd(']');
panelColourDisplay.BackColor = storedColour;
e.Handled = true;
}
}
// Not working, need help with this
private async void buttonStart_Click(object sender, EventArgs e)
{
while (true)
{
GetPixel(pixelYX);
// Should get position of 'pixelY' and 'pixelX'
panelColourDisplay2.BackColor = GetPixel(Cursor.Position);
if (pixelYX == storedColour)
{
MousePress();
}
// Need this to prevent not responding
await Task.Delay(3);
}
}
private void MousePress()
{
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
private void PixelDetectionForm_Click(object sender, EventArgs e)
{
ActiveControl = null;
}
private void PixelDetectionForm_Activated(object sender, EventArgs e)
{
ActiveControl = null;
}
}
}
Thanks
Well, an alternative to the while..loop is using a Timer to achieve that.
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Pixel_detection_test_3
{
public partial class PixelDetectionForm : Form
{
private readonly Timer Tmr;
private Point lastPoint;
//Assign this from your input code.
private Color targetColor;
public PixelDetectionForm()
{
Tmr = new Timer { Interval = 50 };
Tmr.Tick += (s, e) => FindMatches(Cursor.Position);
}
//...
In the timer's Tick event, the FindMatches(..) method is called to check the current Cursor.Position and add the distinct matches into a ListBox. You can replace the last part with what you really need to do when you find a match. Like calling the MousePress() method in your code:
//...
private void FindMatches(Point p)
{
//To avoid the redundant calls..
if (p.Equals(lastPoint)) return;
lastPoint = p;
using (var b = new Bitmap(1, 1))
using (var g = Graphics.FromImage(b))
{
g.CopyFromScreen(p, Point.Empty, b.Size);
var c = b.GetPixel(0, 0);
if (c.ToArgb().Equals(targetColor.ToArgb()) &&
!listBox1.Items.Cast<Point>().Contains(p))
{
listBox1.Items.Add(p);
listBox1.SelectedItem = p;
}
}
}
private void PixelDetectionForm_FormClosing(object sender, FormClosingEventArgs e)
{
Tmr.Dispose();
}
}
}
Start and stop the timer in the click events of the Start and Stop buttons.
Here's a demo:
Another alternative is to use the Global Mouse and Keyboard Hooks. Check this, this, and this for more details.
Edit 2/11/2020
If you just want to check whether a given color at a given point exists in a given image, then you can do:
private void buttonStart_Click(object sender, EventArgs e)
{
var targetColor = ...; //target color.
var targetPoint = ...; //target point.
var sz = Screen.PrimaryScreen.Bounds.Size;
using (var b = new Bitmap(sz.Width, sz.Height, PixelFormat.Format32bppArgb))
using (var g = Graphics.FromImage(b))
{
g.CopyFromScreen(Point.Empty, Point.Empty, b.Size, CopyPixelOperation.SourceCopy);
var bmpData = b.LockBits(new Rectangle(Point.Empty, sz), ImageLockMode.ReadOnly, b.PixelFormat);
var pixBuff = new byte[bmpData.Stride * bmpData.Height];
Marshal.Copy(bmpData.Scan0, pixBuff, 0, pixBuff.Length);
b.UnlockBits(bmpData);
for (var y = 0; y < b.Height; y++)
for(var x = 0; x < b.Width; x++)
{
var pos = (y * bmpData.Stride) + (x * 4);
var blue = pixBuff[pos];
var green = pixBuff[pos + 1];
var red = pixBuff[pos + 2];
var alpha = pixBuff[pos + 3];
if (Color.FromArgb(alpha, red, green, blue).ToArgb().Equals(targetColor.ToArgb()) &&
new Point(x, y).Equals(targetPoint))
{
//execute you code here..
MessageBox.Show("The given color exists at the given point.");
return;
}
}
}
MessageBox.Show("The given color doesn't exist at the given point.");
}
If you want to get a list of all the positions of a given color, then create a new List<Point>() and change the check condition to:
//...
var points = new List<Point>();
if (Color.FromArgb(alpha, red, green, blue).ToArgb().Equals(targetColor.ToArgb()))
{
points.Add(new Point(x, y));
}

How to get the windows messages according to the child?

I am creating a form inside another form. In the child form I have created taskbar button. I am overriding WndProc in the button.I am referring https://www.codeproject.com/Articles/10171/Adding-a-Minimize-to-tray-button-to-a-Form-s-caption bar. But whenever I am getting messages(mouse coordinates) , it is given with respect to the parent form. This makes trouble in calculations .I want those with respect to child form.How to achieve this?
This is the code for form1
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Form2 form2 = new Form2();
form2.TopLevel = false;
form2.AutoScroll = true;
form2.Anchor = AnchorStyles.Top;
form2.Dock = DockStyle.Fill;
this.splitContainer1.Panel2.Controls.Add(form2);
form2.Show();
}
}
this is form2.
public partial class Form2 : Form
{
TyronM.MinTrayBtn Pin_Button;
public Form2()
{
InitializeComponent();
Pin_Button = new TyronM.MinTrayBtn(this);
Pin_Button.MinTrayBtnClicked += new TyronM.MinTrayBtnClickedEventHandler(this.Pin_Button_Clicked);
}
public void Pin_Button_Clicked(object sender, EventArgs e)
{
MessageBox.Show("Pin button got Clicked");
}
protected override void WndProc(ref Message message)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MOVE = 0xF010;
switch (message.Msg)
{
case WM_SYSCOMMAND:
int command = message.WParam.ToInt32() & 0xfff0;
if (command == SC_MOVE)
return;
break;
}
base.WndProc(ref message);
}
}
And this is for button.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Diagnostics;
using System.Resources;
using System.Runtime.InteropServices;
namespace TyronM {
public delegate void MinTrayBtnClickedEventHandler(object sender, EventArgs e);
/// <summary>
/// Summary description for Class.
/// </summary>
public class MinTrayBtn : NativeWindow {
bool pinned = false;
bool pressed = false;
Size wnd_size = new Size();
public bool captured;
Form parent;
public event MinTrayBtnClickedEventHandler MinTrayBtnClicked;
#region Constants
const int WM_SIZE = 5;
const int WM_SYNCPAINT = 136;
const int WM_MOVE = 3;
const int WM_ACTIVATE = 6;
const int WM_LBUTTONDOWN =513;
const int WM_LBUTTONUP =514;
const int WM_LBUTTONDBLCLK =515;
const int WM_MOUSEMOVE = 512;
const int WM_PAINT = 15;
const int WM_GETTEXT = 13;
const int WM_NCCREATE =129;
const int WM_NCLBUTTONDOWN = 161;
const int WM_NCLBUTTONUP = 162;
const int WM_NCMOUSEMOVE = 160;
const int WM_NCACTIVATE =134;
const int WM_NCPAINT = 133;
const int WM_NCHITTEST = 132;
const int WM_NCLBUTTONDBLCLK = 163;
const int VK_LBUTTON = 1;
const int SM_CXSIZE = 30;
const int SM_CYSIZE = 31;
#endregion
#region Extra Constants
const int WM_MBUTTONUP = 0x0208;
#endregion
#region WinAPI Imports
[DllImport("user32")]
public static extern int GetWindowDC(int hwnd);
[DllImport("user32")]
public static extern short GetAsyncKeyState(int vKey);
[DllImport("user32")]
public static extern int SetCapture(int hwnd);
[DllImport("user32")]
public static extern bool ReleaseCapture();
[DllImport("user32")]
public static extern int GetSysColor(int nIndex);
[DllImport("user32")]
public static extern int GetSystemMetrics(int nIndex);
#endregion
#region Constructor and Handle-Handler ^^
public MinTrayBtn(Form parent) {
parent.HandleCreated += new EventHandler(this.OnHandleCreated);
parent.HandleDestroyed+= new EventHandler(this.OnHandleDestroyed);
parent.TextChanged+= new EventHandler(this.OnTextChanged);
this.parent = parent;
}
// Listen for the control's window creation and then hook into it.
internal void OnHandleCreated(object sender, EventArgs e){
// Window is now created, assign handle to NativeWindow.
AssignHandle(((Form)sender).Handle);
}
internal void OnHandleDestroyed(object sender, EventArgs e) {
// Window was destroyed, release hook.
ReleaseHandle();
}
// Changing the Text invalidates the Window, so we got to Draw the Button again
private void OnTextChanged(object sender, EventArgs e) {
DrawButton();
}
#endregion
#region WndProc
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
protected override void WndProc(ref Message m){
//label3.Text = "Button pressed: " + pressed;
//label4.Text = "Mouse captured: " + captured;
// Change the Pressed-State of the Button when the User pressed the
// left mouse button and moves the cursor over the button
if (m.Msg == WM_LBUTTONDBLCLK ||m.Msg == WM_LBUTTONDOWN || m.Msg == WM_LBUTTONUP
||m.Msg == WM_MBUTTONUP
)
MessageBox.Show("click happened");
if(m.Msg==WM_MOUSEMOVE) {
Point pnt2 = new Point((int)m.LParam);
Size rel_pos2 = new Size(parent.PointToClient(new Point(parent.Location.X, parent.Location.Y)));
// Not needed because SetCapture seems to convert the cordinates anyway
//pnt2 = PointToClient(pnt2);
pnt2-=rel_pos2;
//label2.Text = "Cursor #"+pnt2.X+"/"+pnt2.Y;
if(pressed) {
Point pnt = new Point((int)m.LParam);
Size rel_pos = new Size(parent.PointToClient(new Point(parent.Location.X, parent.Location.Y)));
//pnt = PointToClient(pnt);
pnt-=rel_pos;
if(!MouseinBtn(pnt)) {
pressed = false;
DrawButton();
}
} else {
if((GetAsyncKeyState(VK_LBUTTON)&(-32768))!=0) {
Point pnt = new Point((int)m.LParam);
Size rel_pos = new Size(parent.PointToClient(new Point(parent.Location.X, parent.Location.Y)));
//pnt = PointToClient(pnt);
pnt-=rel_pos;
if(MouseinBtn(pnt)) {
pressed = true;
DrawButton();
}
}
}
}
// Ignore Double-Clicks on the Traybutton
if(m.Msg==WM_NCLBUTTONDBLCLK) {
Point pnt = new Point((int)m.LParam);
Size rel_pos = new Size(parent.PointToClient(new Point(parent.Location.X, parent.Location.Y)));
pnt = parent.PointToClient(pnt);
pnt-=rel_pos;
if(MouseinBtn(pnt)) {
return;
}
}
#region NOT WORKING
// Button released and eventually clicked
if(m.Msg==WM_LBUTTONUP)
{
ReleaseCapture();
captured = false;
if(pressed) {
pressed = false;
DrawButton();
Point pnt = new Point((int)m.LParam);
Size rel_pos = new Size(parent.PointToClient(new Point(parent.Location.X, parent.Location.Y)));
pnt-=rel_pos;
if(MouseinBtn(pnt)) {
//TrayButton_clicked();
EventArgs e = new EventArgs();
if (MinTrayBtnClicked != null)
MinTrayBtnClicked(this, e);
return;
}
}
}
#endregion
// Clicking the Button - Capture the Mouse and await until the Uses relases the Button again
if(m.Msg==WM_NCLBUTTONDOWN) {
//MessageBox.Show("clicked");
Point pnt = new Point((int)m.LParam);
Size rel_pos = new Size(parent.PointToClient(new Point(parent.Location.X, parent.Location.Y)));
pnt = parent.PointToClient(pnt);
pnt-=rel_pos;
if(MouseinBtn(pnt)) {
MessageBox.Show("clicked");
pressed = true;
DrawButton();
SetCapture((int)parent.Handle);
captured = true;
return;
}
}
// Drawing the Button and getting the Real Size of the Window
if(m.Msg == WM_ACTIVATE || m.Msg==WM_SIZE || m.Msg==WM_SYNCPAINT || m.Msg==WM_NCACTIVATE || m.Msg==WM_NCCREATE || m.Msg==WM_NCPAINT || m.Msg==WM_NCACTIVATE || m.Msg==WM_NCHITTEST || m.Msg==WM_PAINT) {
if(m.Msg==WM_SIZE) wnd_size = new Size(new Point((int)m.LParam));
DrawButton();
}
base.WndProc(ref m);
}
#endregion
#region Button-Specific Functions
public bool MouseinBtn(Point click) {
int btn_width = GetSystemMetrics(SM_CXSIZE);
int btn_height = GetSystemMetrics(SM_CYSIZE);
Size btn_size = new Size(btn_width, btn_height);
Point pos = new Point(wnd_size.Width - 3 * btn_width - 12 - (btn_width - 18)+7, 6+4);
return click.X>=pos.X && click.X<=pos.X+btn_size.Width &&
click.Y>=pos.Y && click.Y<=pos.Y+btn_size.Height;
}
public void DrawButton() {
Graphics g = Graphics.FromHdc((IntPtr)GetWindowDC((int)parent.Handle)); //m.HWnd));
DrawButton(g, pressed);
}
public void DrawButton(Graphics g, bool pressed) {
int btn_width = GetSystemMetrics(SM_CXSIZE);
int btn_height = GetSystemMetrics(SM_CYSIZE);
//Point pos = new Point(wnd_size.Width-3*btn_width-12-(btn_width-18),6);
Point pos = new Point(wnd_size.Width - 3 * btn_width - 12 - (btn_width - 18)+7, 6+4);
// real button size
btn_width-=2;
btn_height-=4;
Color light = SystemColors.ControlLightLight;
Color icon = SystemColors.ControlText;
Color background = SystemColors.Control;
Color shadow1 = SystemColors.ControlDark;
Color shadow2 = SystemColors.ControlDarkDark;
Color tmp1, tmp2;
if(pressed) {
tmp1 = shadow2;
tmp2 = light;
} else {
tmp1 = light;
tmp2 = shadow2;
}
g.DrawLine(new Pen(tmp1),pos, new Point(pos.X+btn_width-1,pos.Y));
g.DrawLine(new Pen(tmp1),pos, new Point(pos.X,pos.Y+btn_height-1));
if(pressed) {
g.DrawLine(new Pen(shadow1),pos.X+1, pos.Y+1, pos.X+btn_width-2, pos.Y+1);
g.DrawLine(new Pen(shadow1),pos.X+1, pos.Y+1, pos.X+1, pos.Y+btn_height-2);
} else {
g.DrawLine(new Pen(shadow1),pos.X+btn_width-2, pos.Y+1, pos.X+btn_width-2, pos.Y+btn_height-2);
g.DrawLine(new Pen(shadow1),pos.X+1, pos.Y+btn_height-2, pos.X+btn_width-2, pos.Y+btn_height-2);
}
g.DrawLine(new Pen(tmp2),pos.X+btn_width-1, pos.Y+0, pos.X+btn_width-1, pos.Y+btn_height-1);
g.DrawLine(new Pen(tmp2),pos.X+0, pos.Y+btn_height-1, pos.X+btn_width-1, pos.Y+btn_height-1);
g.FillRectangle(new SolidBrush(background),pos.X+1+Convert.ToInt32(pressed), pos.Y+1+Convert.ToInt32(pressed), btn_width-3,btn_height-3);
#region Added Code
g.FillRectangle(new SolidBrush(icon),pos.X+(float)0.5625*btn_width+Convert.ToInt32(pressed),pos.Y+(float)0.6428*btn_height+Convert.ToInt32(pressed),btn_width*(float)0.1875,btn_height*(float)0.143);
g.DrawImage(Image.FromFile("red_Pushpin.jfif"),new Rectangle( pos,new Size(btn_width,btn_height)));
#endregion
}
#endregion
}
}
This is code for main
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
This is the screenshot of the winform.
EDIT:
Upon investigation I found that WM_LBUTTONUP(Mouse left button up) is not triggered. Or it is not coming inside WndProc.
EDIT1:
It seems there was a problem in brackets.I changed the if conditions into switch case. This somewhat cleared the "Mouse left button up" problem. Now it is properly triggering. The remaining problem is mouse points based on parent form.

Inserting a control into another control at specific location?

I am working with win forms. I have two forms : my main form, Form1 and a Form I made called TextBlock. I am making a text editor, where you can place text boxes around a page, and edit them (think word).
Here are my two forms.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
saveFileDialog.Filter = "Text File|*.txt";
var result = saveFileDialog.ShowDialog();
if (result == DialogResult.OK)
{
StreamWriter writer = new
StreamWriter(saveFileDialog.OpenFile());
writer.Write(tb_Main.Text);
writer.Dispose();
writer.Close();
}
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
}
private void button_tb_Click(object sender, EventArgs e)
{
TextBlock tb_edit = new TextBlock();
tb_edit.Text = "New Text Block";
// tb_edit.Multiline = true;
// tb_edit.Size = new Size(100,100);
// tb_edit.MinimumSize = new Size(50, 50);
tb_edit.form1 = this;
tb_edit.TopLevel = false;
tb_edit.btn_accepttb.BringToFront();
tb_Main.Controls.Add(tb_edit);
tb_edit.Show();
tb_edit.BringToFront();
}
}
and my custom form here:
public partial class TextBlock : Form
{
public Form1 form1;
public TextBlock()
{
InitializeComponent();
}
private void btn_accepttb_Click(object sender, EventArgs e)
{
TextBox tb_edit = new TextBox();
tb_edit.Text = "New Text Block";
tb_edit.Multiline = true;
tb_edit.Size = this.Size;
int dif = form1.tb_Main.Lines.Count()*(int)tb_edit.Font.Size;
Point loca = new Point(this.Location.X,this.Location.Y+dif);
tb_edit.Location = this.Location;
form1.tb_Main.Controls.Add(tb_edit);
tb_edit.Show();
tb_edit.BringToFront();
form1.tb_Main.Controls.Remove(this);
}
}
What it does: it makes a copy my TextBlock, for placement and resizing purposes. when you have it where you want and how big you want, you click the button, and it replaces itself with a regular textbox, of that size, in that position.
What I want it to do: currently it is working with one exception. I am adding it to the controls, of tb_Main (my main textbox, it takes up the whole form) in Form1, it shows up. its the right size, except when i fill tb_Main with text, and i scroll, the new textbox stays where it is while its parent scrolls behind it.
Question : if I scroll down into my document and decide I want this textbox here, how do I ensure its position relative to the scroll of the textbox im putting it in. so when I scroll on, it basically stays embedded in the page where I put it (when I say "page" im referring to my tb_Main).
It is better to create a richtextbox instead of textbox. It has all capabilities of textbox and way more. This example works only with a richtextbox.
Subclassed richtextbox:
class MyRichTextBox : RichTextBox {
[DllImport( "user32.dll" )]
private static extern bool GetScrollInfo( IntPtr hwnd, SBOrientation fnBar,
ref SCROLLINFO lpsi );
[StructLayout( LayoutKind.Sequential )]
private struct SCROLLINFO {
public uint cbSize;
public ScrollInfoMask fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
private enum ScrollInfoMask : uint {
SIF_RANGE = 0x1,
SIF_PAGE = 0x2,
SIF_POS = 0x4,
SIF_DISABLENOSCROLL = 0x8,
SIF_TRACKPOS = 0x10,
SIF_ALL = ( SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS ),
}
private enum SBOrientation : int {
SB_HORZ = 0x0,
SB_VERT = 0x1,
}
private const int WM_VSCROLL = 0x115;
private const int SB_LINEUP = 0;
private const int SB_LINEDOWN = 1;
private const int SB_PAGEUP = 2;
private const int SB_PAGEDOWN = 3;
private const int SB_THUMBPOSITION = 4;
private const int SB_THUMBTRACK = 5;
private const int SB_TOP = 6;
private const int SB_BOTTOM = 7;
private const int SB_ENDSCROLL = 8;
private bool isThumbTrack = false;
private TextBox childTextbox = null;
private int scrollPos = 0;
public MyTextBox( TextBox textbox ) { //here you pass the child textbox you want to scroll
childTextbox = textbox;
}
protected override void OnVScroll( EventArgs e ) {
if( childTextbox == null ) { base.OnVScroll( e ); return; }
SCROLLINFO si = new SCROLLINFO();
si.cbSize = (uint)Marshal.SizeOf( si );
si.fMask = ScrollInfoMask.SIF_ALL;
GetScrollInfo( this.Handle, SBOrientation.SB_VERT, ref si );
//there is a difference when the user uses the thumb to get scroll pos.
//When user uses the thumb we get *nTrackPos* otherwise *nPos*
if( isThumbTrack == true ) {
childTextbox.Location = new Point( childTextbox.Location.X, childTextbox.Location.Y -
(si.nTrackPos - scrollPos ) );
isThumbTrack = false;
scrollPos = si.nTrackPos;
}
else {
childTextbox.Location = new Point( childTextbox.Location.X, childTextbox.Location.Y -
( si.nPos - scrollPos ) );
scrollPos = si.nPos;
}
base.OnVScroll( e );
}
protected override void WndProc( ref Message m ) {
int wParam;
if(m.Msg == WM_VSCROLL ) {
wParam = m.WParam.ToInt32();
wParam &= 0xFFFF; //get low 16 bits of wParam
//Check if user is using the thumb to scroll
if( wParam == SB_THUMBTRACK ) {
isThumbTrack = true;
}
}
base.WndProc( ref m );
}
}

How to place a windows form in screen coords and close it on command?

I am working on a c# console program on Windows to move a window, as part of this I am trying to create a transparent box at the target destination to give the user a visual confirmation of the new location. I am currently facing 2 main problems:
I want to place the form in screen coordinates
I want to be able to close the window after the user has confirmed their decision.
Currently I have code in a library that my console client is calling that does this:
public static void CreateBox(Rect rectangle)
{
Form f = new Form();
f.Name = BoxName;
f.BackColor = Color.Blue;
f.FormBorderStyle = FormBorderStyle.None;
f.Bounds = new System.Drawing.Rectangle(rectangle.Left, rectangle.Top, rectangle.Right - rectangle.Left, rectangle.Bottom - rectangle.Top);
f.TopMost = true;
f.Opacity = 0.5;
Application.EnableVisualStyles();
Task.Run(() => Application.Run(f));
}
And after searching some questions on here I have come up with this to attempt to close the form later:
public static void RemoveBox()
{
Form f = Application.OpenForms[BoxName];
if (f != null)
{
f.Close();
}
}
This is throwing an exception as its coming from a different thread, how can I close this window, and how can I place it in screen coordinates exactly where it should go?
EDIT:
I am now using this to attempt to find the box to move/close it unsuccessfully:
public static void CreateBox(Rect rectangle)
{
Form f = new Form();
f.Name = BoxName;
f.BackColor = Color.AliceBlue;
f.FormBorderStyle = FormBorderStyle.None;
f.TopMost = true;
f.Opacity = 0.3;
Application.EnableVisualStyles();
Task.Run(() =>
{
Application.Run(f);
});
MoveBox(rectangle);
}
public static void RemoveBox()
{
IntPtr hWnd = FindBox(new TimeSpan(0, 0, 1));
var proc = Process.GetProcesses().Where(p => p.Handle == hWnd).Single();
if (proc == null)
{
return;
}
proc.Kill();
}
public static void MoveBox(Rect rect)
{
IntPtr hWnd = FindBox(new TimeSpan(0, 0, 1));
MoveWindow(hWnd, rect);
}
private static IntPtr FindBox(TimeSpan timeout)
{
DateTime time = DateTime.Now;
IntPtr hWnd = IntPtr.Zero;
while(DateTime.Now < time.Add(timeout) || hWnd != IntPtr.Zero)
{
hWnd = FindWindowByCaption(IntPtr.Zero, BoxName);
}
return hWnd;
}
Issues with this:
I can't let the FindBox call take long at all because my goal is to make this box appear and snap windows to them as the user drags them and needs to move as they move it around the desktop.
the p.Handle == hWnd check in the RemoveBox function throws and access denied exception.
If you want do just get it working put:
Form.CheckForIllegalCrossThreadCalls = false;
Into your constructor, I do not recommend it however, since it will make the whole Form no longer be thread safe.
EDIT : Here is a quick test I've done using a WinForm. It should run fine on console as well.
public partial class Form1 : Form
{
//Globals
Form f;
Task t;
public Form1()
{
InitializeComponent();
f = new Form();
f.Name = "testForm";
f.BackColor = Color.Blue;
f.FormBorderStyle = FormBorderStyle.None;
f.Bounds = new System.Drawing.Rectangle(0, 0, 100, 100);
f.TopMost = true;
f.Opacity = 0.5;
Application.EnableVisualStyles();
//f.Show();
t = Task.Run(() => Application.Run(f));
}
private void btn_CloseForm_Click(object sender, EventArgs e)
{
if (f.InvokeRequired)
{
this.Invoke(new CloseForm_Delegate(CloseForm));
}
}
delegate void CloseForm_Delegate();
private void CloseForm()
{
f.Close();
}
private void btn_MoveForm_Click(object sender, EventArgs e)
{
if (f.InvokeRequired)
{
//set form position here
this.Invoke(new MoveForm_Delegate(MoveForm), new Point(0, 0));
}
}
delegate void MoveForm_Delegate(Point p);
private void MoveForm(Point p)
{
f.Location = p;
}
}

Transferring graphics between threads using semaphores and buffers in C#

My english isn't very good, its very simple written so sorry in advance...
I have seen similar questions to this but not using graphics.
What I am trying to do is set up a simple circuit display, where a dot starts at the ButtonPanel, which whilst the button is clicked and green, will release more dots, I have used semaphores and buffers to control whether it goes into the next panel, the idea is the dot will do a circuit around the panels (in a clockwise motion) until the (for now) hard-coded limit makes the origin stop sending out dots.
I am confused with the nature of buffers and semaphores I guess, but as much reading as I have done on them, I still fail to get it to behave how I want...
The system when it leaves the first panel, will set off a dot in all 4 WaitPanelThreads at the same time, which is not what I want.
I have tried just adding more semaphores but this doesn't do any good. I built my own semaphores and buffers to try and understand what they are doing but I am unable to proceed with this program. I have spent ages trying to get the system to behave correctly but I am stumped, if any of this didn't make much sense because of my english, ask and I will try to phrase it better.
Below is all the code I am written for this, I am pretty sure the problem is where I construct my WaitPanelThreads. Any assistance is appreciated, I'm trying to teach myself this.
Felix
EDIT: I must also comment that the below code will all work if you slam it into visual studio, you may need to include two references if you want to see how it is set up
using System;
using System.Windows.Forms;
using System.Threading;
using System.ComponentModel;
using System.Collections;
using System.Data;
using System.Drawing;
public class Form1 : Form
{
private Container components = null;
private ButtonPanelThread p1;
private Button btn1;
private WaitPanelThread p2, p3, p4, p5;
private Thread thread1, thread2, thread3, thread4, thread5;
private Semaphore semaphore, semaphore1;
private Buffer buffer, buffer1;
private Thread semThread, semThread1;
private Thread buffThread, buffThread1;
private Panel pnl1, pnl2, pnl3, pnl4, pnl5;
public Form1()
{
InitializeComponent();
semaphore = new Semaphore();
semaphore1 = new Semaphore();
buffer = new Buffer();
buffer1 = new Buffer();
p1 = new ButtonPanelThread(new Point(40, 10),
30, true, pnl1,
Color.Blue,
semaphore,
buffer,
btn1);
p2 = new WaitPanelThread(new Point(10, 10),
80, false, 2, pnl2,
Color.White,
semaphore,
buffer);
p3 = new WaitPanelThread(new Point(10, 10),
77, false, 3, pnl3,
Color.White,
semaphore,
buffer);
p4 = new WaitPanelThread(new Point(250, 10),
77, false, 4, pnl4,
Color.White,
semaphore,
buffer);
p5 = new WaitPanelThread(new Point(10, 250),
77, false, 1, pnl5,
Color.White,
semaphore,
buffer);
semThread = new Thread(new ThreadStart(semaphore.Start));
semThread1 = new Thread(new ThreadStart(semaphore1.Start));
buffThread = new Thread(new ThreadStart(buffer.Start));
buffThread1 = new Thread(new ThreadStart(buffer1.Start));
thread1 = new Thread(new ThreadStart(p1.Start));
thread2 = new Thread(new ThreadStart(p2.Start));
thread3 = new Thread(new ThreadStart(p3.Start));
thread4 = new Thread(new ThreadStart(p4.Start));
thread5 = new Thread(new ThreadStart(p5.Start));
this.Closing += new CancelEventHandler(this.Form1_Closing);
semThread.Start();
semThread1.Start();
buffThread.Start();
buffThread1.Start();
thread1.Start();
thread2.Start();
thread3.Start();
thread4.Start();
thread5.Start();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.Text = "Circuit movements";
this.Size = new System.Drawing.Size(900, 700);
this.BackColor = Color.LightGray;
this.pnl1 = new Panel();
this.pnl1.Location = new Point(100, 100);
this.pnl1.Size = new Size(260, 30);
this.pnl1.BackColor = Color.White;
this.btn1 = new Button();
this.btn1.Size = new Size(30, 30);
this.btn1.BackColor = Color.Pink;
this.btn1.Location = new System.Drawing.Point(0, 0);
this.pnl2 = new Panel();
this.pnl2.Location = new Point(350, 100);
this.pnl2.Size = new Size(260, 30);
this.pnl2.BackColor = Color.White;
this.pnl3 = new Panel();
this.pnl3.Location = new Point(580, 100);
this.pnl3.Size = new Size(30, 260);
this.pnl3.BackColor = Color.White;
this.pnl4 = new Panel();
this.pnl4.Location = new Point(350, 360);
this.pnl4.Size = new Size(260, 30);
this.pnl4.BackColor = Color.White;
this.pnl5 = new Panel();
this.pnl5.Location = new Point(350, 100);
this.pnl5.Size = new Size(30, 260);
this.pnl5.BackColor = Color.White;
this.Controls.Add(pnl1);
this.Controls.Add(pnl2);
this.Controls.Add(pnl3);
this.Controls.Add(pnl4);
this.Controls.Add(pnl5);
this.pnl1.Controls.Add(btn1);
// Wire Closing event.
this.Closing += new CancelEventHandler(this.Form1_Closing);
}
private void Form1_Closing(object sender, CancelEventArgs e)
{
// Environment is a System class.
// Kill off all threads on exit.
Environment.Exit(Environment.ExitCode);
}
}// end class form1
public class Buffer
{
private Color planeColor;
private bool empty = true;
public void Read(ref Color planeColor)
{
lock (this)
{
// Check whether the buffer is empty.
if (empty)
Monitor.Wait(this);
empty = true;
planeColor = this.planeColor;
Monitor.Pulse(this);
}
}
public void Write(Color planeColor)
{
lock (this)
{
// Check whether the buffer is full.
if (!empty)
Monitor.Wait(this);
empty = false;
this.planeColor = planeColor;
Monitor.Pulse(this);
}
}
public void Start()
{
}
}// end class Buffer
public class Semaphore
{
private int count = 0;
public void Wait()
{
lock (this)
{
while (count == 0)
Monitor.Wait(this);
count = 0;
}
}
public void Signal()
{
lock (this)
{
count = 1;
Monitor.Pulse(this);
}
}
public void Start()
{
}
}// end class Semaphore
public class ButtonPanelThread
{
private Point origin;
private int delay;
private Panel panel;
private bool westEast;
private Color colour;
private Point plane;
private int xDelta;
private int yDelta;
private Semaphore semaphore;
private Buffer buffer;
private Button btn;
private bool locked = true;
public ButtonPanelThread(Point origin,
int delay,
bool westEast,
Panel panel,
Color colour,
Semaphore semaphore,
Buffer buffer,
Button btn)
{
this.origin = origin;
this.delay = delay;
this.westEast = westEast;
this.panel = panel;
this.colour = colour;
this.plane = origin;
this.panel.Paint += new PaintEventHandler(this.panel_Paint);
this.xDelta = westEast ? +10 : -10;
this.yDelta = 0;
this.semaphore = semaphore;
this.buffer = buffer;
this.btn = btn;
this.btn.Click += new System.
EventHandler(this.btn_Click);
}
private void btn_Click(object sender,
System.EventArgs e)
{
locked = !locked;
this.btn.BackColor = locked ? Color.Pink : Color.LightGreen;
lock (this)
{
if (!locked)
Monitor.Pulse(this);
}
}
public void Start()
{
Color signal = Color.Red;
Thread.Sleep(delay);
for (int k = 1; k <= 30; k++)
{
this.zeroPlane();
panel.Invalidate();
lock (this)
{
while (locked)
{
Monitor.Wait(this);
}
}
for (int i = 1; i <= 20; i++)
{
this.movePlane(xDelta, yDelta);
Thread.Sleep(delay);
panel.Invalidate();
}
semaphore.Wait();
buffer.Write(this.colour);
}
this.colour = Color.Gray;
panel.Invalidate();
}
private void zeroPlane()
{
plane.X = origin.X;
plane.Y = origin.Y;
}
private void movePlane(int xDelta, int yDelta)
{
plane.X += xDelta; plane.Y += yDelta;
}
private void panel_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
SolidBrush brush = new SolidBrush(colour);
g.FillRectangle(brush, plane.X, plane.Y, 10, 10);
brush.Dispose(); // Dispose graphics resources.
g.Dispose(); //
}
}// end class ButtonPanelThread
public class WaitPanelThread
{
private Point origin;
private int delay;
private Panel panel;
private bool westEast;
private int dir;
/*
* dir options:
* 1 - north;
* 2 - east;
* 3 - south;
* 4 - west.
*/
private Color colour;
private Point plane;
private int xDelta;
private int yDelta;
private Semaphore semaphore;
private Buffer buffer;
public WaitPanelThread(Point origin,
int delay,
bool westEast,
int dir,
Panel panel,
Color colour,
Semaphore sem,
Buffer buff)
{
this.origin = origin;
this.delay = delay;
this.westEast = westEast;
this.dir = dir;
this.panel = panel;
this.colour = colour;
this.plane = origin;
this.panel.Paint += new PaintEventHandler(this.panel_Paint);
switch (dir)
{
case 1:
this.xDelta = 0;
this.yDelta = -10;
break;
case 2:
this.xDelta = +10;
this.yDelta = 0;
break;
case 3:
this.xDelta = 0;
this.yDelta = +10;
break;
case 4:
this.xDelta = -10;
this.yDelta = 0;
break;
}
this.semaphore = sem;
this.buffer = buff;
}
public void Start()
{
this.colour = Color.White;
for (int k = 1; k <= 40; k++)
{
semaphore.Signal();
this.zeroPlane();
buffer.Read(ref this.colour);
for (int i = 1; i <= 20; i++)
{
panel.Invalidate();
this.movePlane(xDelta, yDelta);
Thread.Sleep(delay);
}
this.colour = Color.White;
panel.Invalidate();
}
this.colour = Color.Gray;
panel.Invalidate();
}
private void zeroPlane()
{
plane.X = origin.X;
plane.Y = origin.Y;
}
private void movePlane(int xDelta, int yDelta)
{
plane.X += xDelta; plane.Y += yDelta;
}
private void panel_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
SolidBrush brush = new SolidBrush(colour);
if (dir == 2 || dir == 4)
{
g.FillRectangle(brush, plane.X, plane.Y, 10, 10);
}
if (dir == 1 || dir == 3)
{
g.FillRectangle(brush, plane.X, plane.Y, 10, 10);
}
brush.Dispose(); // Dispose graphics resources.
g.Dispose(); //
}
}// end class WaitPanelThread
public class Driver
{
public static void Main()//
{
Application.Run(new Form1());
}
}// end class TheOne

Categories