I have an application where I am uploading a file in blocks. My front end is WPF and I have a progress bar to show file upload progress (upload is done by separate thread, and the progress bar is in a separate form invoked by the child thread when uploading starts).
I found the total number of blocks in the file to set the maximum property of the progress bar.
Now for each block uploaded I increment the value of progress bar by 1.
But to my surprise, the progress bar starts to increment but never completes ( it stops showing progress after few blocks ).
Here is code for the thread responsible for uploading files:
System.Threading.Thread thread = new Thread(
new ThreadStart(
delegate()
{
// show progress bar - Progress is the name of window containing progress bar
Progress win = new Progress();
win.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
win.Show();
// find number of blocks
long BlockSize = 4096;
FileInfo fileInf = new FileInfo(filename);
long FileSize = fileInf.Length;
long NumBlocks = FileSize / BlockSize;
//set the min and max for progress bar
win.Dispatcher.Invoke(
new Action(
delegate()
{
win.progressBar1.Minimum = 0;
win.progressBar1.Maximum = NumBlocks;
}
), System.Windows.Threading.DispatcherPriority.Render);
//upload file
while (true)
{
// code to upload the file
win.Dispatcher.Invoke(
new Action(
delegate()
{
win.progressBar1.Value += 1;
}
), System.Windows.Threading.DispatcherPriority.Render);
}
}
Can someone help me analyze why is this happening.
Thanks.
Here's the issue:
upload is done by separate thread, and
the progress bar is in a separate form
invoked by the child thread when
uploading starts
If that means your child thread created the form, then that's the problem. Your child thread might be updating the progress bar values, but this will just invalidate the display, and not necessarily refresh the display. When a control's display is invalidated, it simply records that it must redraw it's display the next time it gets a chance. A refresh is when the control actually gets to render to the screen.
A better approach is to create the progress bar form in the main thread.
Your worker thread can then update the status, and your main thread will refresh the display.
One thing to remember: if you're updating a control that was created in a different thread, you must do so via the control's dispatcher.
var dispatcher = progressBar.Dispatcher;
dispatcher.BeginInvoke(new Action( () => { progressBar.Value = currentProgress }));
Edit after seeing the code
All you need to do is move the creation of the progress variable so that it is instantiated by the main thread before the worker thread is created.
Progress win = new Progress();
win.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
win.Show();
System.Threading.Thread thread = new Thread(
new ThreadStart(
delegate()
{
// ...
Related
I try to export in Png a WPF UserControl that I made in a background Thread.
The rendering works great in the UI thread, but I can't run my spinner for user waiting correctly because the two controls run in the UI thread.
When I run my code in the backgroung thread I have the following exception :
System.Windows.Markup.XamlParseException: 'The calling thread cannot access this object because a different thread owns it'
I use the following code to create the thread :
List<Byte[]> images = null;
Thread thread = new Thread(() =>
{
HorizontalViewDesigner horizontalViewDesigner = new HorizontalViewDesigner(true);
horizontalViewDesigner.ItemsSource = new ObservableCollection<ICanvasSelection>(elements);
horizontalViewDesigner.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
horizontalViewDesigner.Arrange(new Rect(new Size(10_000, 10_000)));
Size size = new Size(1024d, 1024d);
images = horizontalViewDesigner.GetImages(size);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
thread = null;
return (images);
The exception occurs in the GetImages method on the call of
userControl.UpdateLayout();
It's seems that this method update Dependency Properties from binding.
Any solution ?
Thanks
In my program I am using a separate thread to read from an external file and convey data to my program. I am in the process of trying to run a ProgressBar on the UI thread while that thread is running, in order to provide feedback to the user. As of now, I have a ProgressBar that opens, but does not close because I do not know how to tell it that the child thread has ended. I have read this tutorial on ProgressBars, and should make a note that I am not using a BackgroundWorker for my thread. Instead of this, I am just using a System.Threading.Thread. Please let me know if this is a problem.
The OpenFile() method that opens the Window containing the ProgressBar, and also launches the thread that will do the open file work:
public void OpenFile()
{
//Open Progress Bar Window
LoadingWindow = new LoadingScreen(App.MainWindowViewModel.LoadScreen);
LoadingWindow.Show();
App.MainWindowViewModel.LoadScreen.IsIndeterminate = true;
FILE_INPUT = true; //bool that Signifies that the background thread is running
//Create Thread -- run routines in thread
var openFileThread = new Thread(() => openDefault(txtFile)); //Passes the file
openFileThread.Start(); //Start File open thread
}
//This thread reads in the file and transfers the data to the program
private void openDefault(StreamReader txtFile)
{
//Gathers info from file and changes program accordingly
FILE_INPUT = false; //background thread is no longer running
}
As of now, I do not have an increment method for my ProgressBar, as I would like to make sure that I can get it working correctly before working on a technical detail like that. Anyway, my question is... how and where in my program do I make it known that the background thread is done running and the LoadingWindow (ProgressBar) can be closed?
Some more code to help give you an idea of my program's flow:
Main Window Code Behind:
private void OpenFile_Click(object sender, RoutedEventArgs e)
{
OpenFile(); //calls to the method that launches the new thread and the window with the Progress Bar
//Wait until background Thread is complete
while (ViewModel.FILE_INPUT == true) { }
//After thread has stopped running, close the window (DOES NOT WORK)
ViewModel.LoadingWindow.Close(); // -- Close Prog Bar
}
Eventual Solution:
//In OpenFile() method
LoadingWindow = new LoadingScreen(App.MainWindowViewModel.LoadScreen); //Open Progress Bar Window
LoadingWindow.Show();
App.MainWindowViewModel.LoadScreen.IsIndeterminate = true;
FILE_INPUT = true; //A file is being inputted*
Dispatcher UIDispatcher = Dispatcher.CurrentDispatcher;
Task.Factory.StartNew(() =>
{
openDefault(txtFile); //Passes the file
//After thread has stopped running, close the Loading Window
UIDispatcher.Invoke((Action)(() => LoadingWindow.Close())); //Close progress bar
});
Without a complete code example, it's hard to provide a complete answer. Depending on how you're actually doing the file I/O and managing the ProgressBar, there might be other changes you could make to also improve the code. But to solve the immediate problem, I recommend taking advantage of the async/await pattern:
public async Task OpenFile()
{
//Open Progress Bar Window
LoadingWindow = new LoadingScreen(App.MainWindowViewModel.LoadScreen);
LoadingWindow.Show();
App.MainWindowViewModel.LoadScreen.IsIndeterminate = true;
FILE_INPUT = true; //bool that Signifies that the background thread is running
//Create Thread -- run routines in thread
await Task.Run(() => openDefault(txtFile)); //Passes the file
}
private async void OpenFile_Click(object sender, RoutedEventArgs e)
{
await OpenFile(); //calls to the method that launches the new thread and the window with the Progress Bar
//After thread has stopped running, close the window
ViewModel.LoadingWindow.Close(); // -- Close Prog Bar
}
This will avoid causing your UI to freeze while the operation is working, while allowing the code to be written in a simple, straight-through manner.
It's possible that with the above changes, you can get rid of your FILE_INPUT variable altogether (it's not clear for what else you might be using it, but it might be nothing else).
Note that the OpenFile() method is changed to return Task. This is the norm for async methods without an actual return value. The same change would be made to the OpenFile_Click() method, except that as an event handler, these are generally required to return void. Either syntax is legal, but the former is preferred when possible so that access to the Task object is possible (for exception handling, completion, etc.).
EDIT: for those without .NET 4.5 and who cannot install the necessary components to support async/await (which is just a compile-time change), here's an example that will work in .NET 4.0:
public void OpenFile()
{
//Open Progress Bar Window
LoadingWindow = new LoadingScreen(App.MainWindowViewModel.LoadScreen);
LoadingWindow.Show();
App.MainWindowViewModel.LoadScreen.IsIndeterminate = true;
FILE_INPUT = true; //bool that Signifies that the background thread is running
//Create Thread -- run routines in thread
Task.Factory.StartNew(() =>
{
openDefault(txtFile); //Passes the file
//After thread has stopped running, close the window
Dispatcher.Invoke((Action)(() => ViewModel.LoadingWindow.Close())); // -- Close Prog Bar
});
}
private void OpenFile_Click(object sender, RoutedEventArgs e)
{
//calls to the method that launches the new thread and the window with the Progress Bar
OpenFile();
}
(This is actually semantically a little different from the async/await implementation, in that rather than creating a continuation for the first task, I just put the window-close operation in that task. But it should have the same net result).
I want to show progress bar while my application create complex screen. my code is:
protected override void LoadSubject(object sender)
{
var win = new Spinner();
win.Show();
Thread th = new Thread(() =>
{
LoadSubjectImpl(sender);
win.Dispatcher.BeginInvoke(new Action(() => win.Close()));
});
th.Start();
}
private void LoadSubjectImpl(object sender)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
StartServiceWorkflow(sender);
})
);
}
its works fine, but the progress bar freeze...
I must use the dispatcher in background thread because of "InvalidOperationException" and I think that the problem, but what can I do?
spinner = costum progressbar.
You will have no luck trying to achieve what you have described. If you'd read closely that InvalidOperationException you would know that you cannot manipulate UI from background thread. What you have done using Dispatcher is synchronizing StartServiceWorkflow to UI thread. So your code is executing in it - that causes the freeze.
To achieve some user experience enhancement you can delegate to background thread tasks like reading form database or processing data that is to be displayed.
I'm working on a project about PDF rendering in the C# language. I convert each page of PDF file to Image and Adds it to a ObservableCollection with a new thread by the below code:
ThreadStart myThreadDelegate = new ThreadStart(DoWork);
myThread = new Thread(myThreadDelegate);
myThread.SetApartmentState(ApartmentState.STA);
void DoWork()
{
for (int i = 0; i < pdfFile.Pages.Count; i++)
{
PdfPage page=pdfFile.LoadPage(i);
myObservableCollection[i]=page;
}
}
then pass the custom item of myObservableCollection to another UserControl for render it but I got an exception:
The calling thread cannot access this object because a different
thread owns it.
I know if I use UI thread my problem could be solved but I want load pdf pages in the background and user doesn't wait for loading all pages and this is possible with a new thread.
You can use threads but have to use the Dispatcher to access UI elements. Only the part, where you pass the item to the UserControl has to be done by the dispatcher.
Application.Current.Dispatcher.BeginInvoke(new Action(() => AddItem()));
BeginInvoke is a asynchronous call and won't block the execution of the following code.
Edit: I'm still not 100% sure if I unterstood the whole idea of your application but made a small sample which demonstrates how you can use threads and UI elements.
I made a Window (that would be your UserControl) which contains a Button and a ListBox. When clicking the Button a thread is started and processes some items. In my case it just adds some texts into a list, I added Thread.Sleep(1000) to simulate the processing of lots of stuff. When the text is prepared, it will be added to the ObservableCollection, which has to be done by the UI thread (Dispatcher). There is nothing blocking the UI but this adding and this is done very fast. You can also start multiple threads at the same time.
This is the code-behind of the Window (the Window itsself just contains a Button and a ListBox):
public partial class MainWindow : Window
{
private ObservableCollection<string> textList;
public MainWindow()
{
textList = new ObservableCollection<string>();
InitializeComponent();
btnStartWork.Click += BtnStartWorkClick;
lstTextList.ItemsSource = textList;
}
private void BtnStartWorkClick(object sender, RoutedEventArgs e)
{
Thread myThread;
ThreadStart myThreadDelegate = DoWork;
myThread = new Thread(myThreadDelegate);
myThread.SetApartmentState(ApartmentState.STA);
myThread.Start();
}
private void DoWork()
{
for (int i = 0; i < 5; i++)
{
string text = string.Format("Text {0}", i);
// block the thread (but not the UI)
Thread.Sleep(1000);
// use the dispatcher to add the item to the list, which will block the UI, but just for a very short time
Application.Current.Dispatcher.BeginInvoke(new Action(() => textList.Add(text)));
}
}
}
This code is from my C# WPF application:
public MainWindow()
{
InitializeComponent();
status("Getting dl links");
getLinks();
}
The procedure getLinks currently displays a couple of links in a messagebox. Those links are displayed in a messagebox before the WPF application becomes visible.
In this is case not a problem, but how would I show progress (like a progressbar) of any
procedure I want to load at startup?
Here is an example on how you can do it. To simplify it a bit, I added the controls directly in the MainWindow constructor, but I would prefer to do this with XAML.
public MainWindow()
{
InitializeComponent();
var progressBar = new ProgressBar();
progressBar.Height = 40;
progressBar.Width = 200;
progressBar.Margin = new Thickness(100, 100, 100, 100);
Task.Factory.StartNew(() =>
{
// getLinks();
for (var i = 0; i < 5; i++)
{
Dispatcher.Invoke(new Action(() => { progressBar.Value += 20; }));
Thread.Sleep(500);
}
});
var stackPanel = new StackPanel();
stackPanel.Children.Add(progressBar);
Content = stackPanel;
}
I first add a ProgressBar somewhere on the interface to make it visible for this demo and then I add it to a new StackPanel, it could be any panel at all, in this case it doesn't matter.
To load the links on another thread, I create a new Task, this is a part of the TPL (Task Parallel Library) in .NET 4.0. In this case I am simulating that getLinks() takes 5 * 500 milliseconds to run and that it in fact is five links that will be loaded, hence 20% each iteration.
What I do then is that I add 20 to the progressBar value, which indicates that it increased with 20%.
This line might confuse you a bit
Dispatcher.Invoke(new Action(() => { progressBar.Value += 20; }));
But it is in fact quite common when you do cross-thread programming with GUI. So the problem is that you are on another thread here, we started of a Task that will run on a separate thread, and you cannot update your UI thread from another thread. So what you need is something called a Dispatcher, and this is accessable from within your Window-class.
Then you Invoke an action on it, which means that you simply say "Run this piece of code on this thread for me".
And if you want to display a MessageBox when everything is loaded, you can simply add a MessageBox.Show("Loaded!"); after the for-loop.
Any 'loading' tasks need to happen on a background thread (see the BackgroundWorker class - google for lots of examples). That way, the UI thread is free to show your window and update your window with status messages. Otherwise, the UI thread is blocked from doing anything until your loading is complete.