C#, WinForms, waiting for timers - c#

Trying to understand Timers and virtual clicks in C# Winforms. I want to have the program have an entered time value by the user (textbox1), then wait that amount of time and click the mouse, then increase the number counter (textbox2).
In the code below, the number counter immediately goes to 10, but the clicks are never ending, despite having a while loop set to stop the clicks at 10. I basically just want the program to wait a slightly random time (time entered to time entered +3), click the mouse, increase the counter, then pick a new random number and continue until 10 total clicks.
public Form1()
{
InitializeComponent();
}
private void NumbersOnly(object sender, KeyPressEventArgs e)
{
char ch = e.KeyChar;
if (!Char.IsDigit(ch) && ch != 8)
{
e.Handled = true;
}
}
static System.Timers.Timer _timer;
int numberofclicks = 0;
[DllImport("user32.dll")]
static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
private const int MOUSEEVENTF_MOVE = 0x0001;
private const int MOUSEEVENTF_LEFTDOWN = 0x0002;
private const int MOUSEEVENTF_LEFTUP = 0x0004;
private const int MOUSEEVENTF_RIGHTDOWN = 0x0008;
private const int MOUSEEVENTF_RIGHTUP = 0x0010;
private const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;
private const int MOUSEEVENTF_MIDDLEUP = 0x0040;
private const int MOUSEEVENTF_ABSOLUTE = 0x8000;
private void StartClicked(object sender, EventArgs e)
{
numberofclicks = 0;
Random rsn = new Random();
while (numberofclicks < 10)
{
string startseconds = textBox1.Text;
int timerstartseconds = Convert.ToInt32(startseconds);
int timertime = rsn.Next(timerstartseconds * 1000, ((timerstartseconds + 3) * 1000));
_timer = new System.Timers.Timer(timertime);
_timer.Elapsed += _timer_Elapsed;
_timer.Enabled = true;
textBox2.Clear();
numberofclicks++;
string numbertextbox = numberofclicks.ToString();
textBox2.Text = numbertextbox;
}
}
void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
LeftClick();
}
public static void LeftClick()
{
mouse_event(MOUSEEVENTF_LEFTDOWN, Control.MousePosition.X, Control.MousePosition.Y, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, Control.MousePosition.X, Control.MousePosition.Y, 0, 0);
}

The problem lies in a fact that StartClicked event handler does not block itself. It is just a simple method that without any delay loops 10 times changing the method level variable and modifying(even creating new!) Timer properties.
It is possible to do what you attempted to do in such a manner(single method with simple loop), but you will have to use async event handler and will have no need for a timer. And as the another answer already discusses how to do it with classic timers, I will give you such async-based solution:
private async void StartClicked(object sender, EventArgs e)
{
numberofclicks = 0;
Random rsn = new Random();
while (numberofclicks < 10)
{
string startseconds = textBox1.Text;
int timerstartseconds = Convert.ToInt32(startseconds);
int timertime = rsn.Next(timerstartseconds * 1000, ((timerstartseconds + 3) * 1000));
await Task.Delay(timertime);
LeftClick();
textBox2.Clear();
numberofclicks++;
string numbertextbox = numberofclicks.ToString();
textBox2.Text = numbertextbox;
}
}
For more information on async-await you can read MSDN on async-await.

When you say _timer.Enabled = true;, the rest of your code keeps executing. Then at some time later, when the timer ticks, the Elapsed event is triggered.
Additionally, each timer you create keeps on ticking - they don't only fire once.
Also, there are many different Timer classes in the .NET framework. For a simple winforms app like yours, I would stick with the System.Windows.Forms.Timer version. You don't have to worry about threading and such that way.
You might be better served with something like this:
private Random rsn = new Random();
private int numberofclicks = 0;
private System.Windows.Forms.Timer _timer;
// set the timer's tick event handler in form_load or similar.
// you could also just drag a timer onto the form and double-click it.
private void StartClicked(object sender, EventArgs e)
{
// don't allow starting again until finished
btnStart.Enabled = false;
numberofclicks = 0;
_timer.Interval = /* get randomish interval */
_timer.Start();
}
void _timer_Tick(object sender, EventArgs e)
{
_timer.Stop();
LeftClick();
numberofclicks++;
if (numberofclicks >= 10) {
btnStart.Enabled = true;
}
else {
_timer.Interval = /* get randomish interval */
_timer.Start();
}
}

