|
Ok so I seem to have my backgroundWorkers worked out. However I can't figure out how to report progress back to the original form. I'll try to pice together the code to have this make sense. FYI I don't have anything in the progressUpdated even yet.
namespace MailTest
{
public partial class Form1 : Form
{
List<BGW> BGWs = new List<BGW>();
public Form1()
{
InitializeComponent();
for (int i = 0; i < 3; i++)
{
BGWs.Add(new BGW());
BGWs[i].ProgressChanged += BGW_ProgressChanged;
BGWs[i].RunWorkerCompleted += BGW_WorkerCompleted;
}
btnGetMessageInfo.Enabled = false;
}
void BGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
BGW bgw = (BGW)sender;
}
void BGW_WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BGW bgw = (BGW)sender;
}
class BGW : BackgroundWorker
{
public BGW()
{
this.WorkerReportsProgress = true;
this.WorkerSupportsCancellation = true;
}
protected override void OnDoWork(DoWorkEventArgs e)
{
base.OnDoWork(e);
WriteMail.WriteAndParseMail();
}
}
private void btnGetMessageInfo_Click(object sender, EventArgs e)
{
processNumberOfWorkers.howMayWorkers();
MessageBox.Show("There are " + GlobalVars.intHowManyWorkers + " workers needed!");
btnGetMessageInfo.Enabled = false;
backgroundWorker2.RunWorkerAsync();
BGWs[0].RunWorkerAsync();
}
class WriteMail
{
public static void WriteAndParseMail()
{
int intEmail;
bool bolSrvMsg;
string txtLogString = null;
TcpClient tcpClient = new TcpClient();
if (percentComplete <= highestPercentageReached)
{
if (percentComplete == 0)
{
percentComplete = 1;
}
highestPercentageReached = percentComplete + 1;
string strCounter = currentMail + " of " + intEmail;
}
I know I'm probably missing something obvious but still new to backgroundWorkers in C# and not seeing many examples for doing this from a class. When I do it from the main form with a different backgroundWorker it works fine, but when I changed it to this "type" it didn't like the function on the same class so I had to create a new one (which I wanted to do anyways).
Thanks in advance!!!!
|
|
|
|
|
MacRaider4 wrote: I'm probably missing something obvious
For some obscure reason there is a BackgroundWorker.WorkerReportsProgress property, and in good Microsoft tradition its default value is the least useful one.
Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.
|
|
|
|
|
I thought the only two choices for that are true and false? which I have in :
public BGW()
{
this.WorkerReportsProgress = true;
this.WorkerSupportsCancellation = true;
}
is this where I get a lot of snickering behind my back
|
|
|
|
|
Sorry, I initially didn't read all your code, I just reported the most likely oversight.
I looked through your code now, and I would say:
1. your ReportProgress method should get called, however it is empty. What makes you say it doesn't work?
2. your Mail stuff is static, having multiple BGW's active in WriteAndParseMail() is not safe, it requires data synchronization.
3. I'm not sure you can have multiple TcpClient instances operating concurrently like that.
4. your percentage stuff is incomplete, so I can't be sure it is correct.
5. for overall progress, I often use a timer (Windows.Forms.Timer) which just gathers the number and updates the GUI at fixed intervals, say once a second. That turns out to be easier than having to check for actual percentage changes (which is however more economical).
Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.
|
|
|
|
|
When I use the hard coded version I have:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbWrite.Value = e.ProgressPercentage;
lblCurrentCount.Text = e.UserState.ToString();
}
Of which does work (when it's not calling from the class), sorry I should have included that.
What makes me say it doesn't work is when I uncomment that section of code out I get a error saying it's not in the current context.
I have had 2 TcpClients running at the same time, so that doesn't seem to be a issue.
Is this what you mean by percentage stuff being incomplete?
percentComplete = (int)((float)currentMail / (float)intEmail * 100);
if (percentComplete <= highestPercentageReached)
{
if (percentComplete == 0)
{
percentComplete = 1;
}
highestPercentageReached = percentComplete + 1;
string strCounter = currentMail + " of " + intEmail;
BGWs[0].ReportProgress(percentComplete, strCounter);
}
|
|
|
|
|
Easy... Your BGWs are defined in Form1, while you're trying to access them from within WriteMail.
I would suggest adding a "sender" parameter to WriteAndParseMail(), so you can pass the worker into it.
protected override void OnDoWork(DoWorkEventArgs e)
{
base.OnDoWork(e);
WriteMail.WriteAndParseMail(this);
}
And in the worker thread...
highestPercentageReached = percentComplete + 1;
string strCounter = currentMail + " of " + intEmail;
(sender as BackgroundWorker).ReportProgress(percentComplete, strCounter);
|
|
|
|
|
I tried that but no luck, though I'm sure it's me. This was so much easier in VB...
|
|
|
|
|
Oh, ya know what... I wonder if the BackgroundWorker only allows ReportProgress to be called during the DoWork... Technically, your routine is running AFTER DoWork completes... I really don't think it's intended to be run that way.
The standard way to use a BackgroundWorker is to hook the DoWork EVENT, and put your code there. No need to subclass it, unless you really want to centralize those two boolean property settings.
Just guessing here, of course... I've never seen the BW subclassed... Never had any need to do that.
|
|
|
|
|
Ok I've been doing some more reading, would this be better using a "thread" vs a BGW? Now granted I don't need to put this in a seperate class, however for what ever reason when I use BGWs[0].RunWorkerAsync(); vs backgroundWorker1.RunWorkerAsync(); I'm not able to have the "function" in the same class (the Form) thus why I moved it to a seperate class. Plus when I was getting into more advanced someone reccomended keeping as little as you can on your main form and using classes for as much as possible. Is this a good habbit, bad habbit or just personal preference? Where I work there are two of us, and he does all the C++ stuff and I do the VB (6+ years, though .net in only the last year) and just starting with C# (about 6 months ago).
I have been able to subclass a BGW in VB but obviously not in C#, yet again if I don't have to not a big deal.
Basically what I need to do is be able to run the same function up to 3 times concurrently. I believe if my memory serves me correctally each "occurance" of the function will have it's own local variables. I also believe that if I use a thread vs a bgw I can lock what I'm writing to and instead of throwing a error, the threads will wait until the current one is done updating. Lets say it's just lblOutput.Text = e.UserState.ToString(); (as if it were a bgw perhaps in a thread you can use the variable directally)?
Does this help to explain what I'm trying to accomplish and maybe give you all a better idea of what path I should be taking?
|
|
|
|
|
MacRaider4 wrote: (as if it were a bgw perhaps in a thread you can use the variable directally)?
Nope, that limitation is universal to all types of multithreading in WinForms (And WPF, for that matter). No touching the GUI controls except from the GUI thread.
MacRaider4 wrote: Plus when I was getting into more advanced someone reccomended keeping as little as you can on your main form and using classes for as much as possible. Is this a good habbit, bad habbit or just personal preference?
That's generally a good idea, except in really small tools when it would be complete overkill. The form is your connection to the GUI, and business logic goes elsewhere... In your particular situation, I think I would probably handle it something like this:
(Consider this C#ish pseudocode, as I don't have VS installed on my home machine at the moment)
public static void SendMessagesAsync(ProgressChangedHandler progressCallback, params SomeClass[] messages)
{
for (int idx = 0; idx < messages.Length; idx++)
{
BackgroundWorker wkr = new BackgroundWorker();
wkr.WorkerSupportsProgress = true;
wkr.ProgressChanged += progressCallback;
wkr.DoWorkEventArgs += wkr_DoWork;
wkr.RunWorkerAsync(messages[idx]);
}
}
private static void wkr_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker wkr = sender as BackgroundWorker;
SomeClass args = e.Argument as SomeClass;
}
private void SomethingCalledFromYourForm()
{
MyStaticClass.SendMessagesAsync(wkr_ProgressChanged, new SomeClass[] {
new SomeClass() { Whatever = parameters, YouWant = toset },
new SomeClass() { Subject = someone, Body = "something else?", Recipient = "someone@somewhere.sometime" }
});
}
I don't know what you need to pass to the WriteMail function, but that's generally how you get information into a background worker... Make a class to hold it, and pass it as the argument to RunWorkerAsync().
|
|
|
|
|
Ian Shlasko wrote: I wonder if the BackgroundWorker only allows ReportProgress to be called during the DoWork
No, internally it uses a SendOrPostCallback delegate and an AsyncOperation . If the AsyncOperation is null (there is no active worker thread) then the delegate is called directly. If it's not null then the AsyncOperation 's Post method is called with the delegate and args as parameters which automatically invokes it on the syncronization context that RunWorkerAsync existed in.
public void ReportProgress(int percentProgress, object userState)
{
if (!this.WorkerReportsProgress)
{
throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerDoesntReportProgress"));
}
ProgressChangedEventArgs arg = new ProgressChangedEventArgs(percentProgress, userState);
if (this.asyncOperation != null)
{
this.asyncOperation.Post(this.progressReporter, arg);
}
else
{
this.progressReporter(arg);
}
}
|
|
|
|
|
Hey guys,
is it possible to load a .jpg on a form and make it transparently to sketch it in paint or somewhere else?
The picture should always stay in the foreground, but I want interact to the window under it.
any suggestions?
|
|
|
|
|
How about...
- Set jpg as form BackgroundImage
- Set form Opacity to 50% (or whatever)
- Set FormBorderStyle to None (but you will have to handle Move another way)
- Set form TopMost to true
Illogical thoughts make me ill
|
|
|
|
|
yeah, thank you !
But 1 Problem remain...
How could I draw under it without set the focus to the Form?
|
|
|
|
|
Ahhh.. I knew it seemed too easy
Maybe this[^] will help
Illogical thoughts make me ill
|
|
|
|
|
Yeah its vb.net but I have no problem with it !
Thank you very much
|
|
|
|
|
I am using a windows service project i.e. winService.
Inside this project, I have placed an xml file.
This winService project, calls a class library project i.e. classLibrary.
Inside the classlibrary, there is a class i.e. myclass.cs which has to get the path of the xml file which as mentioned above is inside the winservice project and it is called test.xml
I am using the following code in the class myclass.cs which is inside the classlibrary project to get the path of the xml file:
string strPath = AppDomain.CurrentDomain.BaseDirectory + "test.xml";
Question:
The code above returns the path of the winservice\bin\debug
Is it correct to place the test.xml inside the bin\debug folder?
or Should the test.xml file be placed on the root of the winservice project instead?
and is the code ok?
Thanks
|
|
|
|
|
When you deploy your project, your executable will be located in a different directory to the one you build into. This means that your xml file should be in the executable directory or a subdirectory of that, so your code is fine(ish). When creating paths from variables, you should always look to use Path.Combine(pathroot, file); .
|
|
|
|
|
Hi,
By executing directory I think you mean my winservice project in this case.
If so, I have placed the test.xml file in this directory.
Am I right so far?
|
|
|
|
|
|
I see.
I will continue testing and will ask questions later if it is ok please.
Thank you.
|
|
|
|
|
As Pete says, paths are notorious for changing, keep them fluid.
------------------------------------
I will never again mention that I was the poster of the One Millionth Lounge Post, nor that it was complete drivel. Dalek Dave
CCC League Table Link
CCC Link[ ^]
|
|
|
|
|
arkiboys wrote: string strPath = AppDomain.CurrentDomain.BaseDirectory + "test.xml";Question:
The code above returns the path of the winservice\bin\debug
Is it correct to place the test.xml inside the bin\debug folder?
It does?
Shouldn't it be AppDomain.CurrentDomain.BaseDirectory + "\\test.xml" or a variation thereof?
..and water fell from the sky like rain.
|
|
|
|
|
As I stated earlier, this method should not be used. Path.Combine is the best way to create the path structure.
|
|
|
|
|
if your app would need to create/modify the XML file, it would not be wise to put such data file in the EXE's folder as:
1. the folder is shared by all users;
2. the folder might be write-protected.
When in doubt, the better approach is to use one of the official folders as returned by Environment.GetFolderPath() .
Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.
|
|
|
|