Threads with common variable - c#

I'm building a NFS joystick manipulating the MouseWheel events. On my main thread I call a second thread that pulses keyboard keys with a generated frequency. My problem is that this frequency dynamically changes in time when you move up or down the MouseWheel but the simulator thread doesn't notice this change. This is a fragment of my code:
int centro = 0;
static bool left;
static bool ismoving;
static int grado;
static int frec;
Thread simulator = new Thread(new ThreadStart(move));
public Form1()
{
InitializeComponent();
}
static void mleft()
{
KeyboardSimulator.KeyDown(Keys.G);
Thread.Sleep(10);
KeyboardSimulator.KeyUp(Keys.G);
}
static void mright()
{
KeyboardSimulator.KeyDown(Keys.J);
Thread.Sleep(10);
KeyboardSimulator.KeyUp(Keys.J);
}
static void move()
{
while (ismoving)
{
if (left)
{
mleft();
Thread.Sleep(frec);
}
else
{
mright();
Thread.Sleep(frec);
}
}
}
void map()
{
if (grado == 0)
{
ismoving = false;
frec = 0;
}
else
{
int ut = 1000;//1seg
left = grado > 0;
grado = Math.Abs(grado);
frec = ut / grado / 10;
ismoving = true;
}
}
private void Window_Closed_1(object sender, EventArgs e)
{
simulator.Abort();
}
private void Form1_Load(object sender, EventArgs e)
{
HookManager.MouseWheel += Window_MouseWheel_1;
simulator = new Thread(new ThreadStart(move));
}
private void Window_MouseWheel_1(object sender, MouseEventArgs e)
{
centro += e.Delta;
grado = centro / 120;
l1.Text = grado + "";
map();
}

Related

How to update the arrays of childforms with timer in c# winform?