Related

Progress Bar Value > Integer doesn't work in if statement?

I have an application I am testing out where audio is played and an action is performed when the audio is played. I have the audio captured in a progress bar which works flawlessly however after I start the program everything will work but the if statement below. Is there something I am missing? I am using NAudio to capture the audio etc as well.
All audio is going to the progress bar when selecting any audio source and will detect at the right level. When I click on a button in the Windows Form Application it right clicks fine, but when I have audio coming through past a certain level it doesn't right click again which is what I am trying to accomplish.
Here is the main code which gets the audio and the mouse buttons:
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
private const int MOUSEEVENTF_RIGHTUP = 0x10;
public void DoMouseClick()
{
uint X = (uint)Cursor.Position.X;
uint Y = (uint)Cursor.Position.Y;
mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, X, Y, 0, 0);
}
private WaveIn recorder;
public Form1()
{
InitializeComponent();
recorder = new WaveIn();
recorder.StartRecording();
MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
var devices = enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active);
audioSourcesList.Items.AddRange(devices.ToArray());
}
private void timer1_Tick(object sender, EventArgs e)
{
if (audioSourcesList.SelectedItem != null)
{
var device = (MMDevice)audioSourcesList.SelectedItem;
device.AudioEndpointVolume.Mute = false;
Label.Text = (Math.Round(device.AudioMeterInformation.MasterPeakValue * 100)).ToString();
progressBar.Value = (int)(device.AudioMeterInformation.MasterPeakValue * 100);
}
}
Here is the block that starts the main program and where it seems to not work:
private void test()
{
Thread.Sleep(2500);
DoMouseClick();
if (progressBar.Value >= 5)
{
DoMouseClick();
Thread.Sleep(2000);
DoMouseClick();
}
}
I did not work with NAudio but set up the threads you need.
If the program stops when it reaches the if condition, it is because the control is for another thread and you are using it for another thread. With the BeginInvoke command, that allowed two threads to run asynchronously so that both threads had access to the required controls.
If you want more information, refer to this link Calling Synchronous Methods Asynchronously
change Form1 constructor to this
public Form1()
{
Thread thread = new Thread(delegate ()
{
recorder = new WaveIn();
recorder.StartRecording();
});
System.Timers.Timer t = new System.Timers.Timer();
t.Interval = 500;
t.Elapsed += T_Elapsed;
t.Start();
MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
var devices = enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active);
audioSourcesList.Items.AddRange(devices.ToArray());
}
Delete the timer1_Tick method and add the following lines to the program
public delegate void InvokeDelegate();
private void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this.BeginInvoke(new InvokeDelegate(InvokeMethod));
}
void InvokeMethod()
{
if (audioSourcesList.SelectedItem != null)
{
var device = (MMDevice)audioSourcesList.SelectedItem;
device.AudioEndpointVolume.Mute = false;
Label.Text = (Math.Round(device.AudioMeterInformation.MasterPeakValue * 100)).ToString();
progressBar.Value = (int)(device.AudioMeterInformation.MasterPeakValue * 100);
}
}

Getting a label to insert text slowly

I am trying to insert text to a lable, BUT the text has to be inserted slowly/character by character/letter by letter,
kinda like in old MUD games.
So far I have tried doing this:
private void StoryBox_Click(object sender, EventArgs e)
{
string text = StoryBox.Text;
var index = 0;
var timer = new System.Timers.Timer(2000);
timer.Elapsed += delegate
{
if (index < text.Length)
{
index++;
StoryBox.Text = "This should come out pretty slowly ";
}
else
{
timer.Enabled = false;
timer.Dispose();
}
};
timer.Enabled = true;
}
This is what I have gathered from the site but I don't particularly understand why this isn't working.
As you can see it's under StoryBox_Click.
Is there a way to automate this? So when the program is opened, it counts a couple seconds and THEN starts writing the text out.
Try this:
private async void button1_Click(object sender, EventArgs e)
{
string yourText = "This should come out pretty slowly";
label1.Text = string.Empty;
int i = 0;
for (i = 0; i <= yourText.Length - 1; i++)
{
label1.Text += yourText[i];
await Task.Delay(500);
}
}
You can use the Shown-Event of YourForm when you want to start it after your GUI has been opened.
So reusing your provided code and changing a few things this may work for you:
Add private fields to YourForm class:
private Timer _timer;
private int _index;
private string _storyText;
and initialising it in YourForm constructor
public YourForm()
{
InitializeComponent();
// init private fields;
this._index = 0;
this._storyText = "This should come out pretty slowly";
// Timer Interval is set to 1 second
this._timer = new Timer { Interval = 1000 };
// Adding EventHandler to Shown Event
this.Shown += this.YourForm_Shown;
this._timer.Tick += delegate
{
if (this._index < this._storyText.Length)
{
StoryBox.Text += this._storyText[this._index];
this._index++;
}
else
{
this._timer.Stop();
}
};
}
and the Shown event for YourForm:
private void YourForm_Shown(object sender, EventArgs e)
{
this._timer.Start();
}

