After clicking a button, I am trying to:
Move a cursor to an input box.
Click the box.
Pause for x time.
Type "1".
Pause for x time.
Type "2" in the box.
... The end result should be "12" in the box.
When I run the program without any delays, it works as intended... albeit fast.
When I try to put a delay between the 1 and 2, I see no click or keyboard events get processed and appear on the form until the method finishes. The textbox changes will go from 2, to 4, to 6.
After textbox reaching 6, "12" will appear in the input box. This looks to me like for both times, no events take place until all the code within the method has finished executing.
With my limited knowledge, I am trying to understand:
Why does this happen?
How I can pause in the middle of the method between events without
freezing the entire form?
How can I have each event appear before the next event as opposed to all events happening when the method finishes?
public void DelayTimer(int interval)
{
Task.Delay(interval).Wait();
}
private void typeButton_Click(object sender, EventArgs e)
{
CursorMovementToPoint(PointToScreen(new Point((typeBox.Location.X + (typeBox.Size.Width / 2)), (typeBox.Location.Y + (typeBox.Size.Height / 2))))); // Moves cursor to center of Box
textBox1.Text = "1"; // Indicates event 1 is done
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, Cursor.Position.X, Cursor.Position.Y, 0, 0); // Clicks on box
textBox1.Text = "2"; // Indicates event 2 is done
DelayTimer(1000); // Pauses for 1 sec
textBox1.Text = "3"; // Indicates pause is done
keybd_event(KEY_1, KEY_1_SCAN, 0, 0); // Types "1" into Box
textBox1.Text = "4";
DelayTimer(1000); // Pauses for 1 sec between typing "1" and before typing "2"
textBox1.Text = "5";
keybd_event(KEY_2, KEY_2_SCAN, 0, 0); // Types "2" into Box
textBox1.Text = "6"; // Finished
}
The Button.Click handler is now declared async.
The DelayTimer() method was removed and a delay was placed directly in the typeButton_Click event handler.
This essentially changed the delay from Task.Delay(1000).Wait(); to await Task.Delay(1000);:
private async void typeButton_Click(object sender, EventArgs e)
{
CursorMovementToPoint(PointToScreen(new Point((typeBox.Location.X + (typeBox.Size.Width / 2)), (typeBox.Location.Y + (typeBox.Size.Height / 2)))));
SendM(MOUSEEVENTF.LEFTDOWN | MOUSEEVENTF.LEFTUP);
textBox1.Text = "1";
await Task.Delay(1500);
SendK(ScanCodeShort.KEY_1);
textBox1.Text = "2";
await Task.Delay(1500);
SendK(ScanCodeShort.KEY_2);
textBox1.Text = "3";
}
This resolved the issue: the events were not processed until the method was completed, the Form is not blocked and other events can be processing (now I can also close the Form while a delay is active).
I also added SendInput() to replace my previous methods to call mouse and keyboard events but that was unrelated to the delay issues. Works for me.
Related
I have a simple question but I could not find a direct answer anywhere
I have a C# program which executes a counter after the user pushes a "start" button. So 1, 2, 3, etc but the increments are performed with casual different time durations, i.e.
1 -> [4 seconds after] 2 -> [7 seconds after] 3 -> etc
and checked in the program every millisecond
I wanted to put an indication on the GUI to let the user know about the number reached
I was thinking to obtain it using a label for the word "Counter:"
// CounterLabel
//
this.CounterLabel.Anchor = ((System.Windows.Forms.AnchorStyles)
((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.CounterLabel.AutoSize = true;
this.CounterLabel.Location = new System.Drawing.Point(1090, 35);
this.CounterLabel.Name = "CounterLabel";
this.CounterLabel.Size = new System.Drawing.Size(58, 17);
this.CounterLabel.TabIndex = 52;
this.CounterLabel.Text = "Counter:";
but then I have two questions:
1) do I need a read-only text box to host the changing number
//
// CounterValue
//
this.CounterValue.Anchor = ((System.Windows.Forms.AnchorStyles)
((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.CounterValue.BackColor = System.Drawing.SystemColors.Control;
this.CounterValue.Location = new System.Drawing.Point(1149, 32);
this.CounterValue.Name = "CounterValue";
this.CounterValue.Size = new System.Drawing.Size(84, 22);
this.CounterValue.TabIndex = 53;
this.CounterValue.ReadOnly = true;
//this.CounterValue.Text += this.GetCounterValue();
or there is a way to have it using only the label?
2) how to perform the control to see if we have to update the UI? I mean, the value to display is checked every msec and I want the interface to be updated every msec as well [without using an "update" button to ask to show the value reached]
Thanks in advance to those who will try to help
1) Yes, you would need read-only text box next to the label.
2) add a method to the form as follows:
void UpdateCounter()
{
if (InvokeRequired)
{
BeginInvoke(new MethodInvoker(UpdateCounter));
return;
}
CounterValue.Text = Counter.ToString();
}
3) call this method everytime the counter is changed.
Or you could use a timer to call the UpdateCounter function.
another option is to use timer control it will automatically changelable value
timer1.star();
private void timer1_Tick(object sender, EventArgs e)
{
//Your code
}
I have a textbox and a listbox in my app. Also i have a text file with many players. What I want is whenever the user enters some text, look in the players file and add the matching players to the matching list which is the data source for the listbox. The problem is that it seems to be very slow and UI freezes a short time but it's quite annoying.
This is the code i have:
private void tb_playername_TextChanged(object sender, EventArgs e)
{
//This method is used to show user the options he can choose with the text he has entered
List<string> matching_players = new List<string>();
foreach (var item in all_players)
{
string player = item.f_name + " " + item.l_name;
if ((player.IndexOf(tb_playername.Text, StringComparison.OrdinalIgnoreCase) >= 0))
{
matching_players.Add("(" + item.rating + ") " + item.f_name + " " + item.l_name);
}
}
if (tb_playername.Text.Length >= 4)
{
matching_players.Sort();
matching_players.Reverse()
listbox_matchingplayers.DataSource = matching_players;
}
}
The problem is that you are doing a relatively time consuming task in the event handler. Event handlers operate on the same thread which takes care of rendering your application and handle any other visual aspects of it, so if this thread is busy, it will not be in a position to react to user input immediately, hence freezing.
The standard approach to this problem is to offload the time consuming tasks to a Background Worker. The background worker will operate in a new thread thus allowing the main thread to continue handling UI events. This example should hopefully put you on the right track when it comes to using a background worker.
EDIT: As per your question, what you could do would be to start searching only when a particular amount of characters is entered, for instance 3, this would reduce the amount of time the background worker runs. If the user keeps on typing, you could stop the current background worker if running and launch a new one.
The background worker will fire an event when finished. You could use the RunWorkerCompletedEventArgs.Result to then extract the returned list act upon it.
private async void tb_playername_TextChanged(object sender, EventArgs e)
{
var text = (sender as TextBox).Text;
// Check length of the text
if (string.IsNullOrEmpty(text) || text.Length <= 3)
return;
// Check timer to not process if user still typing, by measuring the key stoke time
...
// Filtering
List<string> matching_players = await PlayerFilter(text);
// Minimize listbox layout time
listbox_matchingplayers.SuspendLayout();
listbox_matchingplayers.DataSource = matching_players;
listbox_matchingplayers.ResumeLayout();
}
//Time consuming method
private async Task<List<string>> PlayerFilter(string text)
{
//This method is used to show user the options he can choose with the text he has entered
return matching_players;
}
For details of the user typing check wait for user to finish typing in a Text Box
well, I'm writing a bot that will use certain coordinates on screen and then will simulate 15 clicks on them (every click with different coordinates). I already made it work with coordinates I entered manually on the code but now I need a way to record those coordinates. What i wanted to do is: the users press a button, then the program shows a messagebox saying "right click the main menu", the user right clicks that and those coordinates will be recorded on an array, then the program will show a second messagebox asking to right click the next button and so... My problem is that I don't know how to make the method wait for the user to right click to continue.
I tested my program by making an event that would trigger everytime I right click and show the coordinates in a messagebox, using a UserActivityHook class with contains the event OnMouseActivity:
UserActivityHook actHook;
void MainFormLoad(object sender, System.EventArgs e)
{
actHook = new UserActivityHook();
// crate an instance with global hooks
// hang on events
actHook.OnMouseActivity+=new MouseEventHandler(MouseMoved);
}
public void MouseMoved(object sender, MouseEventArgs e)
{
if (e.Clicks > 0)
{
if (e.Button.Equals(MouseButtons.Right))
{
MessageBox.Show("X:" + e.X + " Y:" + e.Y);
}
}
}
I've trying to do something like:
private void button1_Click(object sender, EventArgs e)
{
RecordMacro(cords, 1);
}
public void RecordMacro(int coordinates[][], int slotnumber){
MessageBox.show("Right click main menu");
//saves coordinates on [0][0] and [0][1]
WaitForRightClickAndSaveCords(coordinates[][]);
MessageBox.show("Right click resupply button");
//saves coordinates on [1][0] and [1][1]
WaitForRightClickAndSaveCords(coordinates[][]);
...
}
I'm still a newbie and this is my first question in StackOverflow (I usually find an answer browsing here and don't have the need to ask myself) so I'll gladly accept any critics.
This is easiest to implement using C# 5.0's asynchrony model. We'll start out by creating a method that will generate a Task that will be completed when your conditions are met. It will do this by creating a TaskCompletionSource, adding a handler to the event, and marking the task as completed in the handler. Throw in some boilerplate code to make sure the handler is removed when done, return the Task from the completion source, and we're set:
public static Task<Point> WhenRightClicked(this UserActivityHook hook)
{
var tcs = new TaskCompletionSource<Point>();
MouseEventHandler handler = null;
handler = (s, e) =>
{
if (e.Clicks > 0 && e.Button == MouseButtons.Right)
{
tcs.TrySetResult(new Point(e.X, e.Y));
hook.OnMouseActivity -= handler;
}
};
hook.OnMouseActivity += handler;
return tcs.Task;
}
Now you can write:
public async void RecordMacro(int[][] coordinates, int slotnumber)
{
MessageBox.Show("Right click main menu");
Point mainMenuPosition = await actHook.WhenRightClicked();
MessageBox.Show("Right click resupply button");
Point resupplyButtonPosition = await actHook.WhenRightClicked();
}
There are a myriad number of ways to make this work, none of which you should remotely do. The reason is, that assuming you managed to stop execution of the thread with WaitForRightClick, you would be blocking the UI thread!
By doing that, you prevent the user from being able to click on the element you want (among lots of other reasons to never block the UI thread).
You could thread it or use asynchornous methods, as Servy suggests. This blocks the method (or executes it asynchronously) without blocking the UI thread itself.
While more complex, you could also queue up a bunch of object representing a "ClickTarget". Then, you would listen on the right-click event and record the associated coordinates with the current ClickTarget, dequeue to get the next instruction, and so on.
The complete code would be too long for StackOverflow, but to give you some ideas:
public class ClickTarget
{
Point Coordinates {get; set;}
String TargetName {get; set;}
}
Queue<ClickTarget> clickTargets;
//Obviously you instantiate/populate this somewhere
private void onRightClick(...)
{
ClickTarget target = clickTargets.Dequeue();
target.Coordinates = clickLocation;
MessageBox.Show("Please click on " + clickTargets.Peek().TargetName);
}
I have a code which reads information from a file and displays them to the user.... now i want to STOP my code after displaying the information to the the user and WAIT for the buttonpress which starts my event, because the button clears the textbox and returns some information the the user / admin ...
But i don't have any idea how to break my code from running and wait for the button to get pressed...
Thx a lot
StringBuilder strbuildsection = new StringBuilder();
StringBuilder strbuildbetreff = new StringBuilder();
StringBuilder strbuildinhalt = new StringBuilder();
StringBuilder strbuilduser = new StringBuilder(System.Environment.UserName);
StringBuilder strbuildusercheck = new StringBuilder();
foreach (string Ccat in this.ini.IniGetCategories())
{
string readval = ini.IniReadValue(Ccat, "Read");
string usercheckvar = (this.ini.IniReadValue(Ccat, "SpecifyUser"));
string user = System.Environment.UserName;
if (readval == "0")
{
if (usercheckvar == user || usercheckvar.Equals("All"))
{
strbuildsection.Append(Ccat + Environment.NewLine);
foreach (string cat in this.ini.IniGetKeys(Ccat))
{
strbuildinhalt.Clear();
strbuildusercheck.Clear();
strbuildbetreff.Clear();
strbuildbetreff.Append(this.ini.IniReadValue(Ccat, "Betreff") + Environment.NewLine);
strbuildinhalt.Append(this.ini.IniReadValue(Ccat, "Inhalt") + Environment.NewLine);
}
textBox1.AppendText(strbuildsection.ToString() + strbuildbetreff.ToString() + strbuildinhalt.ToString() + strbuildusercheck.ToString() + Environment.NewLine);
strbuildsection.Clear();
// HERE I want to stop my process and wait until the User
// press the button and start my event
// but i don't know how to do this
// After this the loop continue and so on
}
private void BT_Bestaetigung_Click(object sender, EventArgs e)
{
CODE
}
So i want to start my ´Event´ if the button get pressed and if not, the code should wait for this
As it seems you placed the code in the form which you showed to the user and this blocks you from stopping to wait for the user to respond because you are still inside your loop.
The solution is to use a separate modal dialog:
Create a separate Form which you construct inside your loop and show it to the user when necessary - wait for the form to be closed - work on the results and repeat until done.
Inside this new form you place your controls and buttons that the user needs to interact with and fill them before you show it to him.
frmConfirmationDialog myConfirmationDialog = new frmConfirmationDialog()
//Fill in information to show to the user
myConfirmationDialog.textBox1.AppendText(strbuildsection.ToString() + strbuildbetreff.ToString() + strbuildinhalt.ToString() + strbuildusercheck.ToString() + Environment.NewLine);
//Open Form modally (this will stop your loop until the dialog is closed)
DialogResult myResult = myConfirmationDialog.ShowDialog();
if (myResult == System.Windows.Forms.DialogResult.OK)
{
//Do Stuff here
}
else //catch further type of results here (you could also work with a switch statement
{
//Do Stuff here
}
BTW to get a DialogResult when closing the form set the DialogResult Property of the Confirm or Cancel Buttons to the values you like. This will cause the modal form to be automatically closed with the DialogResult of the button. If you need to catch stuff before closing the form you can either implement an EventHandler for FormClosing or handle the Click event of the buttons.
You have to think it this way: you have a continous operation and you are waiting for the user interaction.
In winforms application, where you can not perform continous operation in UI thread (because your window required constantly receive messages from os, i.e. user input or repaint requrests and have to react to them, or you will have laggy or even frozen UI), typical solution is:
split operation into sub-operations (which are short and doesn't cause UI lags);
put operation into a thread.
So, basicaly, go this way (there is a pattern to avoid using switch with delegating steps, but for simplicity we show classical way):
_timer = new Timer();
_timer.Tick += timer_Tick;
...
// in timer tick
switch(_operation)
{
case operation1:
_operation = nextoperation; // _operation++
...
break;
case operation2:
// do nothing, wait for button press
if(_buttonPressed)
_operation = nextoperation;
break;
case operation3:
// continue after button press
..
...
}
or this way
_thread = new Thread(...)
thread.Start();
...
// in thread
// start operation
...
// wait for button press
while(_buttonPressed)
Thread.Sleep(0);
// continue operation
...
in both cases
// in button click event
_buttonPressed = true;
Random rnd = new Random();
bool stop = false;
Thread thread = null;
btnShow.Click += (source, e) =>
{
stop = true;
MessageBox.Show("Show something");
};
btnClose.Click += (source, e) =>
{
stop = false;
};
if (thread == null)
{
thread = new Thread(() =>
{
while (true) // your working loop
{
while (stop)
{}
// action of your loop
Console.WriteLine(rnd.Next());
}
});
thread.Start();
}
This is the code in the NumericUpDown ValueChanged event:
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
DoThresholdCheck();
}
And this is the DoThresholdCheck() code:
private void DoThresholdCheck()
{
List<int> f;
List<string> fts;
const string D6 = "000{0}.bmp";
if (Directory.Exists(subDirectoryName))
{
if (!File.Exists(subDirectoryName + "\\" + averagesListTextFile + ".txt"))
{
return;
}
else
{
f = new List<int>();
fts = new List<string>();
Bitmap myFiles;
int counter = 0;
double thershold = (double)numericUpDown1.Value;
double max_min_threshold = (thershold / 100) * (max - min) + min;
_fi = new DirectoryInfo(subDirectoryName).GetFiles("*.bmp");
for (int i = 0; i < myNumbers.Count; i++)
{
if (myNumbers[i] >= max_min_threshold)
{
string t = i.ToString("D6") + ".bmp";
if (File.Exists(subDirectoryName + "\\" + t))
{
button1.Enabled = false;
myTrackPanelss1.trackBar1.Enabled = true;
}
else
{
button1.Enabled = true;
myTrackPanelss1.trackBar1.Enabled = false;
pictureBox1.Image = Properties.Resources.Weather_Michmoret;
label5.Visible = true;
break;
}
}
}
if (myTrackPanelss1.trackBar1.Maximum > 0)
{
SetPicture(0);
myTrackPanelss1.trackBar1.Scroll += new EventHandler(trackBar1_Scroll);
}
if (_fi.Length >= 0)
{
label15.Text = _fi.Length.ToString();
label15.Visible = true;
}
}
}
else
{
button1.Enabled = true;
}
}
What I want to do is; each time I change the NumericUpDown value in the program it will call the function in the event. In the event I create a new temp list each time and perform operations and make some checks on that list.
The problem is when I'm changing the NumericUpDown value a few times up and down in the program and then try to use the trackBar1 to move between the images again, the trackbar1 moves very slowly. When initially running the program, the slide bar in the trackbar1 moves quickly and moves quickly between images, but when I change the NumericUpDown values the trackbar moves slowly for some reason.
I tried to add a Timer2 and maybe use it in the NumericUpDown to make it call the function only after 500ms, for example, but it didn't solve it.
I can't figure out why its moving so slow.
I'am not shore if it is the problem, but seems you adding event handler myTrackPanelss1.trackBar1.Scroll += new EventHandler(trackBar1_Scroll); but never removing it. So you always adding event handler and after a wile trackBar1_Scroll will be fired many times. Set breakpoint in trackBar1_Scroll and you will see how many times event handler is raised. Solution could be remove event handler or add it just one time.
For starters, you are performing quite a few IO operations. This can be a costly operation, particularly when you are running it frequently (i.e. on the NumericUpDown's ValueChanged event). I would advise performing as much of this as possible in some initialization operation, not on each event firing.
Secondly, I'm not sure if it will actually affect the performance, but you keep attaching an event handler to the trackBar1.Scroll event. Since I don't see anywhere where you detach from this event, I would say that this is unnecessary. Either you need to better control the attachment and detachment, or simply attach once during control setup. It might also be worth creating a test program to see if attaching the same event handler multiple times does affect performance.
Finally, you could go about this operation by creating a background worker, on which you perform this operation each time it is signalled. If you make use of a wait handle, you can run the operation in a queue-like fashion. This would then not affect your UI operations, but you would have to be careful with what UI functions you call, as they can only be invoked on the UI thread. If you wish to know more about this, let me know and I will give you a brief example.