How to replace Spinner with UIProgress view? - c#

I am relatively new to iOS Xamarin c# development.
I created an app for an iPad which on demand(button DownloadDocuments) downloads documents for offline use.
While download is in progress the "Loading overlay" is shown with the "spinner". The app is in use and everything works great...but.
Now the client wants to see a Progress bar instead of a spinner.
The design is: Master-Detail controller, button is in master, downloading is performed by a procedure in "Utilities" class.
I've read so many posts in all kins of forums, but this Threading matter is still very much "terra Nova" to me.
I coded the UIProgressView into Loading overlay and I can see it, if I assign some 0.5f value to the Progress property.
How do IO make the progress move?
Where(if I have to) do I invoke the new thread and progress value assignment:
- in Utilities.DounloadDocuments?
- In the cycle of downloading files(I'm using WebClient)?
- In the button handler?
Could somebody help me, please.

You would first need to add a class of type Progress and attach an event to the ProgessChanged Event. When that fires then you would set the "Progess" values of your UIProgressView.
Progress<DownloadBytesProgress> progress = new Progress<DownloadBytesProgress>();
progress.ProgressChanged += (s, args) => UIProgressBar.Progress = args.PercentComplete;
Then in your download routine, after each chunk of data is downloaded you would set...
DownloadBytesProgress args = new DownloadBytesProgress(urlToDownload, receivedBytes, totalBytes);
progess.Report(args);
You can see an example here...
github download progress

Related

Demand load web based images on background thread?

I have an application that loads some objects and displays them in a grid. One of those objects is a preview image which could be either a local image file or an image file on a web location. Right now it is working but the performance isn't very good when scrolling because whenever a new item scrolls into view it has to load the preview which takes a second or two.
What I am trying to accomplish is that the box for the preview would show up and start loading the preview on a background thread when it is scrolled into view. When the image is loaded it would notify (via property change event in the view model) and the image would come into place. This way the UI remains responsive and the preview is just blank until it loads and then it shows up. I have the getter for the bound property starting the backgroundWorker if the image isn't already cached, and the worker routine is as follows (_previewCache is the backing property for the bound preview):
void bw_LoadPreview_DoWork(object sender, DoWorkEventArgs e)
{
BitmapImage _tempCache = new BitmapImage(new Uri(PreviewImagePath, UriKind.RelativeOrAbsolute));
if (_tempCache.CanFreeze)
{
_tempCache.Freeze();
Dispatcher.CurrentDispatcher.Invoke((Action)(delegate { _previewCache = _tempCache; }));
}
}
This seems to work pretty well on local files, though it takes a little longer than I would expect for the images to appear, but the UI remains responsive. However when the image is a web path, it always ends up as _tempCache.CanFreeze = false so it skips it and doesn't load. If I try to do the invoke without freezing, it says it can't use the object because it's owned by a different thread.
What am I missing? How do you load a web based image this way? Or is there a better way to approach this issue that I'm not seeing? Any help is greatly appreciated.

Form not shown until the entire process is done

I have a Winform application that receives the command line arguments and does some processing (creating a file, insert data to the database and upload a file to FTP). So in order to see the process I have a listbox that displays information to the user. There is also a progress bar to see the insert status.
Inside my form load event I have a RunCampaign() function that itself invokes some other functions sequentially until the process is done. All these functions add items to the listbox.
The problem is: Nothing (not even an empty form) is shown until the entire process is done. And when the process is complete the entire form containing the listbox of information and the progress bar is shown. And that is not my expected behavior.
How can I solve this problem?
You need to multithread your application so your UI thread is free to show the form:
private void Form1_Load(object sender, System.EventArgs e)
{
Task t = new Task(() =>
{
//Logic
//...
//Update UI
this.Invoke((MethodInvoker)delegate
{
listbox.Items.Add(...); // runs on UI thread
});
});
t.Start();
}
If you need more examples check out this question: How to update the GUI from another thread in C#?
The best way is to use a BackgroundWorker.
If you don't want to use any multi-threading technique, then call RunCampaign() function in Form_Shown() event. Also, invoke Listbox.Update() & Progressbar.Update() whenever you modify any item/value. At least you will see the window, but it will not be responsive until the control comes out of Form_Shown().

Custom Search on objects in Metro / Modern UI

I'm implementing an application for Windows 8. It will search a quite large number of data from xml.
So for now I'm using a text box and list view to show the search results, but when i use the textbox event TextChanged is slows down the typeing, because it executes a new search for each letter typed.
So the question is, how to in metro / modern ui make a delay, to trigger the event after few letters are typed, not one.
You are discouraged from running anything that might bog down the UI as a synchronous task. Try running it as a background task which updates the UI on completion. See Background Task and TimeTrigger for more information. On task completion use the Core Dispatcher to update the UI. Core Dispatcher example
You should not create your own Search experience. You should hook into the SearchPane and handle it there.
SearchPane searchPane = SearchPane.GetForCurrentView();
searchPane.QuerySubmitted += searchPane_QuerySubmitted;
searchPane.SuggestionsRequested += searchPane_SuggestionsRequested;
You can then handle the events as necessary in the handlers. If you are providing suggestions and you have a slow running process that will by async, you msut get a deferral.
void searchPane_SuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
// do real time type ahead here
deferral.Complete();
}
void searchPane_QuerySubmitted(SearchPane sender, SearchPaneQuerySubmittedEventArgs args)
{
// complete search here
}
Why not use the "Leave" event instead of the text changed event of a text box? Would make it a lot smoother and would be when the user has finished their input inside the text box.