The array in the parent form is updated every 100 milliseconds.
Once the child form is opened with menustrip in parent form, the data is passed to child form
Issue:
Till now, I have had success in passing the array data, but I need to update it periodically in the child form also, and I have some difficulties with setting the timer in child form.
Form1: or Parent Form
string[] ArrayPack1Cells = new string[28];
//All the values are first stored in 2D array `volt_info_array[packidindex, voltage_index]`
//and after scaling it, elements store in 1D array depending on the `packidindex` value
Voltages = ((volt_info_array[packidindex, voltage_index] / 1000));
switch(PackIndexes)
{
case 1:
// if size is 28, then convert to array to be passed to child form.
if(ListPack1Cells.Count == 28)
{
ArrayPack1Cells = ListPack1Cells.ToArray();
}
break;
case 2:
.....
}
private void viewToolStripMenuItem_Click(object sender, EventArgs e)
{
ToolStripMenuItem menu = sender as ToolStripMenuItem;
switch (menu.Name)
{
case "pack1ToolStripMenuItem":
if (Application.OpenForms["Pack1"] is Pack1 pack1)
{
pack1.Focus();
return;
}
pack1 = new Pack1();
pack1.TakeThis(ArrayPack1Cells);
pack1.MdiParent = this;
pack1.Show();
Array.Clear(ArrayPack1Cells, 0, ArrayPack1Cells.Length);// Clear it once send to form2
break;
}
Form2: or Child/Pack1 Form
public void TakeThis(string[] ArrayPack1Cells), method copies all the 28 arrays in the texboxes but only once.
public List<Control> Cell_Volt1 = new List<Control>();
public string[] f_temp = new string[28];
public Pack1()
{
InitializeComponent();
Cell_tbxArray();
if (P1_timer.Enabled == false)
{
P1_timer.Enabled = true;
P1_timer.Tick += new System.EventHandler(this.P1_timer_Tick);
P1_timer.Start();
}
else if (P1_timer.Enabled)
{
1_timer.Stop();
P1_timer.Enabled = true;
P1_timer.Start();
}
}
private void Cell_tbxArray()
{
for (int i = 0; i < tableLayoutPanel1.Controls.Count; i++)
{
if (tableLayoutPanel1.Controls[i].GetType() == typeof(TextBox))
{
Cell_Volt1.Add(tableLayoutPanel1.Controls[i]);
}
}
}
public void TakeThis(string[] ArrayPack1Cells)
{
f_temp = ArrayPack1Cells;
int index = 0;
foreach (string item in f_temp)
{
Cell_Volt1[index].Text += item;
index++;
}
}
private void P1_timer_Tick(object sender, EventArgs e)
{
for (int i = 0; i < Cell_Volt1.Count; i++)
{
Cell_Volt1[i].Text += f_temp[i];
}
}
The private void P1_timer_Tick(object sender, EventArgs e) isnt working at all.
Here is my take.
Parent:
public static class Consts
{
public const int NPACKS = 10, NVOLTS = 28;
}
public partial class ParentForm : Form
{
double[,] data = new double[Consts.NPACKS, Consts.NVOLTS];
double[][] uidata = new double[Consts.NPACKS][];
Dictionary<int, PackForm> forms = new Dictionary<int, PackForm>();
public ParentForm()
{
InitializeComponent();
for (int i = 0; i < Consts.NPACKS; i++)
{
uidata[i] = new double[Consts.NVOLTS];
}
}
// wild guess - not clear how do you update it - all of them or slices
void DataContinuousUpdate(double value, int npack, int nvolt)
{
data[npack, nvolt] = value;
var slice = uidata[npack];
// in case a form is trying to refresh UI
lock (slice)
slice[nvolt] = value;
}
void OpenPack(int npack)
{
// assuming access to this method is serial
if (forms.ContainsKey(npack))
return;
var slice = uidata[npack];
lock (slice)
{
var form = new PackForm(npack, slice);
forms.Add(npack, form);
}
}
}
Pack:
public partial class PackForm : Form
{
public int ID { get; private set; }
public double[] Data { get; private set; }
public double ValueScale { get; set; }
TextBox[] VoltTextBox = new TextBox[Consts.NVOLTS];
Timer timer;
private PackForm()
{
InitializeComponent();
CreateTextBoxes();
ValueScale = 1000.0;
this.FormClosing += PackForm_FormClosing;
}
public PackForm(int iD, double[] data) : this()
{
InitializeComponent();
ID = iD;
Data = data;
StartTimer();
}
private void PackForm_FormClosing(object? sender, FormClosingEventArgs e)
{
StopTimer();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void StartTimer()
{
StopTimer();
timer = new Timer();
timer.Enabled = true;
timer.Interval = 1000;
timer.Tick += Timer_Tick;
}
private void StopTimer()
{
if (timer == null) return;
timer.Enabled = false;
timer.Tick -= Timer_Tick;
}
private void Timer_Tick(object? sender, EventArgs e)
{
if (Data == null) return;
lock(Data)
{
for (int i = 0; i < Data.Length; i++)
{
VoltTextBox[i].Text += Data[i];
}
}
}
}

A new message box appears with each second after it appears

I ran into an issue where i tried displaying a display box after a countdown reached a certain time but for some odd reason it replicates with each second despite it having already passed the initial time it was supposed to appear. This is what i tried to do but now the timer has stopped and the time remaining column has stopped runng.
public partial class Form1 : Form
{
private List<CSession> sessionlist = new List<CSession>();
private TimeSpan workingTimeSpan = new TimeSpan();
private TimeSpan fiveMinutes = new TimeSpan(0,1,0);
private TimeSpan oneSecond = new TimeSpan(0,0,1);
public Form1()
{
InitializeComponent();
timer1.Enabled = true;
timer1.Start();
}
private void label1_Click(object sender, EventArgs e)
{
}
private void AddTime_Click(object sender, EventArgs e)
{
workingTimeSpan += fiveMinutes;
DisplayWorkingTimeSpan();
}
private void DisplayWorkingTimeSpan()
{
TimerLabel.Text = workingTimeSpan.ToString();
}
private void DecreaseTime_Click(object sender, EventArgs e)
{
TimeSpan fiveMinutes = new TimeSpan(0,5,0);
workingTimeSpan -= fiveMinutes;
DisplayWorkingTimeSpan();
}
private void Confirm_Click(object sender, EventArgs e)
{
CSession newSession = new CSession();
if(PasswordText.Text == "")
{
MessageBox.Show("Password not entered");
return;
}
newSession.password = PasswordText.Text;
newSession.purchased_time = workingTimeSpan;
newSession.remaining_time = workingTimeSpan;
newSession.status = "Online";
sessionlist.Add(newSession);
PasswordText.Text = "";
TimerLabel.Text = "";
workingTimeSpan = new TimeSpan();
}
private void DisplayAllSessions()
{
listView1.Items.Clear();
foreach(CSession c in sessionlist)
{
string[] row = { c.password, c.purchased_time.ToString(), c.remaining_time.ToString(), c.status };
ListViewItem i = new ListViewItem(row);
listView1.Items.Add(i);
}
}
private void timer1_Tick(object sender, EventArgs e)
{
foreach(CSession c in sessionlist)
{
if (c.remaining_time.TotalMinutes == 5)
{
timer1.Stop();
MessageBox.Show("Time almost up for client.");
}
if (c.remaining_time.TotalSeconds < 1)
{
c.status = "Offline";
}
if(c.status == "Online")
{
c.remaining_time -= oneSecond;
}
}
DisplayAllSessions();
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Close();
}
}
Add a flag that gets toggled when you display the message, so you won't display it again:
private bool MessageDisplayed = false;
private void timer1_Tick(object sender, EventArgs e)
{
foreach(CSession c in sessionlist)
{
if (c.remaining_time.TotalMinutes == 5 && !MessageDisplayed) // <-- check the flag
{
MessageDisplayed = true;
MessageBox.Show("Time almost up for client.");
}
if (c.remaining_time.TotalSeconds < 1)
{
c.status = "Offline";
}
if(c.status == "Online")
{
c.remaining_time -= oneSecond;
}
}
DisplayAllSessions();
}
Now you can leave the timer running and your message will only appear once.

Custom ToggleButton doesn't respond to ManipulationCompleted event

I have a custom UI element that inherits from System.Windows.Controls.Primitives.ToggleButton. I'm also routing my mouse events through a custom TouchDevice that raises touch events instead.
For some reason, the ManipulationCompleted event never fires. First, the custom TouchDevice can be found here: http://blakenui.codeplex.com/SourceControl/changeset/view/67526#Blake.NUI.WPF/Touch/MouseTouchDevice.cs
Here are the relevant parts of my class:
public class ToggleSwitch: ToggleButton {
private Grid _root;
private readonly IList<int> _activeTouchDevices;
private const double UNCHECKED_TRANSLATION = 0;
private TranslateTransform _backgroundTranslation;
private TranslateTransform _thumbTranslation;
private Grid _root;
private Grid _track;
private FrameworkElement _thumb;
private double _checkedTranslation;
private double _dragTranslation;
private bool _wasDragged;
private bool _isDragging;
public override void OnApplyTemplate()
{
...
MouseTouchDevice.RegisterEvents(_root);
_root.IsManipulationEnabled = true;
_root.TouchDown += OnTouchDown;
_root.TouchUp += OnTouchUp;
_root.GotTouchCapture += OnGotTouchCapture;
_root.LostTouchCapture += OnLostTouchCapture;
_root.ManipulationStarted += OnManipulationStarted;
_root.ManipulationDelta += OnManipulationDelta;
_root.ManipulationCompleted += OnManipulationCompleted;
}
private void OnTouchDown(object sender, TouchEventArgs e)
{
e.TouchDevice.Capture(_root);
}
private void OnGotTouchCapture(object sender, TouchEventArgs e)
{
if (e.TouchDevice.Captured == _root)
{
Manipulation.AddManipulator(_root,e.TouchDevice);
_activeTouchDevices.Add(e.TouchDevice.Id);
}
}
private void OnManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
e.Handled = true;
_isDragging = true;
_dragTranslation = Translation;
ChangeVisualState(true);
Translation = _dragTranslation;
}
private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
e.Handled = true;
var horizontalChange = e.DeltaManipulation.Translation.X;
var direction = Math.Abs(horizontalChange) >= Math.Abs(e.DeltaManipulation.Translation.Y) ? Orientation.Horizontal : Orientation.Vertical;
if (direction == Orientation.Horizontal && horizontalChange != 0.0)
{
_wasDragged = true;
_dragTranslation += horizontalChange;
Translation = Math.Max(UNCHECKED_TRANSLATION, Math.Min(_checkedTranslation, _dragTranslation));
}
}
private void OnManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
e.Handled = true;
_isDragging = false;
var click = false;
if (_wasDragged)
{
var edge = (IsChecked ?? false) ? _checkedTranslation : UNCHECKED_TRANSLATION;
if (Translation != edge)
{
click = true;
}
}
else
{
click = true;
}
if (click)
{
OnClick();
}
_wasDragged = false;
}
}
The OnManipulationCompleted method is never entered.

