How to use WPFtoolkit busyindicator into the WPF application - c#

I have made an application in WPF where I am making a long conversion process. so I wanted to put progress bar window, so i have used BusyIndicator progress bar from wpftoolkit dll and trying to show when my conversion will start.
But when I click on Conversion button it showing exception -
"the calling thread cannot access this object because a different thread owns it"...
My Code -
private void ConvertBtn_OnClick(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
ConversionToExcel();//My conversion methode
for (int i = 0; i < 10; i++)
{
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(
() => { ProgressIndicator.BusyContent = string.Format("Inprogress, please wait..."); }
));
Thread.Sleep(1000);
}
}).ContinueWith((task) => { ProgressIndicator.IsBusy = false; }, TaskScheduler.FromCurrentSynchronizationContext()
);
}

Set IsBusy=true before starting new thread.
private void ConvertBtn_OnClick(object sender, RoutedEventArgs e)
{
ProgressIndicator.BusyContent = string.Format("Inprogress, please wait...");
Task.Factory.StartNew(() =>
{
ConversionToExcel();//My conversion method
}).ContinueWith((task) => { ProgressIndicator.IsBusy = false; }, TaskScheduler.FromCurrentSynchronizationContext()
);
}
Also, I think you should have your own IsBusy property and bind it to ProgressIndicator.IsBusy in XAML.

Related

Background worker is not reporting progress Winforms

I have a background worker running, which is dynamically making form fields from an xml file. Depending on the size of the xml, it takes some time to load, so I am using a loading bar to report the progress to use so they won't exit out of the program. The program works as intended, it hides the loading panel and shows the form fields when the worker finishes, but while loading, the loading bar won't load. I received no errors.
This is where the report progress is being called:
if (!retrievePath.Equals(""))
{
// create the template with the data from the file
XDocument filledDoc = templateCreator.CreateTemplateWithGivenData2(retrievePath, fileName2);
tempDoc = filledDoc;
XElement root = tempDoc.Root;
// get child forms of return data state and sections
IDataInterface dataInterface = new DataInterfaceImplementation();
IEnumerable<XElement> sections = dataInterface.GetSections(filledDoc);
// Grab forms that aren't empty
IEnumerable<XElement> forms = XmlClass.GetMefForms(filledDoc).Where(u => u.Value != "").ToList();
IEnumerable<XElement> extra = dataInterface.GetSections(filledDoc).Where(u => u.Value != "").ToList();
// get the return header state
elemForms = dataMiddleman.GetSections(filledDoc);
foreach (XElement el in elemForms)
{
if (el.Name.LocalName.Equals("ReturnHeaderState"))
{
createForms(el, 3);
}
}
foreach (XElement el in forms)
{
i = i + 1;
i = (i / forms.Count()) * 100;
if (i == 100)
{
i = (i / (forms.Count() - 1)) * 100;
}
createForms(el, i);
}
private void createForms(XElement x, int i)
{
this.Invoke((MethodInvoker)delegate {
backgroundWorker1.ReportProgress(i);
var pLabel = new ParentLabel(x);
this.leftGroup.Controls.Add(pLabel);
var parentPanel = new CustomPanel(x);
parentPanel.SendToBack();
this.thebox.Controls.Add(parentPanel);
RecursiveTraverse(x, parentPanel);
pLabel.Click += (sender, e) => PLabel_Click(sender, e);
pPanels.Add(parentPanel);
});
}
This is my background worker code:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
loadingPanel.BringToFront();
populateNewFields();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
loadingBar.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
loadingBar.Value = 100;
Thread.Sleep(100);
loadingPanel.SendToBack();
loadingBar.Value = 0;
}
Your question is about Background worker is not reporting progress Winforms and I hope it's ok if I use a Minimal Reproducible Example to demo how to successfully fire an event when progress occurs on the background thread (which is is one way to achieve the outcome you want) and reducing the complex Xml operations to a "time-consuming black box" to be dealt with as a separate issue.
This Form will provide a means to test the notification using the MockCreateForm method which mimics a form creation by blocking the background worker for 5 ms. I believe your design spec is to send a notification every 100 operations.
Generic event lacks the needed properties so inherit EventArgs to customize the info received (declaring it outside the MainForm class).
public delegate void ProgressEventHandler(ProgressEventArgs e);
public class ProgressEventArgs : EventArgs
{
public ProgressEventArgs(int count, int total)
{
Count = count;
Total = total;
}
public int Count { get; }
public int Total { get; }
}
When the button (actually a CheckBox where Appearance=Button) state is toggled, it calls this worker Task using a CancellationTokenSource and CancellationToken so it can be halted. Every 100 times, the Progress event is fired:
private void btnWorker_CheckedChanged(object sender, EventArgs e)
{
if(btnWorker.Checked)
{
_cts = new CancellationTokenSource();
Task.Run(() =>
{
var formCount = 10000;
for (int i = 0; i < formCount; i++)
{
if(_cts.IsCancellationRequested)
{
return;
}
// Notify every 100 times.
if((i % 100) == 0)
{
Progress?.Invoke(new ProgressEventArgs(count: i, total: formCount));
}
MockCreateForm();
}
Progress?.Invoke(new ProgressEventArgs(count: formCount, total: formCount));
}, _cts.Token);
}
else
{
_cts.Cancel();
labelStatus.Text = "Idle";
}
}
CancellationTokenSource _cts = null;
The only thing left is to consume the event in the MainForm. The only thing that needs to be marshalled back onto the UI thread is the brief moment that Label.Text is being updated.
public MainForm()
{
InitializeComponent();
Progress += (e) =>
{
Invoke((MethodInvoker)delegate
{
labelStatus.Text = $"{e.Count} of {e.Total}";
});
};
}
public event ProgressEventHandler Progress;
What you do on the background thread is up to you. Just put it here:
public void MockCreateForm()
{
Task.Delay(5).Wait();
}
I hope this gets you closer to what you are trying to achieve.

