I want delete java folder with C#,
but I have little problem.
this is the code
private void setDebug(string value)
{
debug.Text = value;
}
private void buildButton_Click(object sender, EventArgs e)
{
// delete java folder
string java_folder = #"C:\Program Files\Java";
if (Directory.Exists(java_folder))
{
setDebug("Deleting Java folder...");
Directory.Delete(java_folder, true);
progressBar.Value += 10;
}
}
when I click on the button the program stuck, but when i delete the line Directory.Delete
it change the debug label to "Deleting java folder..."
I know that the program stuck because it delete the folder but i want it change the debug first before it delete the folder.
what to do? thanks for help :)
The reason that your code doesn't appear to be working is because of the way WinForms handles UI updates.
The UI will not repaint until your method buildButton_Click completes - and nor will the UI be responsive until that time. The Directory.Delete line is something that will take a long time to run, and so your program "sticks" and you don't see the debug label.
What you need to do is look into using threading - read up on Threads, Tasks or the BackgroundWorker class in order to understand how they work. Then, instead of calling Directory.Delete directly in your method, use one of those techniques to run the delete on a background thread.
You need to wrap your code in a different thread, like this:
private void setDebug(string value)
{
debug.Text = value;
}
private void buildButton_Click(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
string java_folder = #"C:\Program Files\Java";
if (Directory.Exists(java_folder))
{
setDebug("Deleting Java folder...");
worker.DoWork += (s, args) => // this is the off-thread code
{
// delete java folder
Directory.Delete(java_folder, true);
};
worker.RunWorkerCompleted += (s,args)=> // this goes off when .DoWork is done
{
progressBar.Value += 10;
};
// this invokes .DoWork handler (which we defined above)
worker.RunWorkerAsync();
}
}
Related
Long story short, due to shifting business requirements, I need to be able to show the end user the progress of a file archival process controlled by a C# console application. The console app essentially gets a list of local files from a db and then copies them to an archive location. This was originally supposed to be a background process triggered by a scheduled task. Now, however, users can launch it manually with various arguments from the command line, so I was recently tasked with letting the user know the status of the archive process.
I thought I would just use a WPF ProgressBar control for this, but now I'm going in circles trying to sort out the best way to do this. I've been working with the answer from #JamesWilkins here: WPF window from a Console project?
I've added the ProgressBar window to the console application, and added the following to the Main method in the console(super simplified for clarity):
[STAThread]
static void Main(string[] args)
{
// EXISTING CONSOLE LOGIC
ParseCommandLineArgs();
Configure();
// ADDED
InitializeWindows(); // opens the WPF window and waits here
// EXISTING CONSOLE LOGIC
BeginArchival();
}
static void InitializeWindows()
{
WinApp = new Application();
WinApp.Run(ProgressBar = new ProgressBar()); // blocking call
}
Then in the ProgressBar.xaml code behind:
public partial class ProgressBar : Window
{
public ProgressBar()
{
InitializeComponent();
}
private void ProgressBar_OnContentRendered(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// foreach file that is archived, report progress to the
// ProgressBar.
for (int i = 0; i < 100; i++)
{
(sender as BackgroundWorker).ReportProgress(i);
}
}
private void worker_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
PbArchiveStatus.Value = e.ProgressPercentage;
}
}
The process waits at the InitializeWindows() method until the progress bar is closed, so it doesn't hit any of the archive logic that I need the progress bar to show progress for. It seems that I essentially need to put all of the existing console logic inside the ProgressBar.worker_DoWork() method, but at this point my brain is starting to hurt so I thought I'd reach out.
Am I on the right track, or is there a better way to add a GUI-based progress bar to a console utility? Let me know if I can clarify anything at all.
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 a MainFrame window with imageViewer control on it. Also there is my dll which calculates changes for the image all was working fine before I decided to add ProgressDialog.(( The Idea was - firstly I am loading the image via dll to main frame (this still OK). Then if user clicks button then show ProgressDialog and in worker.DoWork create new image via the same dllwrapper class (I am using "new")
All seems to be ok but when i am trying to set my currentImage property of imageviewer control (this is nothing more then setter for Image)it show me this error!
This is the code of my userButtonClickHandler from where I am launching ProgressDialog:
void OnThumbnailClick(object sender, RoutedEventArgs e)
{
pd = new ProgressDlg();
pd.Cancel += CancelProcess;
int max = 1000;
System.Windows.Threading.Dispatcher pdDispatcher = pd.Dispatcher;
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
LibWrap lwrap = new LibWrap();//!NEW instance for dll wrapper!
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//ERROR IS HERE!!!//The calling thread cannot access this object because a different thread owns it.
//what process??
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
pd.Close();
};
worker.RunWorkerAsync();
pd.ShowDialog();
}
There is function from the same MainFrame class for canceling (There is OK too)
void CancelProcess(object sender, EventArgs e)
{
worker.CancelAsync();
}
This is class for ProgressDlg (it has nothing more then progress bar and cancel button):
public partial class ProgressDlg : Window
{
public ProgressDlg()
{
InitializeComponent();
}
public string ProgressText
{
set
{
this.lblProgress.Content = value;
}
}
public int ProgressValue
{
set
{
this.progress.Value = value;
}
}
public event EventHandler Cancel = delegate { };
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
Cancel(sender, e);
}
}
}
I am working with this problem for (almost) two days and still couldn't find the solution. Help me please if you have an idea.
1 UPDATE
It seems to me that you was right about this threads - when I am trying to load previously loaded(initial) image (from the main thread) -it loads OK but if I am trying libWrap it fails due to processes conflict!
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
imageViewer.Width = 1000;//work!
imageViewer.CurrentImage = MyPrj.App.draggedImage;//Work!
imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//Fail =(!
}
2 UPDATE
I have tried this construction OnThumbnailClick
Application.Current.MainWindow.Dispatcher.BeginInvoke(new Action(() =>
{
imaeViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(FXPhotoStudio.App.draggedImage));
}
This caused same error/ Perhaps it will be correct to pass this value in MainThread (UI)? But I have no idea how.( I couldnot use serializers - becouse it is rapidly calling operation and this images are temporary/
WPF cannot alter items that were created on another thread.
So if you create an ImageViewer on one thread, you cannot alter it's properties on another thread.
Instead, use the Dispatcher, which is WPF's internal message queue for the main UI thread, to update your objects.
Or, use Henk's Answer to do your work on another thread, but return the result to the main thread so it can update your ImageViewer's properties
You need at least these changes:
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
args.Result = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
if (args.Error != null)
{ ... } // handle error
else if (args.Cancelled)
{ ... } // handle Cancel
else
{
imageViewer.CurrentImage = args.Result;
}
pd.Close();
}
I'm not sure if it's enough but try again.
The imageViewer was created on the main thread of the application (which is appropriate because it is a UI control). UI controls can ONLY be accessed by the thread which created it, and that thread must have its own dispatcher (by which I mean message loop).
Remove the threading code, and it will work.
If you want this to popup the window and then show the image when the conversion completes, you will have to store the returned image in a variable until you return to the main thread, and then make the assignment to the imageViewer.
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
{
}
}
}
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