I'm writing a small WPF application in C# using Kinect and COMs to communicate to an arduino.
I have a combobox where the user can choose the port that he wants to use, I have a watcher on my COMs and everytime a device is Plugged/Unplugged I want to refresh my combobox.
My problem is that the calling to the function is in another class so I have a thread error
Here is the portion of code that doesn't work :
namespace Microsoft.Samples.Kinect.SkeletonBasics
{
using System.IO;
using System.IO.Ports;
using System;
using System.Management;
using System.Windows;
using System.Windows.Media;
using System.Collections.Generic;
using System.Windows.Controls;
using Microsoft.Kinect;
using Microsoft.Kinect.Toolkit;
public partial class MainWindow : Window
{
private WMIReceiveEvent receiveEvent = new WMIReceiveEvent();
internal static ComboBox comboBox; //Internal static variable so it can be used in static method
public MainWindow()
{
InitializeComponent();
}
private void WindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
using (receiveEvent) {}
}
private void ComboBox_Loaded(object sender, RoutedEventArgs e)
{
comboBox = Com_ComboBox;
string[] ports = SerialPort.GetPortNames();
if (ports.Length == 0)
{
Default_Text.Content = "Aucun port détecté";
comboBox.IsHitTestVisible = false;
comboBox.Focusable = false;
}
else Default_Text.Content = "Arduino sur le port :";
comboBox.SelectedIndex = 0;
foreach (string port in ports)
{
comboBox.Items.Add(port);
}
}
internal static void Refresh_Coms() //I'm trying to call this function from the other class but I get a thread error
{
comboBox.Items.Clear();
ComboBoxItem Default_Text = (ComboBoxItem) comboBox.ItemContainerGenerator.ContainerFromIndex(0);
string[] ports = SerialPort.GetPortNames();
if (ports.Length == 0)
{
Default_Text.Content = "Aucun port détecté";
comboBox.IsHitTestVisible = false;
comboBox.Focusable = false;
comboBox.SelectedIndex = 0;
}
else Default_Text.Content = "Arduino sur le port :";
foreach (string port in ports)
{
comboBox.Items.Add(port);
}
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
}
public class WMIReceiveEvent : IDisposable
{
private ManagementEventWatcher watcherAttach;
private ManagementEventWatcher watcherDetach;
public WMIReceiveEvent()
{
try
{
watcherAttach = new ManagementEventWatcher();
watcherAttach.EventArrived += Attaching;
watcherAttach.Query = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
watcherAttach.Start();
watcherDetach = new ManagementEventWatcher();
watcherDetach.EventArrived += Detaching;
watcherDetach.Query = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
watcherDetach.Start();
return;
}
catch (ManagementException err)
{
MessageBox.Show("An error occurred while trying to receive an event: " + err.Message);
}
}
public void Dispose()
{
try
{
watcherAttach.Stop();
watcherDetach.Stop();
watcherAttach.Dispose();
watcherDetach.Dispose();
}
catch
{
MessageBox.Show("An error occurred while trying to close COM event Handler");
}
}
void Attaching(object sender, EventArrivedEventArgs e)
{
if (sender != watcherAttach) return;
Console.WriteLine("Attaching");
MainWindow.Refresh_Coms(); //I can call the function but the thread can't modify the ComboBox
}
void Detaching(object sender, EventArrivedEventArgs e)
{
if (sender != watcherDetach) return;
Console.WriteLine("Detaching");
MainWindow.Refresh_Coms();
}
~WMIReceiveEvent()
{
this.Dispose();
}
}
}
I'm a little new to C#, I've searched on the web but I can't find an easy solution for doing that, can somebody help me please ?
Add SynchronizationContext to Your WMIReceiveEvent class, like:
public class WMIReceiveEvent : IDisposable
{
private readonly SynchronizationContext _syncContext;
public WMIReceiveEvent(SynchronizationContext syncContext)
{
_cyncContext = syncContext;
}
}
and wrap of calling MainWindow.Refresh_Coms(); with Send method of SynchronizationContext:
_syncContext.Send(o => MainWindow.Refresh_Coms(), null);
And last thing is creating Your WMIReceiveEvent class:
private WMIReceiveEvent receiveEvent = new WMIReceiveEvent(SynchronizationContext.Current);
More about SynchronizationContext.
Related
I am writing a program which is supposed to detect when a USB serial device is plugged in and then log on to the new com port. The code below works wonderfully, but I have noticed in debugging the code and stepping through it that the event handler "DetectChange" fires twice. I'm not sure that this is normal, or an action of the debugger.
In any case, the code works, but I am new at event handling and I would like to make sure that I am not going to cause any issues as I add more code to actually read and write from the serial port.
(I got some of this code from stackoverflow, but I have misplaced my paper with names for attribution. If you see your code below, my heartfelt thanks.)
using System;
using System.IO.Ports;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Management;
using System.Threading;
namespace SerialTest
{
public partial class Form1 : Form
{
SerialMethods serialMethods = new SerialMethods();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
loadCmdBox();
}
private void CmdBoxPort_SelectedIndexChanged(object sender, EventArgs e)
{
handleComPort();
}
private void handleComPort()
{
// Set the right port for the selected item.
// The portname is based on the "COMx" part of the string (SelectedItem)
string item = CmdBoxPort.SelectedItem.ToString();
// Search for the expression "(COM" in the "selectedItem" string
if (item.Contains("(COM"))
{
// Get the index number where "(COM" starts in the string
int indexOfCom = item.IndexOf("(COM");
// Set PortName to COMx based on the expression in the "selectedItem" string
// It automatically gets the correct length of the COMx expression to make sure
// that also a COM10, COM11 and so on is working properly.
string PortName = item.Substring(indexOfCom + 1, item.Length - indexOfCom - 2);
if (serialMethods._serialPort.IsOpen)
{
serialMethods._serialPort.Close();
serialMethods.Connect(PortName);
label5.Text = "Active Port: " + PortName;
}
else
{
serialMethods.Connect(PortName);
label5.Text = PortName;
}
}
else
return;
}
private void loadCmdBox()
{
// Get all serial (COM)-ports you can see in the devicemanager
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\cimv2",
"SELECT * FROM Win32_PnPEntity WHERE ClassGuid=\"{4d36e978-e325-11ce-bfc1-08002be10318}\"");
// Sort the items in the combobox
CmdBoxPort.Sorted = true;
// Add all available (COM)-ports to the combobox
foreach (System.Management.ManagementObject queryObj in searcher.Get().Cast<ManagementObject>())
{
CmdBoxPort.Items.Add(queryObj["Caption"]);
}
SerialPortService.PortsChanged += (sender1, changedArgs) => DetectChange(changedArgs.EventType);
label2.Text = "";
label3.Text = "";
label4.Text = "";
}
protected Task<Task> getSerPorts()
{
CmdBoxPort.Text = "";
CmdBoxPort.Update();
if (!String.IsNullOrEmpty(CmdBoxPort.Text))
{
handleComPort();
return Task.FromResult(Task.CompletedTask);
}
else
{
loadCmdBox();
return Task.FromResult(Task.CompletedTask);
}
}
private void ExitButton_Click(object sender, EventArgs e)
{
SerialPortService.CleanUp();
this.Close();
}
private void RefreshButton_Click(object sender, EventArgs e)
{
refresh();
}
protected Task<Task> refresh()
{
label2.Text = "";
label3.Text = "";
label4.Text = "";
CmdBoxPort.Items.Clear();
getSerPorts();
return Task.FromResult(Task.CompletedTask);
}
protected virtual void DetectChange(EventType changedArgs)
{
if (changedArgs == EventType.Insertion)
{
try
{
Task tr = (Task)Invoke(new Action( () => { getSerPorts(); }));
Task rr = (Task)Invoke(new Action(() => { refresh(); }));
}
catch (Exception ex) { MessageBox.Show("Exception at insertion invoke method " + ex, "Exception", MessageBoxButtons.OK); }
}
else if (changedArgs == EventType.Removal)
{
try
{
Task tr = (Task)Invoke(new Action( () => { getSerPorts(); }));
Task rr = (Task)Invoke(new Action(() => { refresh(); }));
}
catch (Exception ex) { MessageBox.Show("Exception at removal invoke method " + ex, "Exception", MessageBoxButtons.OK); }
}
return;
}
}
public static class SerialPortService
{
private static SerialPort _serialPort;
private static string[] _serialPorts;
private static ManagementEventWatcher arrival;
private static ManagementEventWatcher removal;
private static readonly SerialMethods SD = new SerialMethods();
static SerialPortService()
{
_serialPorts = SerialPort.GetPortNames();
MonitorDeviceChanges();
}
public static void CleanUp()
{
arrival.Stop();
removal.Stop();
}
public static event EventHandler<PortsChangedArgs> PortsChanged;
private static void MonitorDeviceChanges()
{
try
{
var deviceArrivalQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
var deviceRemovalQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
arrival = new ManagementEventWatcher(deviceArrivalQuery);
removal = new ManagementEventWatcher(deviceRemovalQuery);
arrival.EventArrived += (o, args) => RaisePortsChangedIfNecessary(EventType.Insertion);
removal.EventArrived += (sender, eventArgs) => RaisePortsChangedIfNecessary(EventType.Removal);
// Start listening for events
arrival.Start();
removal.Start();
}
catch (ManagementException err)
{
MessageBox.Show("Management exception = " + err, "Info", MessageBoxButtons.OK);
}
}
private static void RaisePortsChangedIfNecessary(EventType eventType)
{
lock (_serialPorts)
{
var availableSerialPorts = SerialPort.GetPortNames();
if (eventType == EventType.Insertion)
{
var added = availableSerialPorts.Except(_serialPorts).ToArray();
_serialPorts = availableSerialPorts;
PortsChanged.Raise(null, new PortsChangedArgs(eventType, added));
}
else if (eventType == EventType.Removal)
{
var removed = _serialPorts.Except(availableSerialPorts).ToArray();
_serialPorts = availableSerialPorts;
PortsChanged.Raise(null, new PortsChangedArgs(eventType, removed));
}
}
}
public static void Raise<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs
{
handler?.Invoke(sender, args);
}
}
public enum EventType
{
Insertion,
Removal,
}
public class PortsChangedArgs : EventArgs
{
private readonly EventType _eventType;
private readonly string[] _serialPorts;
public PortsChangedArgs(EventType eventType, string[] serialPorts)
{
_eventType = eventType;
_serialPorts = serialPorts;
}
public string[] SerialPorts => _serialPorts;
public EventType EventType => _eventType;
}
}
Just took a short look at this. It seems like getSerPorts() will always execute loadCmdBox() (CmdBoxPort.Text = ""; ... if (!String.IsNullOrEmpty(CmdBoxPort.Text))) that will attach a new event handler (previous attached event handlers will not be removed by attaching a new one).
You should either remove the existing event handler befor attaching a new one or only attach the event handler once.
I am modifying to first question attempt.
I need help passing the data from a listbox and pass it to another method so every time the listbox gets data add it from the tread, it should also send that data to my new method and it to the my list because in that method I will be doing some parsing because the data from the listbox is a long string barcode but I don't need help on parsing the data because I can do that, the part I need help only is to pass the data from thread that is passing it to the listbox it should also be send to my method ReadAllData() so in that method I will received the data and then I will do the parsing the make a return.
Here is the method where the listbox is stored and receives the data from a thread from telnet port 23
Here is the code I need to send the data from the lst_BarcodeScan to the method ReadAllData method and store to my list.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.Remoting.Messaging;
using System.Security.Authentication.ExtendedProtection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace BarcodeReceivingApp
{
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 TelnetConnection()
{
}
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()
{
var received = "";
do
{
received = Read();
if (received == null)
break;
if (_form.lst_BarcodeScan.InvokeRequired)
{
_form.lst_BarcodeScan.Invoke(new MethodInvoker(delegate
{
_form.lst_BarcodeScan.Items.Add(received + Environment.NewLine);
}));
}
} while (!_isExiting);
CloseConnection();
}
public List<string> ReadAllData()
{
var obtainData = new List<string>();
return obtainData;
}
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();
}
}
}
Main class that will call the methods from the telnetconnection class or any other classes I will add.
using System;
using System.Windows.Forms;
namespace BarcodeReceivingApp
{
public partial class BarcodeReceivingForm : Form
{
//GLOBAL VARIABLES
private const string Hostname = "myip";
private const int Port = 23;
private TelnetConnection _connection;
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 = new TelnetConnection(Hostname, Port);
//_connection.ServerSocket(Hostname, Port, this);
_connection.Exit();
}
private void btn_RemoveItemFromListAt_Click(object sender, EventArgs e)
{
for (var i = lst_BarcodeScan.SelectedIndices.Count - 1; i >= 0; i--)
{
lst_BarcodeScan.Items.RemoveAt(lst_BarcodeScan.SelectedIndices[i]);
}
}
private void BarcodeReceivingForm_Load(object sender, EventArgs e)
{
lst_BarcodeScan.SelectionMode = SelectionMode.MultiSimple;
}
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_BarcodeScan.Items.Clear();
}
}
}
The easiest way to implement without adding more complexity to the thread is to simply raise an event on the listbox when and item is added to it. The problem is that the listbox do no have any events that allow to do that but you can create your own version with a dozen lines of code.
The goal is to create a derived control of the listbox then you add to it a method to trigger a custom event when an item is added and bingo.
Here's the custom listbox class with the custom EventArgs.
// custom override class over the list box so we can create an event when items are added
public class ListBoxWithEvents : ListBox
{
// the event you need to bind to know when items are added
public event EventHandler<ListBoxItemEventArgs> ItemAdded;
// method to call to add items instead of lst.Items.Add(x);
public void AddItem(object data)
{
// add the item normally to the internal list
var index = Items.Add(data);
// invoke the event to notify the binded handlers
InvokeItemAdded(index);
}
public void InvokeItemAdded(int index)
{
// invoke the event if binded anywhere
ItemAdded?.Invoke(this, new ListBoxItemEventArgs(index));
}
}
// basic event handler that will hold the index of the item added
public class ListBoxItemEventArgs : EventArgs
{
public int Index { get; set; } = -1;
public ListBoxItemEventArgs(int index)
{
Index = index;
}
}
Now you need to change your listbox on your form with the ListBoxWithEvents instead. You have 2 ways to do this but i'll give you the easiest. Compile your code and go in the design window for the form. In your toolbox you should have the ListBoxWithEvents control now and you can simply drag and drop in your form and replace the one you have. Since it derive from the listbox all it has is extra features. It didn't lose anything it had before.
Now you need to change 2 things. In your form select the new ListBoxWithEvents control and go in the properties events and you will find the new event called ItemAdded you can double click that and it should create an event like the following. I have also thrown in a sample MessageBox that display all you will need.
private void ListBox1_ItemAdded(object sender, ListBoxItemEventArgs e)
{
MessageBox.Show("Item was added at index " + e.Index + " and the value is " + listBox1.Items[e.Index].ToString());
}
Finally in order to trigger that event you need to use the new method lst.AddItem(object); instead of lst.Items.Add(object); so according to your sample code you need to change this :
_form.lst_BarcodeScan.Invoke(new MethodInvoker(delegate
{
_form.lst_BarcodeScan.Items.Add(received + Environment.NewLine);
}));
to this :
_form.lst_BarcodeScan.Invoke(new MethodInvoker(delegate
{
_form.lst_BarcodeScan.AddItem(received + Environment.NewLine);
}));
Try it and now you should have that event fire every time something is added to the list.
Since you are pretty new to programming i find it important to mention that this event will trigger on the UI thread and not the thread you created. This mean it behave normally like clicking on a button triggers a button_click(object sender, EventArgs e) event. No special thread involved whatsoever.
I open multiple serial ports and assign the DataReceived event to a single method. If now multiple com ports receive at the same time something, SerialPort_DataReceived is called parallel(?) so i tried to use lock so that only one event could be handled at the same time.
using System;
using System.Collections.Generic;
using System.Windows;
using System.IO.Ports;
using System.Text;
namespace MainApplication
{
public partial class MainWindow : Window
{
private SerialConnectionHandler m_SerialConnectionHandler;
public MainWindow()
{
InitializeComponent();
m_SerialConnectionHandler = new SerialConnectionHandler();
m_SerialConnectionHandler.ResponseReceived += SerialConnectionHandler_ResponseReceived;
}
private void SerialConnectionHandler_ResponseReceived(object sender, EventArgs e)
{
// Do something.
}
}
public class SerialConnectionHandler
{
private List<SerialPort> m_SerialConnections;
private List<SerialCommand> m_CommandQueue;
private object m_DataReceivedLock;
public event EventHandler ResponseReceived;
public SerialConnectionHandler()
{
m_SerialConnections = new List<SerialPort>();
m_CommandQueue = new List<SerialCommand>();
m_DataReceivedLock = new object();
foreach (var comPortName in SerialPort.GetPortNames())
{
var newSerialPort = new SerialPort(comPortName);
newSerialPort.DataReceived += SerialPort_DataReceived;
var newSerialCommand = new SerialCommand(comPortName, "Command", "Response");
newSerialPort.Open();
newSerialPort.Write(newSerialCommand.Command, 0, newSerialCommand.Command.Length);
m_SerialConnections.Add(newSerialPort);
}
}
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
lock (m_DataReceivedLock)
{
var serialPort = (SerialPort)sender;
var receivedContent = new byte[serialPort.BytesToRead];
serialPort.Read(receivedContent, 0, receivedContent.Length);
// Clear in buffer.
serialPort.DiscardInBuffer();
// Do something which could lead to problems if multiple com ports receive at the same time something.
foreach (var command in m_CommandQueue.FindAll(command => command.SerialPortName.Equals(serialPort.PortName)))
{
if (command.ExpectedResponse.Equals(receivedContent.ToString()))
{
ResponseReceived?.Invoke(this, new EventArgs());
m_CommandQueue.Remove(command);
break;
}
}
}
}
}
public class SerialCommand
{
public string SerialPortName { get; }
public byte[] Command { get; }
public string ExpectedResponse { get; }
public SerialCommand(string serialPortName, string command, string expectedResponse)
{
SerialPortName = serialPortName;
Command = Encoding.ASCII.GetBytes(command);
ExpectedResponse = expectedResponse;
}
}
}
My question is now, could lock lead to missed received content due to the fact that not every event is handled immediately? I'm calling SerialPort.Read() and SerialPort.DiscardInBuffer() from inside the lock block.
I have a C# library, inside which there is a timer that keeps checking a boolean variable ProcessFinished. ProcessFinished is initialized as false.
What I want is that the main application needs to watch the variable Status from the library, and a message box should display once this ProcessFinished becomes true.
The problem I had is the message box never display if I simple execute the main application, but it displays if I step in the main application.
Here is the timer_tick code in main application:
public Window1()
{
_fl = new FijiLauncherControl();
this._statusTimer = new System.Windows.Forms.Timer(); // read log 4 times per sec
this._statusTimer.Interval = 125;
this._statusTimer.Tick += new EventHandler(_statusTimer_Tick);
InitializeComponent();
}
void _statusTimer_Tick(object sender, EventArgs e)
{
try
{
if (_fl.ProcessFinished)
{
System.Windows.MessageBox.Show("Process is finished");
_statusTimer.Stop();
}
}
catch (Exception ex)
{
}
}
private void FijiLaucherButton_Click(object sender, RoutedEventArgs e)
{
_statusTimer.Start();
_fl.LaunchFiji();
}
where the _fl is the object of the class from the other library.
Inside the library, the timer code is like this:
public FijiLauncherControl()
{
_ijmFile = "";
_fijiExeFile = "";
_logFile = "";
_outputDir = "";
_isLogOn = false;
_processOn = false;
_processFinished = false;
_headless = true;
_doneStr = "Procedure is finished.";
_logFileCheckTimer = new System.Timers.Timer(500); // read log 4 times per sec
_logFileCheckTimer.Enabled = true;
_logFileCheckTimer.Elapsed += new System.Timers.ElapsedEventHandler(_logFileCheckTimer_Elapsed);
}
void _logFileCheckTimer_Elapsed(object sender, EventArgs e)
{
if (_processOn && IsLogOn)
{
try
{
_processFinished = CheckStatuts();
}
catch (Exception ex)
{
}
}
}
I am wondering what is going on here? Is there anyway I can see the message box shows up without stepping in? What is the right way to watch ProcessFinished from the main application?
Would it not be better to fire an event from the thread and catch it. Then show the message box?
Like this maybe?
using System;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click( object sender, EventArgs e )
{
var logChecker = new LogChecker();
logChecker.FinishedExvent += () => MessageBox.Show( "Finished" );
logChecker.Start();
}
}
internal class LogChecker
{
public void Start()
{
var thread = new Thread( CheckLog );
thread.Start();
}
private void CheckLog()
{
var progress = 0;
while ( progress < 3000 )
{
Thread.Sleep( 250 );
progress += 250;
}
FinishedExvent();
}
public event TestEventHandler FinishedExvent;
}
internal delegate void TestEventHandler();
}
Try
volatile bool _processFinished;
I have a working solution that reports progress & text to a progress bar and a label on the applications's main form. I have now moved my worker methods to a class to they are accessible across multiple forms etc.
Within the worker methods are BW.ReportProgress() statements that push back the progress and text to the BackgroundWorker in the main form.
To give a better idea here is the file layout:
MainScreen.cs
List repSelected = new List();
XMLandRar xXMLandRar = new XMLandRar();
private void Rarbtn_Click(object sender, EventArgs e)
{
GetReps();
//Run worker
if (!CreateRarBW.IsBusy)
{
CreateRarBW.RunWorkerAsync();
}
}
//Worker
private void CreateRarBW_DoWork(object sender, DoWorkEventArgs e)
{
xXMLandRar.RarFiles(repSelected);
}
//Progress reporting
private void CreateRarBW_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progBar.Value = e.ProgressPercentage;
Statuslbl.Text = e.UserState.ToString();
}
Then my newly created Class that encompasses all of the worker methods and is to push progress to the main form.
XMLandRar.cs
public class XMLandRar
{
public void RarFiles(List repSelected)
{
int step = 100 / repSelected.Count();
int i = 0;
//Iterate through list and run rar for each
foreach (string rep in repSelected)
{
CreateRarBW.ReportProgress(i, "Raring files for " + rep);
DirectoryExists(rep);
ProcessRunner(rep);
i += step;
CreateRarBW.ReportProgress(i, "Raring files for " + rep);
}
}
}
The problem I am having is that in the XMLandRar class the CreateRarBW is not recognised (obviously) - how can I make a ReportProgress call to the BW in the main screen of the application?
Create an event in your XMLandRar class which you could subscribe to.
This way the XMLandRar class doesn't need to know or care about the UI or progressbar, it only cares about sending a message if anyone would listen. And there can also be more than one subscriber (let's say if you want to report to the background worker and a log, maybe)
Example:
private void Rarbtn_Click(object sender, EventArgs e)
{
GetReps();
//Run worker
if (!CreateRarBW.IsBusy)
{
// This should be done once, maybe in the contructor. Bind to new event.
xXMLandRar.ReportProgress += new EventHandler<XMLandRar.ProgressArgs>(xXMLandRar_ReportProgress);
CreateRarBW.RunWorkerAsync();
}
}
protected void xXMLandRar_ReportProgress(object sender, XMLandRar.ProgressArgs e)
{
// Call the UI backgroundworker
CreateRarBW.ReportProgress(e.Percentage, e.Message);
}
public class XMLandRar
{
// Event handler to bind to for reporting progress
public EventHandler<ProgressArgs> ReportProgress;
// Eventargs to contain information to send to the subscriber
public class ProgressArgs : EventArgs
{
public int Percentage { get; set; }
public string Message { get; set; }
}
public void RarFiles(List repSelected)
{
int step = 100 / repSelected.Count();
int i = 0;
//Iterate through list and run rar for each
foreach (string rep in repSelected)
{
// Report progress if somebody is listening (subscribed)
if (ReportProgress != null)
{
ReportProgress(this, new ProgressArgs { Percentage = i, Message = "Raring files for " + rep });
}
DirectoryExists(rep);
ProcessRunner(rep);
i += step;
// Report progress if somebody is listening (subscribed)
if (ReportProgress != null)
{
ReportProgress(this, new ProgressArgs { Percentage = i, Message = "Raring files for " + rep });
}
}
}
}
The sender object in the DoWork callback is the BackgroundWorker instance which is calling this callback.
This enables to use the instance and add it to your new XMLandRar class.
private void CreateRarBW_DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker.
xXMLandRar.RarFiles(repSelected, worker);
}
XMLandRar.cs
public class XMLandRar
{
public void RarFiles(List repSelected, BackgroundWorker worker)
{
// ...
}
}
Or you set the BackgroundWorkerinstance as a class property to the XMLandRar.
public class XMLandRar
{
protected BackgroundWorker mWorker;
public XMLandRar(BackgroundWorker worker) {
mWorker = BackgroundWorker;
}
public void RarFiles(List repSelected)
{
// Do something with {mWorker}
}
}
Or as mentioned in the comments, using events in the XMLandRar class.
XMLandRar.cs
public class XmlandRarCompletedEventArgs : EventArgs
{
public readonly bool Finished;
public readonly bool Canceled;
public XmlandRarCompletedEventArgs(bool finished)
{
Finished = finished;
Canceled = !finished;
}
}public class OnXmlandRarUpdateEventArgs : EventArgs
{
public readonly int Percentage;
public readonly string Message;
public XmlandRarCompletedEventArgs(int perc) :
this(perc, "") {
}
public XmlandRarCompletedEventArgs(int perc, string message)
{
Percentage = perc;
Message = message;
}
}
public delegate void OnXmlandRarDoWorkHandler(object o);
public delegate void OnXmlandRarUpdateHandler(object o, OnXmlandRarUpdateEventArgs args);
public delegate void OnXmlandRarCompleteHandler(object o, XmlandRarCompletedEventArgs args);
public class XMLandRar
{
public event OnXmlandRarDoWorkHandler OnDoWork;
public event OnXmlandRarUpdateHandler OnUpdate;
public event OnXmlandRarCompletedHandler OnComplete;
public void RarFiles(List repSelected)
{
TriggerDoWork();
int step = 100 / repSelected.Count();
int i = 0;
//Iterate through list and run rar for each
foreach (string rep in repSelected)
{
TriggerUpdate(i, "Raring files for " + rep);
DirectoryExists(rep);
ProcessRunner(rep);
i += step;
TriggerUpdate(i, "Raring files for " + rep);
}
TriggerComplete(true);
}
private void TriggerDoWork()
{
if (OnDoWork != null) {
OnDoWork(this);
}
}
private void TriggerUpdate(perc) {
}
if (OnUpdate != null) {
OnUpdate(this, new OnXmlandRarUpdateEventArgs(perc));
}
private void TriggerUpdate(perc, string message)
{
if (OnUpdate != null) {
OnUpdate(this, new OnXmlandRarUpdateEventArgs(perc, message));
}
}
private void TriggerComplete(bool finished)
{
if (OnComplete != null) {
OnComplete(this, new XmlandRarCompletedEventArgs(finished));
}
}
}
Usage:
private void CreateRarBW_DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
// Attach events to class
xXMLandRar.OnDoWork += delegate(object o) {
// ...
};
xXMLandRar.OnUpdate += delegate(object o, OnXmlandRarUpdateEventArgs args) {
// ...
};
xXMLandRar.OnComplete += delegate(object o, XmlandRarCompletedEventArgs args) {
// ...
};
xXMLandRar.RarFiles(repSelected, worker);
}
Hopefully without typos 'cause I'm in the office.
I have fixed errors in the code submitted and cleaned it up... This is a working sample that will help those that maybe couldn't understand the code since it was broken as it was... Although I would like to say that the intent and functionality of the code after it was cleaned up and enhanced is excellent.
This is working code that can get you started in your project for using a backGroundWorker thread for whatever you need.
Just modify this method -
public void RarFiles(List<string> repSelected)
To do whatever work you need. You will also have to modify the arguments you plan on using.. i.e. you may need a DataTable or some custom object...
You can modify the
public class OnXmlandRarUpdateEventArgs : EventArgs
For your needs.. that way when you get a callback.. you can update your main UI form with the changes made to those items..
You may need to do some tweaking.. but you see what i mean..
This is the Form Code.. Don't forget to create a button on the form...
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 ThreadSample
{
public partial class Form1 : Form
{
List<string> repSelected = new List<string>();
XMLandRar xXMLandRar = new XMLandRar();
BackgroundWorker CreateRarBW = new BackgroundWorker();
public Form1()
{
InitializeComponent();
repSelected = new List<string> { "asdf", "asdfsd", "h;ljj" };
CreateRarBW.DoWork += new DoWorkEventHandler(CreateRarBW_DoWork);
}
private void Rarbtn_Click(object sender, EventArgs e)
{
//GetReps();
//Run worker
if (!CreateRarBW.IsBusy)
{
// This should be done once, maybe in the contructor. Bind to new event.
xXMLandRar.ReportProgress += new EventHandler<XMLandRar.ProgressArgs>(xXMLandRar_ReportProgress);
CreateRarBW.RunWorkerAsync();
}
}
protected void xXMLandRar_ReportProgress(object sender, XMLandRar.ProgressArgs e)
{
// Call the UI backgroundworker
CreateRarBW.ReportProgress(e.Percentage, e.Message);
}
//private void CreateRarBW_DoWork(object sender, DoWorkEventArgs e)
//{
// var worker = sender as BackgroundWorker;
// xXMLandRar.RarFiles(repSelected, worker);
//}
private void CreateRarBW_DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
// Attach events to class
xXMLandRar.OnDoWork += delegate(object o)
{
// ...
MessageBox.Show("Hey ... Something is going on over there in the classLib .. " + o);
};
xXMLandRar.OnUpdate += delegate(object o, OnXmlandRarUpdateEventArgs args)
{
// ...
//foreach (object oo in args)
{
MessageBox.Show("Hey ... Something is going on over there in the classLib .. Message is " + args.Message + " and Percentage is " + args.Percentage);
}
};
xXMLandRar.OnComplete += delegate(object o, XmlandRarCompletedEventArgs args)
{
MessageBox.Show("Hey ... Something is going on over there in the classLib .. Canceled is " + args.Canceled + " and Finished is " + args.Finished);
// ...
};
xXMLandRar.RarFiles(repSelected);//, worker);
}
}
}
This is the class code. You can just create a class in your current project... Keep in mind that the CreateRarBW object is a BackGroundWorker instance... (Wasn't included above..)
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Threading;
namespace ThreadSample
{
public class XmlandRarCompletedEventArgs : EventArgs
{
public readonly bool Finished;
public readonly bool Canceled;
public XmlandRarCompletedEventArgs(bool finished)
{
Finished = finished;
Canceled = !finished;
}
}
public class OnXmlandRarUpdateEventArgs : EventArgs
{
public readonly int Percentage;
public readonly string Message;
public OnXmlandRarUpdateEventArgs(int perc) : this(perc, "")
{
}
public OnXmlandRarUpdateEventArgs(int perc, string message)
{
Percentage = perc;
Message = message;
}
}
public delegate void OnXmlandRarDoWorkHandler(object o);
public delegate void OnXmlandRarUpdateHandler(object o, OnXmlandRarUpdateEventArgs args);
public delegate void OnXmlandRarCompleteHandler(object o, XmlandRarCompletedEventArgs args);
public class XMLandRar // : BackgroundWorker
{
// Event handler to bind to for reporting progress
public EventHandler<ProgressArgs> ReportProgress;
// Eventargs to contain information to send to the subscriber
public class ProgressArgs : EventArgs
{
public int Percentage { get; set; }
public string Message { get; set; }
}
public event OnXmlandRarDoWorkHandler OnDoWork;
public event OnXmlandRarUpdateHandler OnUpdate;
public event OnXmlandRarCompleteHandler OnComplete;
public void RarFiles(List<string> repSelected)
{
TriggerDoWork();
int step = 100 / repSelected.Count();
int i = 0;
//Iterate through list and run rar for each
foreach (string rep in repSelected)
{
TriggerUpdate(i, "Raring files for " + rep);
//DirectoryExists(rep);
//ProcessRunner(rep);
i += step;
TriggerUpdate(i, "Raring files for " + rep);
}
TriggerComplete(true);
}
private void TriggerDoWork()
{
if (OnDoWork != null)
{
OnDoWork(this);
}
}
private void TriggerUpdate(int perc)
{
if (OnUpdate != null)
{
OnUpdate(this, new OnXmlandRarUpdateEventArgs(perc));
}
}
private void TriggerUpdate(int perc, string message)
{
if (OnUpdate != null)
{
OnUpdate(this, new OnXmlandRarUpdateEventArgs(perc, message));
}
}
private void TriggerComplete(bool finished)
{
if (OnComplete != null)
{
OnComplete(this, new XmlandRarCompletedEventArgs(finished));
}
}
}
}