I'm trying to understand the lock mechanism.
if I have multiple events to lock on different value should I use an object lock for each?
More serious code added:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;
namespace ValueChangeOnEventForm
{
public partial class Form1 : Form
{
private Test_Onchange DataSource;
Thread Task1;
private bool Flag_Stop_Task1;
public Form1()
{
InitializeComponent();
graph1.ChartAreas[0].AxisX.ScrollBar.Enabled = true;
graph1.ChartAreas[0].AxisX.IsLabelAutoFit = true;
graph1.ChartAreas[0].AxisX.ScaleView.Size = 100;
graph2.ChartAreas[0].AxisX.ScrollBar.Enabled = true;
graph2.ChartAreas[0].AxisX.IsLabelAutoFit = true;
graph2.ChartAreas[0].AxisX.ScaleView.Size = 100;
DataSource = new Test_Onchange();
DataSource.ValueChanged += new EventHandler(EventValueChange);//Value input info
DataSource.SecondValueChange += new EventHandler(EventSecondValueChange);//second value
Task1 = new Thread(new ThreadStart(Task_1));//create the thread
Task1.Start();//start the thread
}
protected virtual void EventSecondValueChange(object sender, EventArgs e)
{
double valueMAX = 0, size = 0;
if (graph1.InvokeRequired)
{
graph1.Invoke(new MethodInvoker(delegate { graph1.Series["ValueOnGraph"].Points.AddY(DataSource.Value); }));
graph1.Invoke(new MethodInvoker(delegate { valueMAX = graph1.ChartAreas[0].AxisX.Maximum; }));
graph1.Invoke(new MethodInvoker(delegate { size = graph1.ChartAreas[0].AxisX.ScaleView.Size; }));
if (valueMAX - 10 > size)
{
graph1.Invoke(new MethodInvoker(delegate { graph1.ChartAreas[0].AxisX.ScaleView.Scroll(graph1.ChartAreas[0].AxisX.Maximum); }));
graph1.Invoke(new MethodInvoker(delegate { graph1.Series["ValueOnGraph"].Points.RemoveAt(0); }));
}
}
}
protected virtual void EventValueChange(object sender, EventArgs e)
{
double valueMAX=0,size=0;
if (graph2.InvokeRequired)
{
graph2.Invoke(new MethodInvoker(delegate { graph2.Series["ValueOnGraph2"].Points.AddY(DataSource.Secondvalue); }));
graph2.Invoke(new MethodInvoker(delegate { valueMAX = graph2.ChartAreas[0].AxisX.Maximum; }));
graph2.Invoke(new MethodInvoker(delegate { size = graph2.ChartAreas[0].AxisX.ScaleView.Size; }));
if (valueMAX - 10 > size)
{
graph2.Invoke(new MethodInvoker(delegate { graph2.ChartAreas[0].AxisX.ScaleView.Scroll(graph2.ChartAreas[0].AxisX.Maximum); }));
graph2.Invoke(new MethodInvoker(delegate { graph2.Series["ValueOnGraph2"].Points.RemoveAt(0); }));
}
}
}
private void Task_1()
{
while (!Flag_Stop_Task1)
{
Random RandVal = new Random();
Random RandVal2 = new Random();
int Value = RandVal.Next(0, 100);
int SecondValue = RandVal2.Next(50, 200);
DataSource.Value = Value;
DataSource.Secondvalue = SecondValue;
Thread.Sleep(100);
}
Flag_Stop_Task1 = false;
}
private void btn_StopTask_1_Click(object sender, EventArgs e)
{
Flag_Stop_Task1 = true;
}
}
}
And then
namespace ValueChangeOnEventForm
{
class Test_Onchange
{
private int value;
private int secondvalue;
protected object _lock = new object();
public event System.EventHandler ValueChanged;
public event System.EventHandler SecondValueChange;
protected virtual void OnValueChange()
{
lock (this._lock)
{
EventHandler eventvaluechange = ValueChanged;
if (eventvaluechange != null)
eventvaluechange(this, EventArgs.Empty);
}
}
protected virtual void OnSecondValueChange()
{
lock (this._lock)
{
EventHandler eventvaluechange = SecondValueChange;
if (eventvaluechange != null)
eventvaluechange(this, EventArgs.Empty);
}
}
public int Value
{
get { return this.value; }
set
{
if (value != this.value)
{//if value changed enter
this.value = value;
OnValueChange();
}
}
}
public int Secondvalue
{
get { return this.secondvalue; }
set
{
if (value != this.secondvalue)
{//if value changed enter
this.secondvalue = value;
OnSecondValueChange();
}
}
}
}
}
Do I need two lock (lock1 and lock2 object or only one for both value and secondvalue....?
Thanks a lot.
Update
Ok let's do it so.
I'm using beckhoff PLC which are real time Task PLC. and I'm reading two value on it when the value change. like this:
Form1 Class:
namespace RealTimeLock
{
using Beckhoff.App.Ads.Core;
using Beckhoff.App.Ads.Core.Plc;
using TwinCAT.Ads;
using System.IO;
public partial class Form1 : Form
{
private PLC PLCData;
public Form1()
{
InitializeComponent();
}
public Form1(IBAAdsServer _adsServer)
: this()
{
PLCData = new PLC(_adsServer);
PLCData.ErrorBoolChanged += new EventHandler(EventErrorChanged);//error info
PLCData.ForceValChanged += new EventHandler(EventForceChanged);//Force input info
}
protected virtual void EventErrorChanged(object sender, EventArgs e)
{
//state of error PLC
lv_ErrorInfo.Text = "PLC Error num : " + PLCData.i_ErrorID.ToString();
}
protected virtual void EventForceChanged(object sender, EventArgs e)
{//modify graphical data PLC Force data
lv_ForceInfo.Text = PLCData.i_ForceVal.ToString();
c_graphForceIN.Series["ForceData"].Points.AddY(PLCData.i_ForceVal);
if (c_graphForceIN.ChartAreas[0].AxisX.Maximum - 10 > c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Size)
{
c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Scroll(c_graphForceIN.ChartAreas[0].AxisX.Maximum);
c_graphForceIN.Series["ForceData"].Points.RemoveAt(0);
}
}
}
}
Error ID and Force change showed in Form1 label lv_ErrorID and lv_Force and graphForceIN add point.
The events handler on the other side (PLC class) looks like this:
PLC Class:
namespace RealTimeLock
{
using Beckhoff.App.Ads.Core;
using Beckhoff.App.Ads.Core.Plc;
using TwinCAT.Ads;
using System.IO;
public partial class Form1 : Form
{
private PLC PLCData;
public Form1()
{
InitializeComponent();
}
public Form1(IBAAdsServer _adsServer)
: this()
{
PLCData = new PLC(_adsServer);
PLCData.ErrorBoolChanged += new EventHandler(EventErrorChanged);//error info
PLCData.ForceValChanged += new EventHandler(EventForceChanged);//Force input info
}
protected virtual void EventErrorChanged(object sender, EventArgs e)
{
//state of error PLC
lv_ErrorInfo.Text = "PLC Error num : " + PLCData.i_ErrorID.ToString();
}
protected virtual void EventForceChanged(object sender, EventArgs e)
{//modify graphical data PLC Force data
lv_ForceInfo.Text = PLCData.i_ForceVal.ToString();
c_graphForceIN.Series["ForceData"].Points.AddY(PLCData.i_ForceVal);
if (c_graphForceIN.ChartAreas[0].AxisX.Maximum - 10 > c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Size)
{
c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Scroll(c_graphForceIN.ChartAreas[0].AxisX.Maximum);
c_graphForceIN.Series["ForceData"].Points.RemoveAt(0);
}
}
}
}
Does it seem to be correct coding for you guys? and while I have a real time task running there do I need to lock variables and if so, do I need two lock or only one??
Thanks for your remark on this!!
Related
I am trying to auto check every new item in my checkedboxlist I have a button that does that it will auto select all, but we don't want it that way to be controlled by a button. we want it so for every new item we get automatically will get checked.
This is my button that auto select all if there are new 20 items this will auto select all and then submit those new items
Here is the code it works but is not I want I want to auto select for every new items coming in, because I have another process that will keep adding new items to the checkedboxlist, also the lst_BarcodeScanEvents is the checkedboxlist name
private void btn_SelectALLScans_Click(object sender, EventArgs e)
{
for (var i = 0; i < lst_BarcodeScanEvents.Items.Count; i++)
{
lst_BarcodeScanEvents.SetItemChecked(i, true);
}
}
I checked other sources like google and stackoverflow but I could not find anything to my request here.
Here is the main class where I have the logic of buttons, checkedlistbox and more. and the button method btn_ConnectT_Click calls the methods from the second class
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Web;
using BarcodeReceivingApp.Core.Domain;
using BarcodeReceivingApp.Functionality;
namespace BarcodeReceivingApp
{
public partial class BarcodeReceivingForm : Form
{
//GLOBAL VARIABLES
private const string Hostname = "myip";
private const int Port = 23;
private TelnetConnection _connection;
private ParseReceivingBarcode _parseReceivingBarcode;
private ButtonsDisplay _buttonsDisplay;
public BarcodeReceivingForm()
{
InitializeComponent();
//FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
}
private void btn_ConnectT_Click(object sender, EventArgs e)
{
_connection = new TelnetConnection(Hostname, Port);
_connection.ServerSocket(Hostname, Port, this);
}
private void btn_StopConnection_Click(object sender, EventArgs e)
{
_connection.Exit();
}
private void btn_RemoveItemFromListAt_Click(object sender, EventArgs e)
{
if (lst_BarcodeScanEvents.CheckedItems.Count != 0)
for (var i = lst_BarcodeScanEvents.CheckedItems.Count; i > 0; i--)
lst_BarcodeScanEvents.Items.RemoveAt(lst_BarcodeScanEvents.CheckedIndices[i - 1]);
else
MessageBox.Show(#"Element(s) Not Selected...");
}
private void BarcodeReceivingForm_Load(object sender, EventArgs e)
{
_buttonsDisplay = new ButtonsDisplay(this);
_buttonsDisplay.ButtonDisplay();
}
private void btn_ApplicationSettings_Click(object sender, EventArgs e)
{
var bcSettingsForm = new BarcodeReceivingSettingsForm();
bcSettingsForm.Show();
}
private void btn_ClearBarcodeList_Click(object sender, EventArgs e)
{
lst_BarcodeScanEvents.Items.Clear();
}
private void lst_BarcodeScanEvents_ItemAdded(object sender, ListBoxItemEventArgs e)
{
MessageBox.Show(#"Item was added at index " + e.Index + #" and the value is " + lst_BarcodeScanEvents.Items[e.Index].ToString());
}
private void btn_SubmitData_Click(object sender, EventArgs e)
{
var receivingFullBarcode = new List<string>();
_connection.GetBarcodeList();
}
private void btn_SelectALLScans_Click(object sender, EventArgs e)
{
for (var i = 0; i < lst_BarcodeScanEvents.Items.Count; i++)
{
lst_BarcodeScanEvents.SetItemChecked(i, true);
}
}
}
}
Here is the second class where I use a telnet port 23 that scan barcodes where and puts it to the checkedlistbox, now the main emethods here that inserts the data to the checkedlistbox is the method serversocket and the readwrite method
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace BarcodeReceivingApp.Functionality
{
public class TelnetConnection
{
private Thread _readWriteThread;
private TcpClient _client;
private NetworkStream _networkStream;
private string _hostname;
private int _port;
private BarcodeReceivingForm _form;
private bool _isExiting = false;
public TelnetConnection(string hostname, int port)
{
this._hostname = hostname;
this._port = port;
}
public void ServerSocket(string ip, int port, BarcodeReceivingForm f)
{
this._form = f;
try
{
_client = new TcpClient(ip, port);
}
catch (SocketException)
{
MessageBox.Show(#"Failed to connect to server");
return;
}
_networkStream = _client.GetStream();
_readWriteThread = new Thread(ReadWrite);
//_readWriteThread = new Thread(() => ReadWrite(f));
_readWriteThread.Start();
}
public void Exit()
{
_isExiting = true;
}
public void ReadWrite()
{
do
{
var received = Read();
if (received == null)
break;
if (_form.lst_BarcodeScanEvents.InvokeRequired)
{
var received1 = received;
_form.lst_BarcodeScanEvents.Invoke(new MethodInvoker(delegate
{
_form.lst_BarcodeScanEvents.AddItem(received1 + Environment.NewLine);
}));
}
} while (!_isExiting);
//var material = received.Substring(10, 5);
//_form.label5.Text += string.Join(Environment.NewLine, material);
CloseConnection();
}
public List<string> GetBarcodeList()
{
var readData = new List<string>();
foreach (string list in _form.lst_BarcodeScanEvents.Items)
{
readData.Add(list);
MessageBox.Show(list);
}
return readData;
}
public string Read()
{
var data = new byte[1024];
var received = "";
var size = _networkStream.Read(data, 0, data.Length);
if (size == 0)
return null;
received = Encoding.ASCII.GetString(data, 0, size);
return received;
}
public void CloseConnection()
{
MessageBox.Show(#"Closed Connection",#"Important Message");
_networkStream.Close();
_client.Close();
}
}
}
so now like I said before for every new items it gets inserted to the checkedlistbox I want it to be auto selected everytime it adds a new data to the checkedlistbox.
The problem, usually simple, of setting the Checked state of a newly added Item of a CheckedListBox, was somewhat complicated because of the nature of the Listbox in question.
The CheckedListBox is a Custom Control with custom events and properties.
It's actually a relatively common customization (one implementation can be see in this MSDN Forum post:
Have ListBox an event when addid or removing Items?) but it might complicate the interpretation of the events.
The Custom Control, when adding a new Item to the List (from a class other than the Form that hosts the Control), raises the custom ItemAdded event
private void [CheckedListBox]_ItemAdded(object sender, ListBoxItemEventArgs e)
The Index of the Item added is referenced by the e.Index property of the custom ListBoxItemEventArgs object.
With the code provided, it can be determined that this event handler is the natural place where the Checked state of the new Item can be set:
private void lst_BarcodeScanEvents_ItemAdded(object sender, ListBoxItemEventArgs e)
{
lst_BarcodeScanEvents.SetItemChecked(e.Index, true);
}
For that I suggest to create an event that you subscribe after you InitializeComponent() of your form like so :
public Form1()
{
InitializeComponent();
lst_BarcodeScanEvents.ControlAdded += new ControlEventHandler(AddedNewSelect);
}
private void AddedNewSelect(object sender, ControlEventArgs e)
{
lst_BarcodeScanEvents.SetItemChecked(e.Control.TabIndex, true);
}
I am making a program that reads values on NFC cards.
I have a class that reads values from Serial comms sent by an EMBED hooked up to an NFC IC. Once data has been received, the program (in the main form) must change 'page' and process the data.
I have used User Controls as different 'pages'.
I understand that there are multiple threads and i cannot change the GUI from a different thread without using some sort of Invoke/Deleagate?. However, i am unsure on how to implement this on my code.
Here is the code i have (with omissions of irrelevant code):
SerialData Class
public class SerialData
{
public SerialPort ComPort;
public String savedText;
public SerialData()
{
ComPort = new SerialPort();
// set port data
ComPort.PortName = "COM5";
ComPort.BaudRate = 9600;
ComPort.DataBits = 8;
ComPort.RtsEnable = true;
ComPort.DtrEnable = true;
}
public void readCard()
{
try
{
savedText = "";
ComPort.Open();
ComPort.DataReceived += ComPort_DataReceived;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message + ex.StackTrace);
}
}
private void ComPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var serialPort = (SerialPort)sender;
var data = serialPort.ReadExisting();
savedText += data;
}
}
Form1 Code
public partial class Form1 : Form
{
List<UserControl> Pages = new List<UserControl>();
public int CurrentPage = 0;
SerialData SerialDataDriver = new SerialData();
public Form1()
{
InitializeComponent();
Pages.Add(page1);
Pages.Add(page2);
Pages.Add(page3);
// change to first page
ChangeToPage(0);
}
private void Page1ButtonClick(object sender, EventArgs e)
{
ChangeToPage(1);
SerialDataDriver.readCard();
}
void ComPort_DataReceived(string data)
{
SerialDataDriver.ComPort.Close(); /// close ComPort
ChangeToPage(2);
}
}
Here's an example of a custom event:
public class SerialData
{
public SerialPort ComPort;
public String savedText;
public delegate void Data(string message);
public event Data NewData;
private void ComPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var serialPort = (SerialPort)sender;
var data = serialPort.ReadExisting();
savedText += data;
if (NewData != null)
{
NewData(savedText);
}
}
}
...and how to wire up and receive it in the form:
public Form1()
{
InitializeComponent();
Pages.Add(page1);
Pages.Add(page2);
Pages.Add(page3);
// change to first page
ChangeToPage(0);
SerialDataDriver.NewData += SerialDataDriver_NewData;
}
private void SerialDataDriver_NewData(string message)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker) delegate() {
SerialDataDriver_NewData(message);
});
}
else
{
// do something with "message"
this.label1.Text = message;
}
}
You can make a wrapper that implements SerialPort events using custom add and remove accessors:
using System;
using System.IO.Ports;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private readonly SerialData _data;
public Form1()
{
InitializeComponent();
Closing += (sender, args) => { _data.Dispose(); };
_data = new SerialData(new SerialPort("COM5", 9600));
_data.DataReceived += Serial_DataReceived;
}
private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// do something
}
}
public class SerialData : IDisposable
{
private readonly object _locker = new object();
private readonly SerialPort _port;
public SerialData(SerialPort port)
{
_port = port ?? throw new ArgumentNullException(nameof(port));
}
#region IDisposable Members
public void Dispose()
{
_port.Dispose();
}
#endregion
public event SerialDataReceivedEventHandler DataReceived
{
add
{
lock (_locker)
{
_port.DataReceived += value;
}
}
remove
{
lock (_locker)
{
_port.DataReceived -= value;
}
}
}
}
}
I'm a new beginner on this topic. I created a c# win form. In this form, I have two textboxes and one label. What I want to do is create a delegate event to track the textbox's change and add up two numbers from textbox1 and textbox2. The label will show the result automatically. Hope someone can provide me a example for this, thank you so much! There is something I have right now,
events.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Project3
{
public delegate void Calculate(int obj1, int obj2);
public class events
{
int result;
public int Add(int x, int y)
{
result = x + y;
return result;
}
}
}
Form1.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Project3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
label1.Text ="";
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
}
}
}
If you just want to learn how to delegate the result into the label, and learn the delegate and event, here is a sample you may want to try and analyze for learning purposes:
Sample 1:
public delegate int CalculateEventHandler(object obj1, object obj2);
public partial class Form1 : Form
{
public event CalculateEventHandler Calculate;
private string OnCalculate(string text1, string text2)
{
string result = "0";
if (this.Calculate != null)
{
result = this.Calculate(this.textBox1.Text, this.textBox2.Text).ToString();
}
return result;
}
public Form1()
{
this.InitializeComponent();
this.InitializeEvent();
}
private void InitializeEvent()
{
this.Calculate += Form1_Calculate;
}
private int Form1_Calculate(object obj1, object obj2)
{
int a = 0;
int b = 0;
int.TryParse(obj1.ToString(), out a);
int.TryParse(obj2.ToString(), out b);
return a + b;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
this.label1.Text = OnCalculate(this.textBox1.Text, this.textBox2.Text);
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
this.label1.Text = OnCalculate(this.textBox1.Text, this.textBox2.Text);
}
}
Sample 2:
Form1.cs
public partial class Form1 : Form
{
public Form1()
{
this.InitializeComponent();
this.InitializeEvent();
}
private void InitializeEvent()
{
Event.Calculate += Event_Calculate;
}
private int Event_Calculate(object obj1, object obj2)
{
int x = 0;
int y = 0;
int.TryParse(obj1.ToString(), out x);
int.TryParse(obj2.ToString(), out y);
return Event.Add( x, y );
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
this.label1.Text = Event.OnCalculate(this.textBox1.Text, this.textBox2.Text).ToString();
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
this.label1.Text = Event.OnCalculate(this.textBox1.Text, this.textBox2.Text).ToString();
}
}
Event.cs
public delegate int CalculateEventHandler(object obj1, object obj2);
public class Event
{
static public event CalculateEventHandler Calculate;
static public int Add(int x, int y)
{
int result = x + y;
return result;
}
static public int OnCalculate( object obj1, object obj2 )
{
int result = 0;
if( Calculate != null )
{
result = Calculate(obj1, obj2);
}
return result;
}
}
NOTE: The above examples are by no means a good approach to calculate two values, this just serves as an example. The disadvantage of this approach would lead you to somehow spaghetti code, going back and forth to where the logic is going.
There is a simple solution
private void textBox1_TextChanged(object sender, EventArgs e)
{
try
{
if (textBox2.Text == string.Empty)
{
//textBox2.Text = (0).ToString();
label1.Text = ( Convert.ToInt32(textBox1.Text)).ToString();
}
else if (textBox1.Text == string.Empty)
{
label1.Text = (Convert.ToInt32(textBox2.Text)).ToString();
}
else
{
label1.Text = (Convert.ToInt32(textBox1.Text) + Convert.ToInt32(textBox2.Text)).ToString();
}
}
catch (Exception e3)
{
MessageBox.Show(e3.Message);
}
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
try
{
if (textBox2.Text == string.Empty)
{
//textBox2.Text = (0).ToString();
label1.Text = (Convert.ToInt32(textBox1.Text)).ToString();
}
else if (textBox1.Text == string.Empty)
{
label1.Text = (Convert.ToInt32(textBox2.Text)).ToString();
}
else
{
label1.Text = (Convert.ToInt32(textBox1.Text) + Convert.ToInt32(textBox2.Text)).ToString();
}
}
catch (Exception e3)
{
MessageBox.Show(e3.Message);
}
}
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.
Basically i would like to update ProgressBar UI object on the FormMain (WindowsForm). I am using .NET 4.0
Here are the code in the Form1.Designer.cs
namespace ProgressBarApp
{
public partial class Form1 : Form
{
private System.Windows.Forms.ProgressBar curProgressBar;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
CustomProcess theProcess = new CustomProcess();
theProcess.Process();
}
}
}
Here is the definition of CustomProcess.cs
namespace ProgressBarApp
{
class CustomProcess
{
public void Process()
{
for (int i = 0; i < 10; i++)
{
Task ProcessATask = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000); // simulating a process
}
);
Task UpdateProgressBar = ProcessATask.ContinueWith((antecedent) =>
{
// how do i update the progress bar object at UI here ?
}
);
}
}
}
}
You can use SynchronizationContext to do this. To use it for a Task, you need to create a TaskScheduler, which you can do by calling TaskScheduler.FromCurrentSynchronizationContext:
Task UpdateProgressBar = ProcessATask.ContinueWith(antecedent =>
{
// you can update the progress bar object here
}, TaskScheduler.FromCurrentSynchronizationContext());
This will work only if you call Process() directly from the UI thread.
How about using System.Reactive.Linq:
[UPDATE]
using System.Reactive.Linq;
namespace WindowsFormsApplication6
{
public partial class Form1 : Form
{
//private System.Windows.Forms.ProgressBar curProgressBar;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
CustomProcess theProcess = new CustomProcess();
var x = Observable.FromEventPattern(theProcess, "TaskCompleted");
curProgressBar.Maximum = 4;
x.Subscribe((a) =>
{
curProgressBar.Value = ((CustomProcess)a.Sender).Counter;
});
theProcess.Process();
}
}
class CustomProcess
{
public int Counter { get; set; }
public event EventHandler TaskCompleted = OnTaskCompleted;
private static void OnTaskCompleted(object sender, EventArgs e)
{
((CustomProcess)sender).Counter++;
}
public void Process()
{
for (int i = 0; i <= 3; i++)
{
Task ProcessATask = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000); // simulating a process
}
);
var awaiter = ProcessATask.GetAwaiter();
awaiter.OnCompleted(() =>
{
TaskCompleted(this, null);
});
}
}
}
}