Making a text editor with multiple pages using richtextboxes

i am trying to give my text editor multiple pages mode the problem is when the richtextbox reaches the last line it resizes and add a scroll bar which is not what i want, i made a code to transfer the last line of the richtextbox to the one that follows but it's moving the whole text instead and it's kind of sluggish, any help would be appreciated
public partial class Form1 : Form
{
protected static bool GetVisibleScrollbars(Control ctl)
{
int wndStyle = Win32.GetWindowLong(ctl.Handle, Win32.GWL_STYLE);
bool vsVisible = (wndStyle & Win32.WS_VSCROLL) != 0;
return vsVisible;
}
public Form1()
{
InitializeComponent();
}
List<RichTextBox> pages=new List<RichTextBox>();
int currentdocindex = 0;
public void AddPage()
{
RichTextBox B = new RichTextBox();
B.Size = richTextBox1.Size;
panel1.Controls.Add(B);
B.Location = new Point(pages[pages.Count - 1].Location.X, pages[pages.Count - 1].Location.Y + richTextBox1.Height + 20);
pages.Add(B);
B.SelectionIndent = 20;
B.SelectionRightIndent = 20;
B.Enter += new EventHandler(richTextBox_Enter);
}
private void richTextBox_Enter(object sender, EventArgs e)
{
int i = 0;
foreach (RichTextBox box in pages)
{
if (box == (RichTextBox)sender)
{
currentdocindex=i;
break;
}
i++;
}
label1.Text = (currentdocindex + 1).ToString();
}
private void Form1_Load(object sender, EventArgs e)
{
pages.Add(richTextBox1);
richTextBox1.SelectionIndent = 20;
richTextBox1.SelectionRightIndent = 20;
}
private void richTextBox1_Enter(object sender, EventArgs e)
{
int i = 0;
foreach (RichTextBox box in pages)
{
if(box==(RichTextBox)sender)
{
currentdocindex=i;
break;
}
i++;
}
}
bool added = false;
private void timer1_Tick(object sender, EventArgs e)
{
int correntPageIndex = currentdocindex;
if (GetVisibleScrollbars(pages[currentdocindex]))
{
if (!added)
{
AddPage();
added = true;
}
}
else
{
added = false;
}
}
if(GetVisibleScrollbars(pages[correntPageIndex]))
{
string LastLineText = pages[correntPageIndex].Lines[pages[correntPageIndex].Lines.Count() - 1];
int LastLineStartIndex = pages[correntPageIndex].Text.LastIndexOf(LastLineText);
pages[correntPageIndex].SelectionStart = LastLineStartIndex;
pages[correntPageIndex].SelectionLength = pages[correntPageIndex].Text.Length - 1;
LastLineText = pages[correntPageIndex].SelectedRtf;
pages[correntPageIndex].Text = pages[correntPageIndex].Text.Remove(LastLineStartIndex);
pages[correntPageIndex + 1].SelectionStart = 0;
pages[correntPageIndex+1].SelectedRtf = LastLineText;
}
}
}
public class Win32
{
// offset of window style value
public const int GWL_STYLE = -16;
// window style constants for scrollbars
public const int WS_VSCROLL = 0x00200000;
public const int WS_HSCROLL = 0x00100000;
[DllImport("user32.dll", SetLastError = true)]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
}
RichTextBox is a pain for this sort of thing, because to mutate a small portion of text you have to actually select the text first (which it appears you're attempting to do) and ensure the change only affects that text. It's a little nasty on the memory usage, but you might be better served by determining how many characters you want per page and subscribing to the KeyDown Event to determine when you move to a new page. Try to adapt something like this and see if it works better.
public void MyKeyDownHandler(object sender, System.Windows.Forms.KeyEventArgs e)
{
if(this.CurrentPageControl.RTB.Text.Length >= MY_LIMITING_CONSTANT_I_SET)
{
MyPageUserControl mpuc = new MyPageUserControl();
mpuc.RTB.Text = this.CurrentPageControl.RTB.Text.Split(' ').Last();
thePageCollectionIPresumeYouHave.Add(mpuc);
this.CurrentPageControl = thePageCollectionIPresumeYouHave.Last();
mpuc.RTB.Focus();
}
}
Caveat: I did that entirely from memory and without a chance to read all of your code ( I had to skim) because I'm at work.
Another Caveat: I assumed you put your RichTextBoxes in a custom "page" control. If you didn't, I hope my code shows you why you might want to.

multiple tasks using same timer for different double interval value

Please consider below test program.
I have three tasks with specific interval. [task1-- task1Interval , [task2-- task2Interval , [task3-- task3Interval]
I want to use single timer to execute all three tasks.
Every thing works fine when interval is in integer.
we want to achieve the same functionality with double interval values for e.g [task1Interval-- 0.1 , [task2Interval-- 2.1 , [task3Interval-- 3.1].
Any pointers for the same would be highly appreciated.
public class Class1
{
private System.Timers.Timer _timer;
private int _counter=0;
private int task1Interval = 1;
private int task2Interval = 2;
private int task3Interval = 3;
public void Start()
{
this._timer = new System.Timers.Timer(100);
this._timer.AutoReset = true;
this._timer.Elapsed += new System.Timers.ElapsedEventHandler(this.serviceTimerElapse);
this._timer.Enabled = true;
this._timer.Start();
}
private void serviceTimerElapse(object source, System.Timers.ElapsedEventArgs e)
{
this._counter++;
if (this._counter % task1Interval == 0)
{
task1();
}
if (this._counter % task2Interval == 0)
{
task2();
}
if (this._counter % task3Interval == 0)
{
task3();
}
}
private void task1()
{
Console.WriteLine("task1 started");
}
private void task2()
{
Console.WriteLine("task2 started");
}
private void task3()
{
Console.WriteLine("task3 started");
}
}
This is very common to what you would see in a primitive game engine. You need a world "clock" with some level of granularity and you handle each event within each "tick".
You already known your granularity needs to be the least common denominator among your intervals (in this case 0.1). Then a primitive solution would include counters for each task along with a threshold.
Ex (pseudo code):
private const double TICK = 0.1;
private double TASK_1_THRESHOLD = 0.1;
private double TASK_1_COUNT = 0.0;
private double TASK_2_THRESHOLD = 2.1;
private double TASK_2_COUNT = 0.0;
private double TASK_3_THRESHOLD = 3.1;
private double TASK_3_COUNT = 0.0;
private void tick()
{
TASK_1_COUNT += TICK;
TASK_2_COUNT += TICK;
TASK_3_COUNT += TICK;
if (TASK_1_COUNT >= TASK_1_THRESHOLD) {
TASK_1_COUNT = 0.0;
task1();
}
// do this for each
}

DispatcherTimer and Button c# conflict

I'm very new to WP8 dev and c#. I'm trying to make a loop that counts up by n on an interval. I want to press a button to increment n.
Here is my code right now:
namespace Petsounds {
public partial class MainPage : PhoneApplicationPage {
float clicks = 0;
float clickers = 0;
float clickerBuyers = 0;
float clickerCost = 5;
float clickerBuyerCost = 500;
long savedTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
bool buyClickerButtonFlag = false;
bool clickButtonFlag = false;
// Constructor
public MainPage() {
InitializeComponent();
//
DispatcherTimer t = new DispatcherTimer();
t.Interval = TimeSpan.FromMilliseconds(10);
t.Tick += (s, e) => startLoop();
t.Start();
}
private void clickButtonOnClick(object sender, RoutedEventArgs e) {
clickButtonFlag = true;
System.Diagnostics.Debug.WriteLine("clicked!" + clicks);
}
private void buyClickerButtonOnClick(object sender, RoutedEventArgs e) {
buyClickerButtonFlag = true;
}
private void startLoop() {
if (true) {
long nowTime = savedTime;
long timePassed = nowTime - savedTime;
//user input
if (clickButtonFlag) {
clickButtonFlag = false;
clicks++;
System.Diagnostics.Debug.WriteLine("clicked!" + clicks);
}
if (buyClickerButtonFlag) {
buyClickerButtonFlag = false;
if (clicks > clickerCost) {
clickers++;
clicks -= clickerCost;
clickerCost *= 1.6F;
}
System.Diagnostics.Debug.WriteLine("clicker bought!" + clickers);
}
//update vars
if (timePassed > TimeSpan.TicksPerSecond) {
savedTime = nowTime;
nowTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
clicks += clickers;
}
//update display
clickCount.Text = clicks.ToString();
buyClickerButtonCost.Text = "Cossst " + clickerCost.ToString();
}
}
}
}
My button's are inconsistent, and if I remove the thread, the buttons are responsive (but of course the counter doesn't work.)
EDIT:
I've changed
DispatcherTimer t = new DispatcherTimer();
t.Interval = TimeSpan.FromMilliseconds(10);
t.Tick += (s, e) => startLoop();
t.Start();
to
Timer myTimer = new Timer(startLoop);
myTimer.Change(1000, 10);
And now get an error:
A first chance exception of type 'System.UnauthorizedAccessException' occurred in System.Windows.ni.dll
on line
clickCount.Text = clicks.ToString();
First of all... you will quickly find that 10ms is not really 10ms... It might not even be that close... If you did 1000ms... that would be expected to be more accurate.
Also, a DispatcherTimer is going to queue up a function call to the GUI thread each interval... which means you are flooding the GUI thread with startLoop() calls. This doesn't give the thread much time to update anything else... like your buttons.
There is a different approach you might want to consider.
If your task is to increment a numeric value when a user touches a button (and have the numbers increase at a steady pace) consider using the RepeatButton.
RepeatButton: Represents a control that raises its Click event repeatedly from the time it is pressed until it is released.
XAML
<!--
Delay: The time, in milliseconds, the RepeatButton waits
when it is pressed before it starts repeating the click action.
Interval: The time, in milliseconds, between repetitions
of the click action, as soon as repeating starts.
-->
<RepeatButton Content='Buy'
Interval='50' Delay='100'
Click='RepeatButton_Click' />
Code
private float buyCounter = 0;
private void RepeatButton_Click(object sender, RoutedEventArgs e) {
buyCounter += 1;
buyClickerButtonCost.Text = buyCounter.ToString();
}
It's like #Andrew said - DispatcherTimer works on UI thread and with so small intervall you are blocking it.
If you want such a small interval you can use Timer on different Thread:
public MainPage()
{
InitializeComponent();
System.Threading.Timer myTimer = new Timer(MyTimerCallback);
myTimer.Change(1000, 10);
}
private static int value = 0;
private static void MyTimerCallback(object state)
{
value++;
}
But you must remember that you use it on different Thread - this Timer has no access to your UI elements (buttons and so on).
EDIT
You convinced me to check it:
static float clicks = 0;
static float clickers = 0;
static float clickerCost = 5;
static long savedTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
static bool buyClickerButtonFlag = false;
static bool clickButtonFlag = false;
public MainPage()
{
InitializeComponent();
first.Click += ShowCounter;
DispatcherTimer t = new DispatcherTimer();
t.Interval = TimeSpan.FromSeconds(5);
t.Tick += ShowCounter;
t.Start();
System.Threading.Timer myTimer = new Timer(MyTimerCallback);
myTimer.Change(10, 10);
}
private void ShowCounter(object sender, EventArgs e)
{
textBlck.Text = clicks.ToString();
}
private static void MyTimerCallback(object state)
{
clicks++; // added to check running
if (true)
{
long nowTime = savedTime;
long timePassed = nowTime - savedTime;
//user input
if (clickButtonFlag)
{
clickButtonFlag = false;
clicks++;
System.Diagnostics.Debug.WriteLine("clicked!" + clicks);
}
if (buyClickerButtonFlag)
{
buyClickerButtonFlag = false;
if (clicks > clickerCost)
{
clickers++;
clicks -= clickerCost;
clickerCost *= 1.6F;
}
System.Diagnostics.Debug.WriteLine("clicker bought!" + clickers);
}
//update vars
if (timePassed > TimeSpan.TicksPerSecond)
{
savedTime = nowTime;
nowTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
clicks += clickers;
}
}
}
I tested it on the device and buttons works.
On the other hand - what's the point of putting a method that waits for a flag buton click, when you can put the job easily to button click event. Let it happen when user clicked button - don't check buton state all the time.

Categories

Resources