Uisng Visual Studio 2008.
The main form is subscribing to an event in a class in order to report progress.
The ReportProgress event in the CSVReader class is ALWAYS coming up null and I just can't seem to see why this is.
Main Form:
//Constructor
public CreateSQL()
{
InitializeComponent();
csvReader = new CSVReader();
}
private void btnCreateSQL_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
**csvReader.ReportProgress += new EventHandler<CSVReader.ProgressArgs>(CSVReader_ReportProgress);**
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
}
}
protected void CSVReader_ReportProgress(object sender, CSVReader.ProgressArgs e)
{
// Call the UI backgroundworker
backgroundWorker1.ReportProgress(e.Percentage, e.Message);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
DataTable dt = csvReader.ReadCSVFile(sFile, _hasHeader);
}
And the Class CSVReader:
public class CSVReader : IDisposable
{
// 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 CSVReader()
{
}
public DataTable ReadCSVFile(string filename, bool headerRow)
{
using (CSVReader reader = new CSVReader(new FileInfo(filename)))
return reader.CreateDataTable(headerRow);
}
public DataTable CreateDataTable(bool headerRow)
{
// Read the CSV data into rows
List<List<object>> rows = new List<List<object>>();
List<object> readRow = null;
while ((readRow = ReadRow()) != null)
rows.Add(readRow);
// Report progress if somebody is listening (subscribed)
**if (ReportProgress != null)
{
ReportProgress(this, new ProgressArgs { Percentage = 33, Message = "Reading File" });
}**
}
The ReportProgress event is tested above if it's null and it is always is null.
I placed it here just for a test to get it sending back a couple variables, 33 and a string, but it's always null.
Thanks for any and all input!
Related
I currently have an installation "framework" that does specific things. What I need now to do is be able to call my form in parallel with my script. Something like this:
InstallationForm f = new InstallationForm();
Application.Run(f);
InstallSoftware(f);
private static void InstallSoftware(InstallationForm f) {
f.WriteToTextbox("Starting installation...");
Utils.Execute(#"C:\temp\setup.msi", #"-s C:\temp\instructions.xml");
...
f.WriteToTextbox("Installation finished");
The current way I can do this is by adding the Form.Shown handler in InstallSoftware, but that seems really messy. Is there anyway I can do this better?
Your code will not work, because Application.Run(f) returns not until the form was closed.
You may use a simplified Model/View/Controller pattern. Create an InstallationFormController class that has several events, e.g. for textual notifications to be written to your textbox. The InstallationForm registers on these events in it's OnLoad() method and then calls InstallationFormController.Initialize(). That method starts your installation (on a worker thread/task). That installation callback method fires several text events.
InstallationForm f = new InstallationForm(new InstallationFormController());
Application.Run(f);
internal class InstallationFormController
{
public event EventHandler<DataEventArgsT<string>> NotificationTextChanged;
public InstallationFormController()
{
}
public void Initialize()
{
Task.Factory.StartNew(DoInstallation);
}
private void DoInstallation()
{
...
OnNotificationTextChanged(new DataEventArgsT<string>("Installation finished"));
}
private void OnNotificationTextChanged(DataEventArgsT<string> e)
{
if(NotificationTextChanged != null)
NotificationTextChanged(this, e);
}
}
public class DataEventArgsT<T> : EventArgs
{
...
public T Data { get; set; }
}
internal class InstallationForm : Form
{
private readonly InstallationFormController _controller;
public InstallationForm()
{
InitializeComponent();
}
public InstallationForm(InstallationFormController controller) : this()
{
if(controller == null)
throw new ArgumentNullException("controller")
_controller = controller;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_controller.NotificationTextChanged += Controller_NotificationTextChanged;
_controller.Initialize();
}
protected virtual void Controller_NotificationTextChanged(object sender, DataEventArgsT<string> e)
{
if(this.InvokeRequired)
{ // call this method on UI thread!!!
var callback = new EventHandler<DataEventArgsT<string>>(Controller_NotificationTextChanged);
this.Invoke(callback, new object[] {sender, e});
}
else
{
_myTextBox.Text = e.Data;
}
}
...
}
Definittion of ProgressChanged:
// Summary:
// Event called whenever the progress of the upload changes.
public event Action<IUploadProgress> ProgressChanged;
public void insertFile(String filePath)
{
//.. some code
insertRequest.ProgressChanged += Upload_ProgressChanged;
}
public void Upload_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
//.. I need filePath from insertFile() here!
}
How to pass additional paramtres to Upload_ProgressChanged ?
I did the following:
public void insertFile(String filePath)
{
//.. some code
ProgressChangedEventArgs args = new ProgressChangedEventArgs()
{
path = filePath
};
insertRequest.ProgressChanged += Upload_ProgressChanged;
}
static void Upload_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
public class ProgressChangedEventArgs : EventArgs
{
public string path { get; set; }
}
And I have mistake Can not implicitly convert type 'void' to 'System.Action<Google.Apis.Upload.IUploadProgress>'
Instead of using an event you can capture the variable inside a closure
insertRequest.ProgressChanges += progress => { /* Do something with filePath here */ };
Firstly, define the EventArgs class - this will let you have whatever information you like...
public class ProgressChgEventArgs : System.EventArgs
{
public string Name { get;set; }
public int InstanceId { get;set; }
public ProgressChgEventArgs(string name, int id)
{
Name = name;
InstanceId = id;
}
}
Next, create the event that consumes these arguments:
public event EventHandler<ProgressChgEventArgs> ProgressChanged;
Then, have an 'On....' method that invokes the handlers
public void OnProgressChanged(ProgressChgEventArgs e)
{
var handler = new EventHandler<ProgressChgEventArgs>();
if (handler != null)
handler(this, e);
}
Now, at the relevant point in your code (presumably when the progress changes!) you call OnProgressChanged(), passing in an appropriate instance of the ProgressChgEventArgs:
private void Progress(string caller, int callerId)
{
var arguments = new ProgressChgEventArgs(caller, callerId);
OnProgressChanged(arguments);
}
I have an observer with a background worker. Lets say the observer has the following structure:
internal class Observer
{
private readonly BackgroundWorker bw1;
internal Object target;
public Observer()
{
bw1 = new BackgroundWorker();
bw1.DoWork += bw1_DoWork;
bw1.RunWorkerCompleted += bw1_RunWorkerCompleted;
bw1.WorkerSupportsCancellation = true;
}
private void bw1_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = new object(); // Query to database
}
private void bw1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
target = e.Result as object;
if (ChangedScannedValue != null)
{
ChangedScannedValue(_scannedValue);
}
}
private String _scannedValue = string.Empty;
internal delegate void OnChangedScannedValue(String scannedValue);
internal event OnChangedScannedValue ChangedScannedValue;
internal String ScannedValue
{
get { return _scannedValue; }
set
{
_scannedValue = value;
bw1.RunWorkerAsync(_scannedValue);
//ProcessScannedValue();
}
}
}
I have another class listening to the event.
public partial class myControl : UserControl
{
Observer _observer = new Observer();
public myControl()
{
InitializeComponent();
}
internal void LoadData(Observer observer)
{
_observer = observer;
_observer.ChangedScannedValue += _observer_ChangedScannedValue;
}
void _observer_ChangedScannedValue(string ScannedValue)
{
if (_observer.target != null)
{
// Do Stuff
}
else
{
MessageBox.Show("NO TARGET FOUND.");
}
}
}
The thing is. initially, after the background worker finishes, I get the message box "NO TARGET FOUND.", however immediatley after, it would //Do Stuff Debugging shows that the RunWorkerCompleted event fires twice. This only happens on the first change to scanned value, all changes afterwards work as desired.
Questions:
1) Why does RunWorkerCompleted fire twice?
2) Why is the target not updated on the first fire of RunWorkerCompleted
You could try again with target being set in bw1_DoWork already, i.e.:
private void bw1_DoWork(object sender, DoWorkEventArgs e) {
target = new object(); // Query to database
}
private void bw1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (ChangedScannedValue != null) {
ChangedScannedValue(_scannedValue);
}
}
Eventually you may want to replace the BackgroundWorker by a simpler solution with ThreadPool. I'd suggest this:
internal String ScannedValue {
get { return _scannedValue; }
set {
_scannedValue = value;
ThreadPool.QueueUserWorkItem( (WaitCallback) delegate {
target = new object(); // query database
if (ChangedScannedValue != null) ChangedScannedValue(_scannedValue);
} );
}
}
I have a C# WinForms application with using of WCF Data Services and I can't find a way to know if the application is doing a process to a web service. I have tried to implement IClientMessageInspector, etc. but it seems that it doesn't work.
Is there any other way to catch when a Windows Form app is consuming a web service? The purpose is to display a "running process" icon when the communication to a service is opened and then hide it when it's close.
Thanks.
you should add event in place where you calling WcfService methods and then listen to this event in UI layer. With custom event argument in this event, you should provide more info for consuming UI layer like enum values Connected, TransferStarted, TransferEnded, Closed,...
I think there are two main possible cases:
Wcf communication take place synchronous - you are calling some wrapper methods of object which handles connection/requests/responses with wcf service - in this case i think its trivial, you make UI actions before every call of wrapper for example
try
{
WcfEx.IwcfS5ExtensionClient client = new IwcfS5ExtensionClient("MyEndpointConfigurationName");
client.Open();
if (client.State == CommunicationState.Opened)
{
//change UI to Connected
}
else
{
//change ui to Connection Error
}
Application.DoEvents();
//Change UI to Transfering data
Application.DoEvents();
client.DoWork();
//change UI to transfer done
Application.DoEvents();
client.Close();
//change ui to Closed
}
catch (Exception e)
{
//change ui to Comunication error
}
Second is if wcf communication is async
-i will add more sofisticated sample later today
EDIT: Example with async work and notifying form
using System;
using System.ComponentModel;
using System.Threading;
using WcfEx;
public class MyForm : Form
{
public MyForm()
{
WcfHandler WcfConnection = new WcfHandler();
WcfConnection.ProgressChanged += WcfConnectionOnProgressChanged;
}
private delegate void WcfConnectionOnProgressChangedDelegate(object Sender, WcfHandler.ProgressChangedEventArgs EventArgs);
private void WcfConnectionOnProgressChanged(object Sender, WcfHandler.ProgressChangedEventArgs EventArgs)
{
//multi thread synchronization check
if (this.InvokeRequired)
{
object[] Parameters = new object[2];
Parameters[0] = Sender;
Parameters[1] = EventArgs;
this.Invoke(new WcfConnectionOnProgressChangedDelegate(WcfConnectionOnProgressChanged), Parameters);
}
else
{
if (EventArgs == null)
return;
switch (EventArgs.StateValue)
{
case WcfHandler.ProgressChangedEventArgs.State.Started:
{
this.Text = "Starting connection...";
break;
}
case WcfHandler.ProgressChangedEventArgs.State.Processing:
{
this.Text = "Downloading updates...";
break;
}
case WcfHandler.ProgressChangedEventArgs.State.Finished:
{
this.Text = EventArgs.Succes ? "Update completed" : "Update failed";
break;
}
}
Application.DoEvents();
}
}
public class WcfHandler
{
public class ProgressChangedEventArgs : EventArgs
{
public enum State : int
{
Started,
Processing,
Finished
};
public bool Succes { get; set; }
public State StateValue { get; set; }
}
public delegate void ProgressChangedEventHandler(object sender, ProgressChangedEventArgs EventArgs);
public event ProgressChangedEventHandler ProgressChanged;
protected virtual void OnProgressChanged(ProgressChangedEventArgs e)
{
if (ProgressChanged != null)
{
ProgressChanged(this, e);
}
}
public void StartChecking()
{
BackgroundWorker bWorker = new BackgroundWorker();
bWorker.DoWork += CheckStatesAsync;
bWorker.RunWorkerCompleted += BWorkerOnRunWorkerCompleted;
bWorker.RunWorkerAsync();
}
private void CheckStatesAsync(object sender, DoWorkEventArgs e)
{
while (true)
{
WcfEx.IwcfS5ExtensionClient client = new IwcfS5ExtensionClient("MyWcfBindingConfig");
ProgressChangedEventArgs Controller = new ProgressChangedEventArgs();
Controller.StateValue = ProgressChangedEventArgs.State.Started;
Controller.Succes = true;
this.OnProgressChanged(Controller);
try
{
client.Open();
Controller.StateValue = ProgressChangedEventArgs.State.Processing;
Controller.Succes = true;
this.OnProgressChanged(Controller);
//do some work
}
catch (Exception)
{
this.OnProgressChanged(new ProgressChangedEventArgs()
{
StateValue = ProgressChangedEventArgs.State.Finished,
Succes = false
});
}
Thread.Sleep(8000);
}
}
private void BWorkerOnRunWorkerCompleted(object Sender, RunWorkerCompletedEventArgs RunWorkerCompletedEventArgs)
{
ProgressChangedEventArgs Controller = new ProgressChangedEventArgs();
Controller.StateValue = ProgressChangedEventArgs.State.Finished;
Controller.Succes = true;
this.OnProgressChanged(Controller);
}
}
I'm coding a class to move and copy files. I'm raising events when the current file progress and the total progress changes. When I test the code on my XP machine, it works fine, but when I run it on my Windows 7 64-Bit machine, the current progress doesn't update the UI correctly. The current progress ProgressBar only gets half way then starts on the next file which does the same. The total progress ProgressBar updates fine. Any ideas why this is happening?
EDIT: The Windows 7 machine is running a quad-core and the XP is running a dual-core. Not sure if that might be what's making a difference. I'm only a hobbyist so excuse my ignorance :)
EDIT: Code added (Background)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Threading;
using System.Timers;
using Timer = System.Timers.Timer;
namespace nGenSolutions.IO
{
public class FileTransporter
{
#region Delegates
public delegate void CurrentFileChangedEventHandler(string fileName);
public delegate void CurrentProgressChangedEventHandler(int percentComplete);
public delegate void CurrentWriteSpeedUpdatedEventHandler(long bytesPerSecond);
public delegate void TotalProgressChangedEventHandler(int percentComplete);
public delegate void TransportCompleteEventHandler(FileTransportResult result);
#endregion
private readonly List<string> _destinationFiles = new List<string>();
private readonly List<string> _sourceFiles = new List<string>();
private long _bytesCopiedSinceInterval;
private FileTransportResult _result;
private Timer _speedTimer;
private long _totalDataLength;
private BackgroundWorker _worker;
public bool TransportInProgress { get; private set; }
public event CurrentFileChangedEventHandler CurrentFileChanged;
public event CurrentProgressChangedEventHandler CurrentProgressChanged;
public event CurrentWriteSpeedUpdatedEventHandler CurrentWriteSpeedUpdated;
public event TotalProgressChangedEventHandler TotalProgressChanged;
public event TransportCompleteEventHandler TransportComplete;
public void AddFile(string sourceFile, string destinationFile)
{
if (!File.Exists(sourceFile))
throw new FileNotFoundException("The specified file does not exist!", sourceFile);
var fileInfo = new FileInfo(sourceFile);
_totalDataLength += fileInfo.Length;
_sourceFiles.Add(sourceFile);
_destinationFiles.Add(destinationFile);
}
public void BeginTransport()
{
// update the write speed every 3 seconds
_speedTimer = new Timer {Interval = 3000};
_speedTimer.Elapsed += SpeedTimerElapsed;
_worker = new BackgroundWorker();
_worker.DoWork += DoTransport;
_worker.RunWorkerCompleted += WorkerCompleted;
_worker.RunWorkerAsync();
_speedTimer.Start();
TransportInProgress = true;
}
private void SpeedTimerElapsed(object sender, ElapsedEventArgs e)
{
InvokeCurrentSpeedUpdated(_bytesCopiedSinceInterval);
_bytesCopiedSinceInterval = 0;
}
private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
TransportInProgress = false;
InvokeTransportComplete(_result);
}
public void CancelTransport(bool rollbackChanges)
{
if (TransportInProgress == false)
throw new InvalidOperationException("You tried to stop the transport before you started it!");
_result = FileTransportResult.Cancelled;
_worker.CancelAsync();
while (_worker.IsBusy)
{
// wait for worker to die an 'orrible death
}
// TODO: rollback changes if requested
}
private void DoTransport(object sender, DoWorkEventArgs e)
{
long totalBytesCopied = 0;
int totalPercentComplete = 0;
for (int i = 0; i < _sourceFiles.Count; i++)
{
string sourceFile = _sourceFiles[i];
string destinationFile = _destinationFiles[i];
long currentFileLength = new FileInfo(sourceFile).Length;
InvokeCurrentFileChanged(sourceFile);
using (var sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read))
{
using (var destinationStream = new FileStream(destinationFile, FileMode.Create, FileAccess.Write))
{
using (var reader = new BinaryReader(sourceStream))
{
using (var writer = new BinaryWriter(destinationStream))
{
int lastPercentComplete = 0;
for (int j = 0; j < currentFileLength; j++)
{
writer.Write(reader.ReadByte());
totalBytesCopied += 1;
_bytesCopiedSinceInterval += 1;
int current = Convert.ToInt32((j/(double) currentFileLength)*100);
int total = Convert.ToInt32((totalBytesCopied/(double) _totalDataLength)*100);
// raise progress events every 3%
if (current%3 == 0)
{
// only raise the event if the progress has increased
if (current > lastPercentComplete)
{
lastPercentComplete = current;
InvokeCurrentProgressChanged(lastPercentComplete);
}
}
if (total%3 == 0)
{
// only raise the event if the progress has increased
if (total > totalPercentComplete)
{
totalPercentComplete = total;
InvokeTotalProgressChanged(totalPercentComplete);
}
}
}
}
InvokeCurrentProgressChanged(100);
}
}
}
}
InvokeTotalProgressChanged(100);
}
private void InvokeCurrentFileChanged(string fileName)
{
CurrentFileChangedEventHandler handler = CurrentFileChanged;
if (handler == null) return;
handler(fileName);
}
private void InvokeCurrentProgressChanged(int percentComplete)
{
CurrentProgressChangedEventHandler handler = CurrentProgressChanged;
if (handler == null) return;
handler(percentComplete);
}
private void InvokeCurrentSpeedUpdated(long bytesPerSecond)
{
CurrentWriteSpeedUpdatedEventHandler handler = CurrentWriteSpeedUpdated;
if (handler == null) return;
handler(bytesPerSecond);
}
private void InvokeTotalProgressChanged(int percentComplete)
{
TotalProgressChangedEventHandler handler = TotalProgressChanged;
if (handler == null) return;
handler(percentComplete);
}
private void InvokeTransportComplete(FileTransportResult result)
{
TransportCompleteEventHandler handler = TransportComplete;
if (handler == null) return;
handler(result);
}
}
}
EDIT: Code added (GUI)
using System;
using System.IO;
using System.Windows.Forms;
using ExtensionMethods;
using nGenSolutions.IO;
namespace TestApplication
{
public partial class ProgressForm : Form
{
public ProgressForm()
{
InitializeComponent();
}
private void ProgressForm_Load(object sender, EventArgs e)
{
var transporter = new FileTransporter();
foreach (string fileName in Directory.GetFiles("C:\\Temp\\"))
{
transporter.AddFile(fileName, "C:\\" + Path.GetFileName(fileName));
}
transporter.CurrentFileChanged += transporter_CurrentFileChanged;
transporter.CurrentProgressChanged += transporter_CurrentProgressChanged;
transporter.TotalProgressChanged += transporter_TotalProgressChanged;
transporter.CurrentWriteSpeedUpdated += transporter_CurrentWriteSpeedUpdated;
transporter.TransportComplete += transporter_TransportComplete;
transporter.BeginTransport();
}
void transporter_TransportComplete(FileTransportResult result)
{
Close();
}
void transporter_CurrentWriteSpeedUpdated(long bytesPerSecond)
{
double megaBytesPerSecond = (double)bytesPerSecond/1024000;
currentSpeedLabel.SafeInvoke(x=> x.Text = string.Format("Transfer speed: {0:0.0} MB/s", megaBytesPerSecond));
}
private void transporter_TotalProgressChanged(int percentComplete)
{
totalProgressBar.SafeInvoke(x => x.Value = percentComplete);
}
private void transporter_CurrentProgressChanged(int percentComplete)
{
currentProgressBar.SafeInvoke(x => x.Value = percentComplete);
}
private void transporter_CurrentFileChanged(string fileName)
{
this.SafeInvoke(x => x.Text = string.Format("Current file: {0}", fileName));
}
}
}
EDIT: SafeInvoke code added
public static void SafeInvoke<T>(this T #this, Action<T> action) where T : Control
{
if (#this.InvokeRequired)
{
#this.Invoke(action, new object[] {#this});
}
else
{
if (!#this.IsHandleCreated) return;
if (#this.IsDisposed)
throw new ObjectDisposedException("#this is disposed.");
action(#this);
}
}
Well, if transporter_CurrentProgressChanged gets correct values, the program works properly. You can try to add some minimal Thread.Sleep call to InvokeCurrentProgressChanged (maybe with 0 parameter) when progress value is 100%, to get UI chance to update itself, but in this case you reduce the program performance. It is possibly better to leave the program unchanged, since it works as expected, and main progress bar is updated.