How to move mouse cursor using C# using while infinite loop? - c#

I found a solution to move the cursor via this URL in my Windows-Form app.
How to move mouse cursor using C#?
but as i want to run infinitely but with a break so that when i want to stop it, it should stop here is what i'm trying to achieve.
private void btnMove_Click(object sender, EventArgs e)
{
//int i = 0;
while (true)
{
//i++;
Cursor = new Cursor(Cursor.Current.Handle);
Cursor.Position = new Point(Cursor.Position.X - 40, Cursor.Position.Y - 40);
Thread.Sleep(5000);
Cursor.Position = new Point(Cursor.Position.X + 40, Cursor.Position.Y + 40);
}
//Task t = new Task(() =>
//{
//});
//t.Start();
}
It works but freezes my code. I just want to run it, and whenever i want to stop it, it should stop not freeze.

Ultimately, the answer here is: "don't".
Windows forms are based on a message pump. If you have an event-handler from one message (like "click") that loops forever, it will never get around to processing other messages (like "draw"), so: your app is now unresponsive.
Instead of an infinite loop: use a Timer, and move the position in the callback. In this case, a System.Windows.Forms.Timer would be most appropriate.

Related

Draw an arrow between 2 positions in an interval of 4 seconds in c#

I want to make an arrow in c#, which goes from A position to B position in 5 seconds for example. I want to put a map image in the form and when i click on a button i want to draw an arrow from A position to B position in an interval of seconds. i have made an arrow when it is in a horizontal position, but when i try to make it oblique it draws me a triangle instead of an arrow and i don't know how to fix it.
here i made an arrow from a position 12 with a width of 300
and i try to make the same with an oblique arrow but when i put different positions it draws me a triangle not an arrow.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace WindowsFormsApp4
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(400, 273);
this.Text = "";
this.Resize += new System.EventHandler(this.Form1_Resize);
this.Paint += new System.Windows.Forms.PaintEventHandler(this.Form1_Paint);
}
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillRectangle(Brushes.White, this.ClientRectangle);
Pen p = new Pen(Color.Black, 5);
p.StartCap = LineCap.Round;
for(int i=1; i<=300;i++)
{
System.Threading.Thread.Sleep(2);
g.DrawLine(p, 12, 30, i, 30);
Cursor.Current = Cursors.Default;
}
p.EndCap = LineCap.ArrowAnchor;
g.DrawLine(p, 12, 30, 310, 30);
p.Dispose();
}
private void Form1_Resize(object sender, System.EventArgs e)
{
Invalidate();
}
}
}
The fundamental problem with your code is that you are doing the entire animation loop inside the Paint event handler. This means that the window is never clear out between each line you draw, so you get all of the copies of the line you're drawing, start to finish, laid on top of each other in the same view.
It is not clear from your question exactly what you expect to see on the screen. However, another potential problem with your code is that the moving end point of the line does not start at the start point of the line, but rather at a point with the same Y coordinate where you want the line to end. This means that the arrow end of the line traverses a horizontal line leading to the final end point, rather than gradually extending from the start point of the line.
There is also the minor point that you seem to be confused about what the DrawLine() method does. You state that the width of your line is 300, but in fact the second argument of the DrawLine() method is just another point. The width of the line is defined by the Pen you use to draw the line. The width of the box containing the line is defined by the start and end point, but in this case is not 300, but rather (at the final length of the line) the difference between your start X coordinate and end X coordinate (i.e. 288).
The fundamental problem described above can be addressed by running a loop outside of the Paint event handler, which updates values that describe the line, and then call Invalidate() so that the Paint event handler can be called to draw just the current state of the animation.
On the assumption that what you really wanted was for a line to extend out from the start point, rather than traverse a horizontal line, the example I show below implements the animation that way as well, in addition to fixing the fundamental issue. I did nothing to change the length or width of the line.
public Form1()
{
InitializeComponent();
this.DoubleBuffered = true;
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(400, 273);
this.Resize += new System.EventHandler(this.Form1_Resize);
this.Paint += new System.Windows.Forms.PaintEventHandler(this.Form1_Paint);
var task = AnimateLine();
}
private readonly Point _lineStart = new Point(12, 30);
private readonly Point _lineFinalEnd = new Point(300, 60);
private const int _animateSteps = 300;
private Point _lineCurrentEnd;
private bool _drawArrow;
private async Task AnimateLine()
{
Size size = new Size(_lineFinalEnd) - new Size(_lineStart);
for (int i = 1; i <= _animateSteps; i++)
{
await Task.Delay(2);
Size currentSize = new Size(
size.Width * i / _animateSteps, size.Height * i / _animateSteps);
_lineCurrentEnd = _lineStart + currentSize;
Invalidate();
}
_drawArrow = true;
Invalidate();
}
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
using (Pen p = new Pen(Color.Black, 5))
{
p.StartCap = LineCap.Round;
p.EndCap = _drawArrow ? LineCap.ArrowAnchor : p.EndCap;
g.DrawLine(p, _lineStart, _lineCurrentEnd);
}
}
Note that the repeated erasing and redrawing of the window would make the window flicker. This is a basic issue with any sort of animation, and the fix is to enable double-buffering for the window, hence the this.DoubleBuffered = true; statement added to the constructor.
Some other points worth mentioning:
The await Task.Delay() call is used so that the loop can yield the UI thread with each iteration of the loop, which allows the UI thread to raise the Paint event, as well as allows any other UI activity to still work during the animation. You can find lots more information about that C# feature in the How and When to use async and await article, and of course by reading the documentation.
Whether you use Thread.Sleep() or Task.Delay(), specifying a delay of 2 ms isn't very useful. The Windows thread scheduler does not schedule threads to that degree of precision. A thread that sleeps for 2 ms could be woken up as much as 50 ms later in the normal case, and even later if the CPU is under heavy load. Nor does a 2 ms delay provide a useful animation frame rate; that would be a 500 Hz refresh rate, which is easily 10x or more faster than the human brain needs in order to perceive a smooth animation.My example above does nothing to try to address this issue, but you should explore implementing the loop slightly differently, such that instead of the number of animation steps, you specify a reasonable animation interval (say, every 50 or 100 ms), make an attempt to delay that interval, but then use a Stopwatch to actually measure what the real delay was and compute the progress within the animation based on the actual time elapsed. This will allow you to have precise control over the total duration of the animation, as well as somewhat precise control over the refresh rate used for the animation.