C# why cant i exit this while loop?

I'm tryig to make a program that can auto press space.
but i can only enable it. after the program crashes.
so i cant turn it off. pleace help
here is my code:
using System.Windows.Forms;
namespace Auto_Abillty
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public bool startFlag = false;
public bool stopFlag = false;
private void Preiststop_Click(object sender, EventArgs e)
{
stopFlag = true;
}
private void Preist_Click(object sender, EventArgs e)
{
startFlag = true;
while (startFlag)
{
SendKeys.Send(" ");
Thread.Sleep(5000);
if (stopFlag)
startFlag = false;
}
}
}
}
I would suggest using System.Windows.Forms.Timer with 5000 ms interval instead of your approach. Then by clicking on the button on a form you may start or stop it.
Another approach is to move your while loop into another thread that will make UI thread still be responsive
You must abort the thread as well.
public bool x = false;
public bool j = false;
private void Preiststop_Click(object sender, EventArgs e)
{
j = true;
}
private void Preist_Click(object sender, EventArgs e)
{
x = true;
Thread newThread = new Thread(delegate ()
{
DoPriestWork();
});
newThread.Start();
//loop to wait for the response from DoPriestWork thread
while (x)
{
Thread.Sleep(5000);
if (j)
x = false;
}
newThread.Abort();
}
public void DoPriestWork()
{
//x = true;
//while (x == true)
//{
SendKeys.Send(" ");
//Thread.Sleep(5000);
// if (j == true)
// x = false;
//}
}
Seems like all you need is a thread so another button can be pushed to change the condition of "j". If there is only one thread, the UI will not be updated until that thread is stopped hence the need to make another thread, like below example.
public Form1()
{
InitializeComponent();
}
public bool x = false;
public bool j = false;
private void Preiststop_Click(object sender, EventArgs e)
{
j = true;
}
private void Preist_Click(object sender, EventArgs e)
{
Thread newThread = new Thread(DoPriestWork);
newThread.Start();
}
public void DoPriestWork()
{
x = true;
while (x == true)
{
SendKeys.Send(" ");
Thread.Sleep(5000);
if (j == true)
x = false;
}
}
Your Preist_Click call blocks the UI: it executes in the same thread, instead of starting its own.
Try this:
private bool keepRunning = true;
private void Preiststop_Click(object sender, EventArgs e)
{
keepRunning = false;
}
private async void Preist_Click(object sender, EventArgs e)
{
keepRunning = true;
await Task.Run(() => {
while (keepRunning)
{
SendKeys.Send(" ");
Thread.Sleep(5000);
}
});
}

