Well, what I am trying to do is show a animated gif while it reads a directory full of files, however the UI freezes, which is ok, but i would like to keep the gif running till the operation is finished. Any ideas?
I am doing it on a Windows Form using VS2010 C#
Here is some example code how you can Load your files aysnchronous. Maybe it helps you. I like this way more than using DoEvents. With DoEvents I had already have some ungood side-effects, therefore I try not to use it.
BackgroundWorker bgWorker = new BackgroundWorker() { WorkerReportsProgress=true};
bgWorker.DoWork += (s, e) => {
// Load here your file/s
// Use bgWorker.ReportProgress(); to report the current progress
};
bgWorker.ProgressChanged+=(s,e)=>{
// Here you will be informed about progress and here it is save to change/show progress. You can access from here savely a ProgressBars or another control.
};
bgWorker.RunWorkerCompleted += (s, e) => {
// Here you will be informed if the job is done.
// Use this event to unlock your gui
};
bgWorker.RunWorkerAsync();
You have two options.
Start a separate thread to handle the file operations.
periodically call Application.DoEvents() within the file loop. That will cause your app to process pending messages (thus updating your UI), but will negatively impact the speed of the file processing loop.
Posting from my phone so no example links.
Run enumeration is a separate thread and update the GUI in the main thread.
Would something like this work with backgroundWorker?
private void buttonRename_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
foreach (ListViewItem myitem in listView.Items)
{
try
{
//Rename
}
catch
{
}
}
}
Related
I have the following constellation:
MainForm.cs -> Including all my Form Elements
Program.cs -> includes the main part, which is a xmlreader/writer to alter xml attributes in xml files that can be as large as 4gb
So this little app works but of course the UI gets unresponsive and freezes which I want to avoid, I also hope to reduce the duration of this process on the way
I start the call of my xmlread/write method from a BtnClick event:
void BtnApplyChangesClick(object sender, EventArgs e)
{
Program p = Program.Instance;
pbApplyChanges.Minimum = 0;
pbApplyChanges.Step = 1;
Cursor.Current = Cursors.WaitCursor;
foreach(DataGridViewRow cr in dataGridView2.Rows)
{
pbApplyChanges.Maximum = dataGridView2.Rows.Count;
p.changeElements(cr.Cells["Filename"].Value.ToString(), txtTenant.Text, txtDate.Text, txtEvtId2.Text);
pbApplyChanges.PerformStep();
}
Cursor.Current = Cursors.Arrow;
MessageBox.Show("Job done");
}
In the call I use my singleton instance of Program.cs and my main Method there (changeElements) uses 4 String params, that are all taken from information in the Form! (I suppose this is kinda bad practice but it worked so far...)
When I tried to replace this method call with a backgroundWorker (itself made the method call then) I failed as the method call wasn't even made... I found out that UI elements can't be accessed from the BW thread, so I suppose this is also the reason for my method call not working?!
So how can I get this constellation to work? Do I have to pass all 4 string Params AND the class instance (of Program.cs) to the background worker? Is BW even the best tool for the job?
In general the BackgroundWorker shouldn't access any UI-Elements. It's an old advice in Winforms that accessing UI-Elements should just happen from the UI-Thread.
You can use the Background-Worker like this:
private void Main(string[] args)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += Bw_DoWork;
bw.RunWorkerCompleted += Bw_RunWorkerCompleted;
//Parameter you need to work in Background-Thread for example your strings
string[] param = new[] {"Text1", "Text2", "Text3", "Text4"};
//Start work
bw.RunWorkerAsync(param);
}
//Do your Background-Work
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
string[] param = e.Argument as string[];
//Process your long running task
e.Result = null; //Set your Result of the long running task
}
//Taking your results
private void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Apply your Results to your GUI-Elements
myTextBox1.Text = e.Result.ToString();
}
Background-Worker is some old school stuff by the way, so if you like to learn something new take a look here and inform yourself about the TPL. This gives you a better handling of asynchronous.
In fact I think it's not really good to store 4gb data in a XML-File. Do you think about a Database? Or split the XML-File in many XML-Files? So you would be able to read data in chunks.
I hope this helps you.
I don't use background worker for this. I use normal threads instead. Try this code:
public void ButtonDoWork_Click(eventArgs......) {
DoWorkThread = new Thread(new ThreadStart(DoWork)); // Setup thread
DoWorkThread.isBackground = true; // Its background so, we need to set background flag
DoWorkThread.Start(); // Start the thread
}
private Thread DoWorkThread: // our Thread object
private void DoWork() { // This void contains action that will be performed by thread
//TODO: Background processing. To update UI from another thread use Control.Invoke(...)
}
Please note, I don't tested this code - I write it from my memory and it's late so it can not work.
You can also read about Threads at MSDN :)
I have an application that finds the shortest path between 2 squares, and when the path is longer or more complicate it can take 1-2 seconds to find it and I want to write on the screen a loading message that changes (first "Loading" then "Loading." then "Loading.." etc.).
Another problem is that the application give a "Not Responding" message if it take longer (10-12 seconds) how can I get rid of this?
The code so far:
Form1.cs:
namespace PathFinder
{
Map map1;
public Form1()
{
map1 = new Map(tileDimension, mapDimension);
map1.Generate(); //the function that calculate the path
this.Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
//drawings
this.Invalidate();
}
}
Map.cs:
namespace PathFinder
{
public Map(Point tileDim, Point mapDim)
{
//Initialization
}
public Generate()
{
//lots of loops
}
}
The reason for this is that UI main thread must process events.
If it does not for a period, it starts complaining, that is what you are experiencing.
So, you should not block the UI thread with any lengthy processing.
Use the BackgroundWorker Class for such operations.
Another option (not recommended) would be using
for...
{
// ...
// Some lengthy part of processing, but not as lengthy as the whole thing
Application.DoEvents();
}
in-between the lengthy operation cycles, if you choose to do processing in the UI thread.
Use a BackgroundWorker to offload long-running calculations on a worker thread. That prevents the UI from freezing. BGW is well covered by the MSDN Library, be sure to follow the examples.
Any drawing you have to do however still needs to be done on the UI thread with the Paint event. Just make sure that you can do so as quickly as possible. Have the worker store the path in, say, a Point[] or a GraphicsPath. Call Invalidate() in the BGW's RunWorkerCompleted event handler to get the paint event to run.
Seeing your code might help.To avoid window to halt you may use seperate thread for calculations or in your process you may use Applications.DoEvents(); if winform.
As i said.
Well Does this works?
namespace PathFinder
{
Map map1;
BackgroundWorker GetSomeData = new BackgroundWorker();
public Form1()
{
GetSomeData .DoWork += new DoWorkEventHandler(GetSomeData_DoWork);
map1 = new Map(tileDimension, mapDimension);
GetSomeData.RunWorkerAsync();
this.Invalidate();
}
void GetSomeData_DoWork(object sender, DoWorkEventArgs e)
{
map1.Generate(); //the function that calculate the path
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
//drawings
this.Invalidate();
}
}
I'm trying to run a method that gets file list from specified folders and represents it in DataGridView. The method is ran in BackgroundWorker, so I'm expecting GUI to stay active. But it still freezes. Here's an example:
private void startScan_Click(object sender, EventArgs e)
{
bckgrFileScanner.RunWorkerAsync();
}
private void bckgrFileScanner_DoWork(object sender, DoWorkEventArgs e)
{
//for each folder in list perform this function, which scans folder and gets all files
for (int i = 0; i < folderList.Items.Count; i++)
{
GetFileList(ref scannedFiles, folderList.Items[i].ToString(), bckgrFileScanner);
}
}
public static void GetFileList(ref List<FileInfo> fList, string fPath, BackgroundWorker scanner)
{
DirectoryInfo di = new DirectoryInfo(fPath);
FileInfo[] fi = di.GetFiles();
foreach (FileInfo fiTemp in fi)
{
if (fiTemp.Name.StartsWith("~$") == false)
{
//adds items to list of all scanned files
fList.Add(fiTemp);
//reports file name to ProgressChanged method
scanner.ReportProgress(0, fiTemp);
}
}
DirectoryInfo[] dFolders = di.GetDirectories();
//use recursion for all subfolders
foreach (DirectoryInfo d in dFolders)
{
GetFileList(ref fList, d.FullName, scanner);
}
}
private void bckgrFileScanner_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//userstate is filename, so add it to table
filesDataGrid.Rows.Add(e.UserState.ToString());
}
Do not update your reportprogress event at each row of your loop.
Instead, call the ReportProgress every 2 or more iteration (the best way is to compute et step to not update at each row if you have 200000)
And fill the grid when the whole process has finished (in the completed event), to update all the rows. I think progress is meant to update a progress only, not fill a bunch of data into a control but i may be wrong :
from here :
Tips You probably know more than you think about the BackgroundWorker
class. BackgroundWorker has a name that might indicate it is more
complex than it really is. There are many more details about threading
and abort calls, but once you understand that BackgroundWorker is just
a structural "overlay" to threads in Windows Forms, it is quite
intuitive. Here are the steps again:
First, call RunWorkerAsync with an argument. You can pass any argument
to this method on BackgroundWorker, including null. It simply must
inherit from object, which everything does.
Second, custom processing is run. Your expensive code is executed in
the DoWork method. Insert pause here as your program does its
calculations.
Third, it finishes. When your processing is done, RunWorkerCompleted
is called. In this method, you receive the result. In this way, your
BackgroundWorker object modifies an object on another thread, and you
receive it when it is done.
I think it hangs up when too much updates in a short time are required, and even calling Apllication.DoEvents() does not work all the times.
I hope it will help, a little late i know, but it's better than never ;)
This (simplified) example works for me and has a lovely responsive UI:
BackgroundWorker m_objWorker = new BackgroundWorker();
public FormBackgroundWorkerExample()
{
InitializeComponent();
m_objWorker.WorkerReportsProgress = true;
m_objWorker.DoWork += new DoWorkEventHandler(m_objWorker_DoWork);
m_objWorker.ProgressChanged += new ProgressChangedEventHandler(m_objWorker_ProgressChanged);
}
private void btnStart_Click(object sender, EventArgs e)
{
m_objWorker.RunWorkerAsync();
}
private void m_objWorker_DoWork(object sender, DoWorkEventArgs e)
{
//for each folder in list perform this function, which scans folder and gets all files
for (int i = 0; i <= 100; i++)
{
m_objWorker.ReportProgress(i, "FooBar");
Thread.Sleep(1000);
}
}
private void m_objWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
dataGridView1.Rows.Add(e.UserState.ToString());
}
Perhaps this'll give you an idea where yours is going wrong?
Edit: You might want to try it without the datagrid just to try and isolate the problem.
Might be because you are locking the backgroundworker itself.
In your DoWork method
GetFileList(ref scannedFiles, folderList.Items[i].ToString(), bckgrFileScanner);
Normaly you should have access to bckgrFileScanner from your DoWork Method, and just call it directly as bckgrFileScanner.ReportProgress( .... .... )
When passing it like you do, it will now report progress on the background workers thread, which is now not the same as the UI thread. (who owns bckgrFileScanner)
Edit To clarify:
Your UI thread owns bckgrFileScanner and fires RunWorkerAsync()
The stuff that happens in DoWork is now on its own thread.
The DoWork thread is "stealing" the variable bckgrFileScanner
Now the ReportProgress fires on the DoWork thread, instead of the UIs thread
You are calling ReportProgress on scanner. Shouldn't be that bckgrFileScanner?
EDIT
Is by any chance the scannedFiles list databound to the UI? If so a change to the list causes a UI update.
Possible reasons:
I think you just want to report the filename:
//reports file name to ProgressChanged method
scanner.ReportProgress(0, fiTemp.Name);
Or the folderList in DoWork is a UI control:
for (int i = 0; i < folderList.Items.Count; i++)
{
GetFileList(ref scannedFiles, folderList.Items[i].ToString(), bckgrFileScanner);
}
Why not pass the list of folders to the RunWorkerAsync method.
string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
public void unzip(String zFile)
{
Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(zFile);
zip.ExtractProgress += new EventHandler<ExtractProgressEventArgs>(zip_ExtractProgress);
zip.ExtractAll(desktop + "\\cache", ExtractExistingFileAction.OverwriteSilently);
zip.Dispose();
zip = null;
}
void zip_ExtractProgress(object sender, ExtractProgressEventArgs e)
{
if (e.EventType == ZipProgressEventType.Extracting_EntryBytesWritten)
{
label2.Text = "debug: " + ((e.EntriesExtracted));
}
else if (e.EventType == ZipProgressEventType.Extracting_BeforeExtractEntry)
{
label3.Text = e.CurrentEntry.FileName;
}
}
private void button1_Click(object sender, EventArgs e)
{
unzip(desktop + "\\cache.zip");
}
When I execute the unzip button1_Click() my application freezes. I'm new to C# and I'm not really sure how to fix this, can someone help me?
Long running blocking operations should not run on the main UI thread, since as you can see the UI will freeze up.
Consider using a BackgroundWorker to do the work on a separate thread.
There's a good summary here.
Report progress back to the UI by handling the ProgressChanged event and calling backgroundWorker.ReportProgress(), rather than directly updating label2.Text from inside there.
i.e. from inside your zip_ExtractProgress method, call backgroundWorker.ReportProgress
label3.Text = e.CurrentEntry.FileName;
label3.Update();
The Update() method ensures that the label is painted, now showing the Text property you assigned. Without it, the painting doesn't happen until the unzipping code stops running and your program goes idle again. Otherwise known as 'pumping the message loop'. Calling Update() is only a partial fix, your window is still catatonic and won't respond to mouse clicks for example. If it takes longer than a couple of seconds, Windows displays the "Not responding" ghost window.
Get some experience with coding in C#, then tackle threading with the BackgroundWorker class.
Easiest way: Execute the unzip method using a BackgroundWorker. Be sure to modify GUI controls only on the main GUI thread, by using Invoke.
I have written a function which takes a whole bunch of files, zips them up and then allows the user to download this zip all through an asp.net website.
However, these zip files are going to be pretty large (like 1GB large), so it won't happen immediately, what i'd like to do is to be able to have this running on a seperate thread (possible?) while the user continues to navigate around the website, and once the zip is created, have some way of notifying them so they can then download this zip.
I was hoping for some pointers on how best to do this in c# and also in terms of UI.
Thanks in advance.
Use
Asynchronous Pages in ASP.NET 2.0
you can't wait too long in asp.net to complete the task due to the fear of page recycling , session expires , it would be great that you zip these files using some other process and update a flag either in database or somewhere in the private network that your zip component is ready !! your asp.net application should read this flag and than let the user know that their zip is ready to be downloaded.
You can use BackgroundWorker component. It has ability to raise progress event which you can use to give feedback to the main thread (like UI).
public void static Main()
{
var bw = new BackgroundWorkder();
bw.DoWork += _worker_DoWork;
bw.RunWorkerCompleted += _worker_RunWorkerCompleted;
bw.ProgressChanged += _worker_ProgressChanged;
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = false;
//START PROCESSING
bw.RunWorkerAsync(/*PASS FILE DATA*/);
}
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
var data = e.Argument as /*DATA OBJECT YOU PASSED*/;
//PSEUDO CODE
foreach(var file in FILES)
{
zipFile;
//HERE YOU CAN REPORT PROGRESS
bw.ReportProgress(/*int percentProgress, object userState*/)
}
}
private void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Just update progress bar with % complete or something
}
private void _worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//...
}
else
{
//......
}
}
use the backgroundworker: http://msdn.microsoft.com/en-us/library/cc221403(VS.95).aspx
The BackgroundWorker object is probably what you are looking for.
Here is a good tutorial: http://dotnetperls.com/backgroundworker