System.Threading.Tasks.TaskCanceledException: 'A task was canceled.' when Closing App

I have a thread that change the field
private void SongChange(object sender, RoutedEventArgs e)
{
SongChangeAction();
songLength = (int)BGMPlayer.NaturalDuration.TimeSpan.TotalSeconds;
songProgress = new Thread(SongProgressUpdate) { IsBackground = true };
songProgress.Start();
}
private void SongProgressUpdate()
{
while (true)
{
Dispatcher.Invoke(() => { workingResources.SongProgress = BGMPlayer.Position.TotalSeconds / songLength * 100; });
Thread.Sleep(1000);
}
}
Note: songLength is a double, BGMPlayer is a MediaElement to play music in background, workingResources.SongProgress is a double bind to a Progress Bar
When I close the Program using "X" button or on taskbar, The program threw exception System.Threading.Tasks.TaskCanceledException Even I try to set the thread IsBackground = true but it work not throw exception If I stop the program using Visual Studio.
The Program Throw exception at line:
Dispatcher.Invoke(() => { workingResources.SongProgress = BGMPlayer.Position.TotalSeconds / songLength * 100; });
I can prevent it by using try/catch but I want to find something more efficient
Try using a CancellationToken on close of the window:
private readonly CancellationTokenSource _shutDown = new CancellationTokenSource();
public WindowName()
{
this.Closed =+ (s, e) => this._shutDown.Cancel();
}
private void SongChange(object sender, RoutedEventArgs e)
{
SongChangeAction();
songLength = (int)BGMPlayer.NaturalDuration.TimeSpan.TotalSeconds;
songProgress = Task.Factory.StartNew(SongProgressUpdate, this._shutDown.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
private void SongProgressUpdate()
{
while (!this._shutDown.IsCancellationRequested)
{
Dispatcher.Invoke(() => { workingResources.SongProgress = BGMPlayer.Position.TotalSeconds / songLength * 100; });
Thread.Sleep(1000);
}
}
Try using Microsoft's Reactive Framework. Then you can do this:
private void SongChange(object sender, RoutedEventArgs e)
{
SongChangeAction();
songLength = (int)BGMPlayer.NaturalDuration.TimeSpan.TotalSeconds;
IDisposable subscription =
Observable
.Interval(TimeSpan.FromSeconds(1.0))
.ObserveOnDispatcher()
.Subscribe(x => workingResources.SongProgress = BGMPlayer.Position.TotalSeconds / songLength * 100);
}
You just need to make sure that you save a reference to subscription and call .Dispose() on it when you are closing your app.
NuGet "System.Reactive" and "System.Reactive.Windows.Threading", and add using System.Reactive.Linq; to your code.

Create and show progressBar with backgroundWorker - VS2013

I want show marquee progress bar in another thread of my app.
Here is my code:
bkgWorker->RunWorkerAsync();
private: System::Windows::Forms::ProgressBar^ progressBar;
private: System::Void bkgWorker_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
progressBar = (gcnew System::Windows::Forms::ProgressBar());
progressBar->Location = System::Drawing::Point(548, 349);
progressBar->MarqueeAnimationSpeed = 15;
progressBar->Name = L"progressBar";
progressBar->Size = System::Drawing::Size(100, 23);
progressBar->Style = System::Windows::Forms::ProgressBarStyle::Marquee;
progressBar->TabIndex = 23;
progressBar->Show();
}
private: System::Void bkgWorker_RunWorkerCompleted(System::Object^ sender, System::ComponentModel::RunWorkerCompletedEventArgs^ e) {
progressBar->Hide();
}
There is no fault, but I do not see the progress bar on my form.
What am I doing wrong ?
Thanks for help.
there are better and newer solutions replaced the old good background worker.
I suggest you to look at async await design.
Read this post: Reporting Progress from Async Tasks
The code should look something like this:
public async void StartProcessingButton_Click(object sender, EventArgs e)
{
// The Progress<T> constructor captures our UI context,
// so the lambda will be run on the UI thread.
var progress = new Progress<int>(percent =>
{
textBox1.Text = percent + "%";
});
// DoProcessing is run on the thread pool.
await Task.Run(() => DoProcessing(progress));
textBox1.Text = "Done!";
}
public void DoProcessing(IProgress<int> progress)
{
for (int i = 0; i != 100; ++i)
{
Thread.Sleep(100); // CPU-bound work
if (progress != null)
progress.Report(i);
}
}

show progress of the background task on a pop-up windows in windows form application using C#.Net

I am creating some files from xml data in the background using
Task.Factory.StartNew(() => xmlconvert(xx, yy));
Now, the question is how to show the progress of this method using a StatusStrip control with some message and the progress or at least just a scrolling animation for the progress. I don't just have any idea how would it work.
Update:
First of all, this method 'xmlconvert(xx, yy)' has four different forms depends on the condition user selects at runtime.
In the main form of my application user can select from different conditions to process on the data. Then finally when user click on the Button 'Create' all these conditions are being checked and a suitable method will be called within that button click event. I need to show the progress of this method which is being invoked at runtime.
private void btnCreateRelease_Click(object sender, EventArgs e)
{
// Checks set of conditions
if(cond 1)
{
xmlconvert_1();
}
else if (cond2)
{
xmlconvert_2();
}
else if (cond3)
{
xmlconvert_3();
}
else if (cond4)
{
xmlconvert_4();
}
}
I want to show progress of one of these methods which will be invoked at runtime depends on the condition.
Thanks a lot.
You can use the BackgroundWorker for this, and it's pretty simple, too. Here's a sample to get you going:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
void Form1_Shown(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Do your work in here.
xmlconvert(xx, yy);
for (int i = 0; i <= 100; i++)
{
backgroundWorker1.ReportProgress(i);
System.Threading.Thread.Sleep(100);
}
}
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
}
And here's the link to the documentation.
To get it to work in your scenario, I would suggest you add a Progress bar to your StatusStrip control and update it from within the backgroundWorker1_ProgressChanged event.
If you wish just to show, that your app is not hang may help following approach:
public static class ActionExtensions
{
public static void RunWithMargueProgress(this Action action)
{
var progressForm = new ProgressForm();
progressForm.Show();
Task.Factory.StartNew(action)
.ContinueWith(t =>
{
progressForm.Close();
progressForm.Dispose();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
Where ProgressForm would be a simple form with ProgressBar, that is set to Marquee style. If you have idea, how it is progressing, it is better to show progress for user and use BackgroundWorker.
As long as it's parameter is Action, it is easily reusable.
Usage would be:
private void button_Click(object sender, EventArgs e)
{
Action action = () => Thread.Sleep(5000);
action.RunWithMargueProgress();
}
If you have control in status strip, that you wish to animate, you can do it like this:
public static void RunWithMargueProgress(this Action action, ToolStripProgressBar progressBar)
{
progressBar.Style = ProgressBarStyle.Marquee;
progressBar.MarqueeAnimationSpeed = 30;
Task.Factory.StartNew(action)
.ContinueWith(t =>
{
progressBar.MarqueeAnimationSpeed = 0;
progressBar.Style = ProgressBarStyle.Continuous;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
Usage would be pretty much the same:
private void button_Click(object sender, EventArgs e)
{
Action action = () => Thread.Sleep(5000);
action.RunWithMargueProgress(ToolStripProgressBar);
}

UI freezes even process start with separate thread?

In My application have time consuming process.There fore i try to do that operation in separate thread.Even i Stared it separate thread my Main UI still freezes during the time of long running process.But still i couldn't figure out the reason for that?Some thing wrong in my code?
My Event Hander Code
private void BtnloadClick(object sender, EventArgs e)
{
if (null != cmbSource.SelectedItem)
{
string selectedITem = ((FeedSource) cmbSource.SelectedItem).Url;
if (!string.IsNullOrEmpty(selectedITem))
{
Thread starter = new Thread(() => BindDataUI(selectedITem));
starter.IsBackground = true;
starter.Start();
}
}
private void BindDataUI(string url)
{
if (feedGridView1.InvokeRequired)
{
BeginInvoke(new Action(() => BindDataGrid(url)));
}
else
BindDataGrid(ss);
}
private void BindDataGrid(string selectedItem)
{
for (int i = 0; i < 10; i++)
{
//Time consuming Process
}
}
Your thread is completely useless :-)
In your thread you are executing BindDataUI which marshals the execution back to the UI thread using Invoke.
Your complete code is equivalent to this:
private void BtnloadClick(object sender, EventArgs e)
{
if (null != cmbSource.SelectedItem)
{
string selectedITem = ((FeedSource) cmbSource.SelectedItem).Url;
if (!string.IsNullOrEmpty(selectedITem))
{
BindDataGrid(selectedITem);
}
}
private void BindDataGrid(string selectedItem)
{
for (int i = 0; i < 10; i++)
{
//Time consuming Process
}
}
It would be better to only marshal these parts of BindDataGrid to the UI thread that really need to run on this thread because they need to update the UI.

Categories

Resources