Why is the backgroundworker not pausing when I click the button?

In Form1 I removed/deleted the _busy variable. In Form1 top I did:
BackgroundWebCrawling bgwc;
Then in the button4 pause click event I did:
private void button4_Click(object sender, EventArgs e)
{
bgwc.PauseWorker();
label6.Text = "Process Paused";
button5.Enabled = true;
button4.Enabled = false;
}
In the button5 click event button I did:
private void button5_Click(object sender, EventArgs e)
{
bgwc.ContinueWorker();
label6.Text = "Process Resumed";
button4.Enabled = true;
button5.Enabled = false;
}
And the cancel button click event:
private void button3_Click(object sender, EventArgs e)
{
bgwc.CancelWorker();
cancel = true;
}
Then I'm checking in Form1 completed event if cancel is true or not:
if (cancel == true)
{
label6.Text = "Process Cancelled";
}
else
{
label6.Text = "Process Completed";
}
And this is how the BackgroundWebCrawling class look like now:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HtmlAgilityPack;
using System.Net;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;
namespace GatherLinks
{
class BackgroundWebCrawling
{
public string f;
int counter = 0;
List<string> WebSitesToCrawl;
int MaxSimultaneousThreads;
public BackgroundWorker mainBackGroundWorker;
BackgroundWorker secondryBackGroundWorker;
WebcrawlerConfiguration webcrawlerCFG;
List<WebCrawler> webcrawlers;
int maxlevels;
public event EventHandler<BackgroundWebCrawlingProgressEventHandler> ProgressEvent;
ManualResetEvent _busy = new ManualResetEvent(true);
public BackgroundWebCrawling()
{
webcrawlers = new List<WebCrawler>();
mainBackGroundWorker = new BackgroundWorker();
mainBackGroundWorker.WorkerSupportsCancellation = true;
mainBackGroundWorker.DoWork += mainBackGroundWorker_DoWork;
}
private void mainBackGroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 0; i < WebSitesToCrawl.Count; i++)
{
_busy.WaitOne();
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
while (counter >= MaxSimultaneousThreads)
{
Thread.Sleep(10);
}
WebCrawler wc = new WebCrawler(webcrawlerCFG);
webcrawlers.Add(wc);
counter++;
secondryBackGroundWorker = new BackgroundWorker();
secondryBackGroundWorker.DoWork += secondryBackGroundWorker_DoWork;
object[] args = new object[] { wc, WebSitesToCrawl[i] };
secondryBackGroundWorker.RunWorkerAsync(args);
}
while (counter > 0)
{
Thread.Sleep(10);
}
}
private void secondryBackGroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
object[] args = (object[])e.Argument;
WebCrawler wc = (WebCrawler)args[0];
string mainUrl = (string)args[1];
wc.ProgressEvent += new EventHandler<WebCrawler.WebCrawlerProgressEventHandler>(x_ProgressEvent);
wc.webCrawler(mainUrl, maxlevels);
counter--;
}
public void Start(List<string> sitestocrawl, int threadsNumber, int maxlevels, WebcrawlerConfiguration wccfg)
{
this.maxlevels = maxlevels;
webcrawlerCFG = wccfg;
WebSitesToCrawl = sitestocrawl;
MaxSimultaneousThreads = threadsNumber;
mainBackGroundWorker.RunWorkerAsync();
}
private void x_ProgressEvent(object sender, WebCrawler.WebCrawlerProgressEventHandler e)
{
// OK .. so now you get the data here in e
// and here you should call the event to form1
Object[] temp_arr = new Object[8];
temp_arr[0] = e.csFiles;
temp_arr[1] = e.mainUrl;
temp_arr[2] = e.levels;
temp_arr[3] = e.currentCrawlingSite;
temp_arr[4] = e.sitesToCrawl;
temp_arr[5] = e.done;
temp_arr[6] = e.failedUrls;
temp_arr[7] = e.failed;
OnProgressEvent(temp_arr); /// Send the data + additional data from this class to Form1..
///
/*
* temp_arr[0] = csFiles;
temp_arr[1] = mainUrl;
temp_arr[2] = levels;
temp_arr[3] = currentCrawlingSite;
temp_arr[4] = sitesToCrawl;*/
}
private void GetLists(List<string> allWebSites)
{
}
public class BackgroundWebCrawlingProgressEventHandler : EventArgs
{
public List<string> csFiles { get; set; }
public string mainUrl { get; set; }
public int levels { get; set; }
public List<string> currentCrawlingSite { get; set; }
public List<string> sitesToCrawl { get; set; }
public bool done { get; set; }
public int failedUrls { get; set; }
public bool failed { get; set; }
}
protected void OnProgressEvent(Object[] some_params) // Probably you need to some vars here to...
{
// some_params to put in evenetArgs..
if (ProgressEvent != null)
ProgressEvent(this,
new BackgroundWebCrawlingProgressEventHandler()
{
csFiles = (List<string>)some_params[0],
mainUrl = (string)some_params[1],
levels = (int)some_params[2],
currentCrawlingSite = (List<string>)some_params[3],
sitesToCrawl = (List<string>)some_params[4],
done = (bool)some_params[5],
failedUrls = (int)some_params[6],
failed = (bool)some_params[7]
});
}
public void PauseWorker()
{
if (mainBackGroundWorker.IsBusy)
{
_busy.Reset();
}
}
public void ContinueWorker()
{
_busy.Set();
}
public void CancelWorker()
{
ContinueWorker();
mainBackGroundWorker.CancelAsync();
}
}
}
So I added the methods the pause the continue the cancel. In the dowork event, I changed all the things and added things.
But when I click the buttons there is no effect. Not pausing, not continue and not cancel. Nothing.
You never check the _busy status in mainBackGroundWorker_DoWork method;
for (int i = 0; i < WebSitesToCrawl.Count; i++)
{
_busy.WaitOne();
//...
}
also you should have your ManualResetEvent _busy in class with BackgroundWorker
ManualResetEvent _busy = new ManualResetEvent(true);
public BackgroundWorker mainBackGroundWorker;
public void PauseWorker()
{
if(mainBackGroundWorker.IsBusy)
{
_busy.Reset();
}
}
public void ContinueWorker()
{
_busy.Set();
}
and in Form1:
private void button4_Click(object sender, EventArgs e)
{
bgwc.PauseWorker();
//...
}
private void button5_Click(object sender, EventArgs e)
{
bgwc.ContinueWorker();
//...
}
to cancel the BackgroundWorker you can use CancellationPending property and CancelAsync method. Note: you should first unpause the worker.
public void CancelWorker()
{
ContinueWorker();
mainBackGroundWorker.CancelAsync();
}
private void mainBackGroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 0; i < WebSitesToCrawl.Count; i++)
{
_busy.WaitOne();
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
//...
}
}
If this doesn't help you, then you have problems with mainBackGroundWorker code and secondryBackGroundWorker.
This code only pauses mainBackGroundWorker, but not secondryBackGroundWorkers. The same with cancelation. If main worker is canceled? it will wait for all the secondary workers to finish their jobs. Also if you pause main worker? you can still have new results arriving from secondary workers.
You do not handle errors. If you have an exception in second worker, than you do not get any notification about that and also your main worker will never stop, because counter will never be 0.
There can be another problems, witch cause this behaviour.

Categories

Resources