C# progress bar not synced with download (WebClient class)

I am coding a system which has a small FTP module included inside, it's not the main feature at all, but needed...
I must link the progressbar with the WebClient class event DownloadProgressChangedEventHandler and AsyncCompletedEventHandler, the progressbar increment is ok, and the ASyncCompletedEventHandler launch a MessageBox (as intended), the problem is that the progress bar see to load too slow...
problem :
My MessageBox pop at 100% (launched by the event handler), BUT when the MessageBox pop my progress bar is only at +-80% (but the .VALUE is really 100), the first though I had was that they have added a "smooth" effect in Windows Vista which slow down the progressbar relatively to it's true value.
If any of you have experienced the same problem thanks for your help.
For those of you who want to know, Vista actually add a "smoothing" to the loadingbar change, it look like the "Tweening effect" of Flash.
I tried on Windows XP and the bar reach 100% exactly when the value reach 100. On Vista it look like they have decided that the "loading" would be splitted over a delay X.
Before you display your message box, call Application.DoEvents() to force all the pending Windows queued messages to be processed. Your progress bar is probably not getting a chance to paint itself one last time before the message box displays.
Why not simply set the progress bar value to 100% on the same DownloadProgressChangedEventHandler event handler that displays the message box?

Run a modal dialog on a non-UI thread

I'm writing a simple data UI using standard .Net databinding to a typed DataSet from SQL Server.
I have a reload button which calls Fill on all of the DataAdapters to get new data from the database (in case another user changed the data).
This takes some time, during which the UI is frozen. It must be run on the UI thread or the databinding event handlers throw cross-thread exceptions.
I'd like to show a modal "Please Wait" dialog on a background thread (so that it can be animated) while the UI thread connects to the database.
How can I show a modal dialog box on the non-UI thread?
EDIT: I'm aware that best practice is to run the operation in the background, but I can't do that because of the databinding events.
You should do the opposite. Run your long-running process on a background thread and leave the UI thread free to respond to the user actions.
If you want to block any user actions while it is processing you have a number of options, including modal dialogs. Once the background thread completes processing you can inform the main thread about the outcome
The code running in the databinding events need to be decoupled from the UI, probably using some kind of data transfer object.
Then you can run the query operation in a separate thread or a BackgroundWorker, and leave the UI thread as it was.
Edit: The really quick way to fix this is to get the events to run in their own delegate using InvokeRequired and .Invoke. That will give the methods UI context. My co-worker does this like it's going out of style and it annoys me to no end because it's rarely a good idea to do it this way... but if you want a fast solution this will work. (I'm not at work so I don't have a sample with me; I'll try to come up with something.)
Edit 2: I'm not sure what you're asking for is possible. I made a sample app that created a modal dialog in another thread, and it ends up being modeless. Instead of using a modal dialog, could you use some other control or set of controls to indicate progress change, most likely directly on the same form?
using( var frmDialog = new MyPleasWaitDialog() ) {
// data loading is started after the form is shown
frmDialog.Load += (_sender, _e) {
// load data in separate thread
ThreadPool.QueueWorkItem( (_state)=> {
myAdapter.Fill( myDataSet );
// refresh UI components in correct (UI) thread
frmDialog.Invoke( (Action)myDataControl.Refresh );
// close dialog
frmDialog.Invoke( (Action)frmDialog.Close() );
}
}
// shows dialog
frmDialog.ShowDialog( this );
}
Here is an example of using BackgroundWorker to do the loading of data and running a user friendly form to show 'Loading records' or similar...
public void Run()
{
bgWorkrFillDS = new BackgroundWorker();
bgWorkrFillDS.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorkrFillDS_RunWorkerCompleted);
bgWorkrFillDS.DoWork += new DoWorkEventHandler(bgWorkrFillDS_DoWork);
bgWorkrFillDS.RunWorkerAsync();
}
void bgWorkrFillDS_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bgWrkrFillDS = (BackgroundWorker)sender as BackgroundWorker;
if (bgWrkrFillDS != null)
{
// Load up the form that shows a 'Loading....'
// Here we fill in the DS
// someDataSetAdapter.Fill(myDataSet);
}
}
void bgWorkrFillDS_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Hide or unload the form when the work is done
}
Hope this helps...
Take care,
Tom.
I solved this problem by creating a new DataSet, loading in in the background, then calling DataSet.Merge on the UI thread. Thanks everyone for your advice, which led to this solution.
As an added bonus, this runs much faster than it used to (calling Fill in the background, which only worked with no grids open). Does anyone know why?

Categories

Resources