C# Events do not trigger while GUI Thread is busy (drawing). How to solve?

Short intro to my program (Solved)
My program is taking an image and reveals tile after tile (guessing game).
I decided to go with fading in (manually) each tile.
Since I need the user to be able to interact with the GUI, I do the calculations and thread blocking things like Thread.Sleep on another thread. I also added an OnClickEvent to a picturebox (which overlays the image to reveal). => if someone guessed the image the user can click on the picturebox to fully reveal the image. (For fading I am clipping a region of the picturebox and then clear it with a color. The color's alpha value is decreasing step by step until it is fully transparent. Then I go to the next region.)
Incoming problem
After each iteration I need to refresh the picturebox so that it displays the new "situation". Therefore I have to invoke the action on the GUI Thread.
Now if the time between each refresh becomes too short like 10 ms the GUI seems to be so busy refreshing/drawing the image that it doesn't fire my OnClickEvent anymore.
The reveal function
public void Reveal(int step, int intervalFading, int intervalNextTile)
{
StopThread = false; // Changed by the OnClickEvent
Graphics grx = Graphics.FromImage(Overlay.Image);
step = 255 / step;
foreach (RectangleF R in AreasShuffled)
{
grx.Clip = new Region(R);
for (int i = 255; i >= 0; i-=step) //Fading out loop
{
Thread.Sleep(intervalFading); //if intervalFading < 15 GUI is too busy
if (StopThread) //Condition if someone guessed correctly
{
grx.ResetClip();
grx.Clear(Color.FromArgb(0, 0, 0, 0)); //revealing the image
ParentControl.BeginInvoke((Action)(() => Overlay.Refresh()));
grx.Dispose();
return;
}
grx.Clear(Color.FromArgb(i, 0, 0, 0)); //Clearing region
ParentControl.BeginInvoke((Action)(() => Overlay.Refresh())); //Redrawing image
}
grx.Clear(Color.FromArgb(0, 0, 0, 0));
ParentControl.BeginInvoke((Action)(() => Overlay.Refresh()));
Thread.Sleep(intervalNextTile);
}
grx.ResetClip();
grx.Clear(Color.FromArgb(0, 0, 0, 0));
ParentControl.BeginInvoke((Action)(() => Overlay.Refresh()));
grx.Dispose();
}
Solution
As adviced I used async tasks. Here is the updated function. (Yeah, I didn't update the grx.dispose() ^^)
public async Task Reveal(int step, int intervalFading, int intervalNextTile)
{
taskIsRunning = true;
stopTask = false; // Changed by the OnClickEvent
Graphics grx = Graphics.FromImage(Overlay.Image);
step = 255 / step;
foreach (RectangleF R in AreasShuffled)
{
grx.Clip = new Region(R);
for (int i = 255; i >= 0; i -= step)
{
await Task.Delay(intervalFading);
if (stopTask)
{
grx.ResetClip();
grx.Clear(Color.FromArgb(0, 0, 0, 0));
Overlay.Refresh();
grx.Dispose();
taskIsRunning = false;
return;
}
grx.Clear(Color.FromArgb(i, 0, 0, 0));
Overlay.Refresh();
}
grx.Clear(Color.FromArgb(0, 0, 0, 0));
Overlay.Refresh();
await Task.Delay(intervalNextTile);
}
grx.ResetClip();
grx.Clear(Color.FromArgb(0, 0, 0, 0));
Overlay.Refresh();
grx.Dispose();
taskIsRunning = false;
}
and the calling function that checks whether the task is running or not
private void pictureBoxOverlay_Click(object sender, EventArgs e)
{
if (UCM != null && UCM.taskIsRunning) //if it is running the function is being notified
{ //and reveals the image.
UCM.stopTask = true;
}
else //makes sure that the user has to click again to start with the next image
{
if (index < Images.Count - 1)
{
PrepareNextImage();
UCM.Reveal(Properties.Settings.Default.steps, Properties.Settings.Default.fadeInterval, Properties.Settings.Default.nextTileInterval);
}
else
MessageBox.Show("End of presentation.");
}
}
Thanks for your help ;)
There is a short answer here. Don't call Thread.Sleep on the UI thread if you expect your UI to be responsive
Update
It appears that you are not running your animation code of the UI thread. Good stuff! So what could be the problem? I suspect the 4 calls to BeginInvoke many times per second is causing the application message pump to flood with invoke events and delay the GUI updates while servicing them.
Fix this by reducing the number of invokes. Do all your updates in a single invoke per interval.
This short example invokes back to the calling context only once each interval. You should call it from the UI.
async Task Animate(Control control, int interval)
{
while(true)
{
// this line causes the method to pause by queueing
// everything after await similarly to `BeginInvoke`
await Task.Delay(interval);
// all of this still happens on the UI thread
// increment control properties here
// check to see if the animation should end.
if (END STATE IS MET)
{
return;
}
}
}
As a side note - you are calling grx.Dispose a few times. It may be better to wrap the whole code block in using(grx){ }. This still works with async! How? Darkest magicks.

How to stop Paint events from stacking up

I'm creating an application to schedule different tasks. These are displayed by drawing rectangles on a panel. This has to be responsive. So I need to draw and invalidate on every size change. When I reach a maximum height of my planning panel it autoscrolls.
The problem is when I grab the scrollbar and start scrolling away for a bit, when I release the scrollbar my whole application and computer freezes.
Most likely this is due to the onpaint event being called on every little scroll and stacking up, leaving the application to hang until they are all done.
Now my question is: How would I be able to fix this? Possibly by keeping the paint event from being called multiple times, but how?
The method called by the paint event:
private void panelPlanning_Paint(object sender, PaintEventArgs e)
{
for (int i = 0; i < userList.Count; i++)
{
Label temp = new Label();
temp.Text = userList[i].Text;
temp.Width = panelUsers.Width;
temp.Height = 50;
temp.BorderStyle = BorderStyle.FixedSingle;
temp.Location = new Point(0, i * 50);
temp.TextAlign = ContentAlignment.MiddleCenter;
panelUsers.Controls.Add(temp);
foreach (FullTask task in taskList)
{
if (task.AssignedTo == userList[i].Text && task.StartDate != "" && task.DueDate != "")
{
DateTime start = DateTime.ParseExact(task.StartDate, "dd/MM/yyyy", CultureInfo.InvariantCulture);
DateTime end = DateTime.ParseExact(task.DueDate, "dd/MM/yyyy", CultureInfo.InvariantCulture);
Brush brush;
if (task.Priority == Enums.priorities[2])
{
brush = Brushes.Yellow;
}
else if (task.Priority == Enums.priorities[1])
{
brush = new SolidBrush(Color.FromArgb(255, 0, 80, 123));
}
else
{
brush = Brushes.Red;
}
panelPlanning.CreateGraphics().FillRectangle(brush, new Rectangle((start.Subtract(dtPickerStart.Value).Days + 1) * labelDaysWidth, i * 50, (end.Subtract(start).Days + 1) * labelDaysWidth, 25));
}
}
}
}
You are adding new Label controls on every single paint event. Don't do this:
// Label temp = new Label();
// temp.Text = userList[i].Text;
// temp.Width = panelUsers.Width;
// temp.Height = 50;
// temp.BorderStyle = BorderStyle.FixedSingle;
// temp.Location = new Point(0, i * 50);
// temp.TextAlign = ContentAlignment.MiddleCenter;
// panelUsers.Controls.Add(temp);
Also, use the e.Graphics object supplied by the argument, not CreateGraphics,
The following line of code should only be executed once:
panelUsers.Controls.Add(temp);
You are constantly adding a new instance of temp to the panelUsers control.
You have to trace which FullTask actually has to be displayed on the screen. This can be achieved by looking on Control.ClientRectangle and keeping in mind current scroll position.
In this way from the all set of the FullTasks to draw you will get only that subset of tasks that actually visible on the screen, and draw only those ones.
This is a correct way of dealing with drawable artifacts which is implemented in more or less all drawing frameworks 2D or even 3D.
There is also concept of tracing if some element is covered (or part of it is covered) by another element, so it will be "culed", but in your case this doesn't seem to matter.
Pay attention also on fact that you adding controls. Do not do that. If the amount of FullTasks is big, just draw them with Graphics object.
as already Described by #LarsTech, don't add controls in the paint event, and use the supplied Graphics object instead of your own ...
If you still face performance problems, consider painting on a bitmap whenever you change something, and only draw that bitmap onto the screen in onPaint

Gtk.Application.Invoke is not working

I am trying to build an application on MONO using Backgroundworker. My application is working fine when i use the sequential method. Basically my application draws some rectangles the on the drawing area on click of the button.
I am trying to compare the difference in execution time between sequential, using Backgroundworker and others.
I have a problem in using background worker, have a look at the following code,
static void DoWork (object sender, DoWorkEventArgs e)
{
Context ct = (Context)e.Argument;
house.DrawHouse rect = new house.DrawHouse ();
PointD p1, p2, p3, p4;
p1 = new PointD (55, 250);
p2 = new PointD (65, 250);
p3 = new PointD (65, 90);
p4 = new PointD (55, 90);
Gtk.Application.Invoke (delegate {
ct.MoveTo (p1);
ct.LineTo (p2);
ct.LineTo (p3);
ct.LineTo (p4);
ct.LineTo (p1);
ct.ClosePath ();
ct.Color = new Color (0, 0, 0);
ct.FillPreserve ();
ct.Color = new Color (255, 255, 255);
ct.Stroke ();
});
}
}
in the above code worker thread is creating creating the rectangle and giving to the GUI thread to print it. but it is throwing an error in the ct.MoveTo (p1);
Hope to hear from some one soon.
Are you sure you're calling that code in the GUI thread? To find out, you could use this tool.
Also, let me guess: you're running this on Windows, aren't you? I got this impression because AccessViolationExceptions happen more often on MS.NET than on Mono (somehow, the interoperability with the unmanaged world is less strict on Mono).
This has been brought up sometimes in bugs, but not yet fixed. In MS.NET these issues are normally fixed by applying proper calling conventions. An example is this, maybe you're missing some of this? (Or Cairo's binding? be sure to clone gtk-sharp and have a look at the DllImport being called; maybe you can spot a bug there and send a pull request to fix it?)
You are correct. Yes, I am running this on windows. and for the first point of yours, worker thread will start the task and create the rectangle and gives to GUI thread to print it on the screen. (Gtk.Application.Invoke (delegate { }); this will switch to GUI thread)
Have a look at the following code I am doing the same thing but not in perspective of painting but it does some time consuming calculation. The calculation which is time consuming is done by the worker thread and giving the result to GUI thread to print on the UI. This is absolutely working fine for me with out any error.
void DoWork (object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
var watch = Stopwatch.StartNew ();
for (int i = lowerLimit; i <= upperLimit; i++) {
int j = i;
var result = SumRootN (j);
Gtk.Application.Invoke (delegate {
textview15.Buffer.Text += "root " + j.ToString () + " = " + result.ToString () + Environment.NewLine;
});
worker.ReportProgress ((int)(((double)i / (double)upperLimit) * 100));
}
var time = watch.ElapsedMilliseconds;
textview20.Buffer.Text = "Background Worker: \n execution took " + time + " MS to \n complete the execution \n SLOW & RESPONSIVE";
}
But if i do the same thing on for painting it is giving me the error i attached above.
Please correct me if i am wrong
thanks for the reply knocte, I will have look at your response.

Best way to move an image across part of the screen?

I have a Silverlight WP7 app and an image on my page that I want to appear to slide across the screen. What is the best way of doing this? I wrote this real quick but the UI doesn't update until the entire method is done.
private void SpinImg(Image img, double left) {
for(int i = 1; i <= 10000; i++) {
img.Margin = new Thickness(left, img.Margin.Top + 1, 0, 0);
if(img.Margin.Top > 314) {
//move it to the top
img.Margin = new Thickness(left, -105, 0, 0);
}
int wait = 1000 / i;
Thread.Sleep(wait);
}
}
Use a Storyboard - this is hardware-acceleratable, and all occurs on the Render thread, so you'll see much better performance than trying to update position directly over and over.
Storyboard has the advantage of being time-based instead of frame-based, so it's easy to declare "I want the image to move from to in 0.5 seconds" and it will just happen.
Thread.Sleep will freeze ALL UI processing, use Dispatcher class.

Categories