I am trying to use webclient check download a stream before it is handled by an ExtendedImage, because my app is showing a bug when the uri is not found.
So my solution is to load the image first and then read the webclient result into the extended image.
This is what I am trying to do.
WebClient wc = new WebClient();
wc.OpenReadAsync(Uri);
wc.OpenReadCompleted += delegate(object Sender, OpenReadCompletedEventArgs e){
Logo = new BitmapImage();
ExtendedImage hExtendedImage = new ExtendedImage();
try
{
hExtendedImage.SetSource(e.Result);
Logo.SetSource(hExtendedImage.ToStream());
}
catch (WebException)
{
}
};
but now I am getting an "image is not loaded" error from hExtendedImage on this line
Logo.SetSource(hExtendedImage.ToStream());
I'm obviously loading the image from e.Result into the hExtendedImage wrong.
var client = new WebClient();
// Always define event handlers,
// BEFORE calling any method that could invoke them.
client.OpenReadCompleted += (s1, e1)
{
Logo = new BitmapImage();
var extendedImage = new ExtendedImage();
extendedImage.OnLoadingCompleted += (s2, e2)
{
// Invoke the dispatcher, so we're sure it's set on the UI thread.
Dispatcher.BeginInvoke(new Action
(
() => Logo.SetSource(extendedImage.ToStream()))
);
};
extendedImage.SetSource(e1.Result);
};
client.OpenReadAsync(Uri);
Unfortunately SetSource is ansyc. Use the event LoadingCompleted of hExtendedImage to set Logo source.
Be careful: LoadingCompleted callback is not in ui thread! You must invoke dispatcher if you want to change UI controls like Image.
From ExtendedBitmap source on CodePlex:
public void SetSource(Stream stream)
{
Contract.Requires<ArgumentNullException>(stream != null, "Stream cannot be null.");
if (_uriSource == null)
{
LoadAsync(stream);
}
}
Related
My app downloads several images from the internet and shows progress of the overall download in one single progress bar. The problems is that the progress isn't being updated correctly when using a BackgroundTransferService.
This is what I have.
From my PhoneApplicationPage_Loaded I call download a list of the images urls that i want to download.
WebClient wc = new WebClient();
wc.DownloadStringCompleted += wc_DownloadStringCompleted;
wc.DownloadStringAsync(new Uri(MyDownloadList), null);
Then in the wc_DownloadStringCompleted I register a custom event DownloadProgress in my DownloadManagerClass which updated the progress bar in the UI pbDownloadProgress
Then I call the code which adds a new BackgroundTransferRequest and register the TransferStatusChanged and TransferProgressChanged (that resides in the DownloadManagerClass).
void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
var up = e.Result;
Dispatcher.BeginInvoke(() => txtDownloadProgressText.Text = "Downloading");
downloadManager.DownloadProgress += (src, args) =>
{
Debug.WriteLine("Updating progress to " + args.Progress ` 100);
Dispatcher.BeginInvoke(() =>
{
if (pbDownloadProgress.IsIndeterminate)
{
pbDownloadProgress.IsIndeterminate = false;
pbDownloadProgress.Minimum = 0;
pbDownloadProgress.Maximum = 100;
}
pbDownloadProgress.Value = args.Progress * 100;
});
};
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += (s, ev) =>
{
downloadManager.DownloadImages(up);
};
bg.RunWorkerAsync();
}
}
And my DownloadImages method
internal void DownloadImages(String result)
{
List<String> URLs = ConvertStringToListUrls(result);
var localFile = IsolatedStorageFile.GetUserStoreForApplication();
if (!localFile.DirectoryExists(TRANSFERS_DIRECTORY))
{
localFile.CreateDirectory(TRANSFERS_DIRECTORY);
}
numberDownloadedItems = 0;
totalDownloadingItems = URLs.Count;
foreach (String item in URLs)
{
Debug.WriteLine("Adding Uri:" + uri);
Uri transferUri = new Uri(Uri.EscapeUriString(item), UriKind.RelativeOrAbsolute);
BackgroundTransferRequest transferRequest = new BackgroundTransferRequest(transferUri);
transferRequest.Method = "GET";
string downloadFile = transferFileName.Substring(transferFileName.LastIndexOf("/") + 1);
Uri downloadUri = new Uri(TRANSFERS_DIRECTORY + "/" + downloadFile, UriKind.RelativeOrAbsolute);
transferRequest.DownloadLocation = downloadUri;
transferRequest.TransferStatusChanged += new EventHandler<BackgroundTransferEventArgs>(transfer_TransferStatusChanged);
transferRequest.Tag = downloadFile;
transferRequest.TransferPreferences = TransferPreferences.AllowCellularAndBattery;
BackgroundTransferService.Add(transferRequest);
Debug.WriteLine("Adding transferID: " + transferRequest.RequestId);
}
}
Finally my transfer_TransferStatusChanged method which mainly calls the DownloadProgress event when one file is completed.
void transfer_TransferProgressChanged(object sender, BackgroundTransferEventArgs e)
{
if (transfer.TransferStatus == TransferStatus.Completed && (transfer.StatusCode == 200 || transfer.StatusCode == 206)) {
if (RemoveTransferRequest(transfer.RequestId))
if (DownloadProgress != null) DownloadProgress(this, new ProgressEventArgs(++numberDownloadedItems, totalDownloadingItems));
}
}
Every step seems to be working fine but the problem is that on the output I get the debug messages indicating the progress that should be updated (like the ones below) but the progressbar itself remains unchanged.
Removing transferID: {05ea9c10-025f-4e90-bc55-e9d36424b5f0}
10:37:27 AM:200: Updating progress to 14.2857142857143
Removing transferID: {e92f79fe-dc99-48b3-b0b1-76d3f5cedd51}
10:37:27 AM:651: Updating progress to 21.4285714285714
Removing transferID: {f61c8c9e-aa41-4e2a-8906-fca06961e69e}
10:37:28 AM:30: Updating progress to 28.5714285714286
Removing transferID: {46abc1df-37e6-432a-9b55-0adb3a8a2235}
I already tried to remove the dispatcher use, to reorganize the code but I can't seem to get it to work.
PS: I removed some exception catching from the code to keep it simpler
1: Can someone explain the last line of the first function to me?
2: The second function doesn't work. Please tell me why.The PHP script is fetching the data.
I edited the code to get this, but the app now crashes with a System nullreferenceexception.
Please help.
private void checkbutton_Click(object sender, RoutedEventArgs e)
{
statustext.Text = "Checking for new score";
var webclient = new WebClient();
webclient.OpenReadCompleted += new OpenReadCompletedEventHandler(getscores_OpenReadCompleted);
webclient.OpenReadAsync(new Uri("http://example.com/get.php?"+DateTime.Now));
}
void getscores_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
StreamReader s = null;
Stream reply=null;
try
{
reply = (Stream)e.Result;
s = new StreamReader(reply);
}
catch
{
statustext.Text = "ERROR IN FETCHING";
}
scorebox.Text = s.ReadToEnd();
statustext.Text = "DoNE";
}
The last line of the first method is attaching a handler to an event. It is saying that when the OpenReadCompleted event fires, that is to say when the read completes, the getscores_OpenReadCompleted method should be called.
The getscores_OpenReadCompleted doesn't work because it's trying to access a UI element from a non-UI thread.
You're also adding the handler after starting the asynchronous operation, so while it's unlikely, it's certainly possible that the operation completes very quickly and the event is fired before you add the handler. While this situation would be very unusual, it's fixed very quickly and easily by simply adding the handler before you start the asynchronous operation.
There are a couple of issues here:
Register the delegate before the call to OpenReadAsync
Read the stream from the event arguments and close the stream when you're done.
private void checkbutton_Click(object sender, RoutedEventArgs e)
{
statustext.Text = "Checking for new score";
var webclient = new WebClient();
webclient.OpenReadCompleted += new OpenReadCompletedEventHandler(getscores_OpenReadCompleted);
webclient.OpenReadAsync(new Uri("http://example.com/get.php?"+DateTime.Now));
}
void getscores_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
Stream reply = null;
StreamReader s = null;
string outputText = string.Empty;
try
{
reply = (Stream)e.Result;
s = new StreamReader(reply);
outputText = s.ReadToEnd();
}
finally
{
if (s != null)
{
s.Close();
}
if (reply != null)
{
reply.Close();
}
}
statustext.Text = outputText;
}
See the usage of the OpenReadAsync method here:
http://msdn.microsoft.com/en-us/library/system.net.openreadcompletedeventhandler(v=vs.110).aspx
and here
http://msdn.microsoft.com/en-us/library/ms144211(v=vs.110).aspx
I am using VS2010 on a WPF app. I cannot use the async feature.
Basically I need to load two images, and after images are downloaded a need to print them.
I need to manage the downloading of both images. How to achieve this?
var bitmap = new BitmapImage(new Uri("http://abcd.com/logo.png"));
var bitmapB = new BitmapImage(new Uri("http://abcd.com/logoB.png"));
if (!bitmap.IsDownloading)
{
// print immediately code here
}
else
{
bitmap.DownloadCompleted += (o, e) =>
{
// print when download completed
};
}
if (!bitmapB.IsDownloading)
{
// print immediately
}
else
{
bitmapB.DownloadCompleted += (o, e) =>
{
// print when download completed
};
}
It sounds like what you really want is the WebClient Class. You can use a number of its methods to download either the image as a file, or as a byte collection. The best part is that you can also do this asynchronously, so it will all happen on a background thread automatically.
When using these asynchronous methods, you need to handle the relevant Download...Completed event to retrieve the results. From the DownloadStringCompletedEventHandler Event page on MSDN:
public static void DownLoadFileInBackground2 (string address)
{
WebClient client = new WebClient ();
Uri uri = new Uri(address);
// Specify that the DownloadFileCallback method gets called
// when the download completes.
client.DownloadFileCompleted += new AsyncCompletedEventHandler(
DownloadFileCallback2);
// Specify a progress notification handler.
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(
DownloadProgressCallback);
client.DownloadFileAsync (uri, "serverdata.txt");
}
Take a look at the WebClient.DownloadDataAsync Method and WebClient.DownloadFileAsync Method pages on the MSDN website for more information.
Something like the following code should do the job for a variable number of images. It downloads all images sequentially in a ThreadPool thread before invoking a PrintImages method in the UI thread. Note the after downloading each image is frozen (by image.Freeze()) in order to make it cross-thread accessible.
private void DownloadAndPrintImagesAsync(IEnumerable<string> urls)
{
ThreadPool.QueueUserWorkItem(o =>
{
var images = urls.Select(url => DownloadImage(url));
Dispatcher.Invoke(new Action(() => PrintImages(images)));
});
}
private BitmapImage DownloadImage(string url)
{
var buffer = new WebClient().DownloadData(url);
var image = new BitmapImage();
using (var stream = new MemoryStream(buffer))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
}
image.Freeze();
return image;
}
private void PrintImages(IEnumerable<BitmapImage> images)
{
// print here
}
You might improve this by issuing multiple downloads in parallel, but that would complicate the code. You would have to wait for all asynchronous downloads to be finished before printing.
Update: Based on the proposal given by Sheridan, you may modify the DownloadAndPrintImagesAsync method like this:
private List<BitmapImage> images = new List<BitmapImage>();
private void DownloadAndPrintImagesAsync(IEnumerable<string> urls)
{
foreach (var url in urls)
{
var webClient = new WebClient();
webClient.DownloadDataCompleted += ImageDownloadCompleted;
webClient.DownloadDataAsync(new Uri(url));
}
}
private void ImageDownloadCompleted(object sender, DownloadDataCompletedEventArgs e)
{
if (!e.Cancelled && e.Error == null)
{
var image = new BitmapImage();
using (var stream = new MemoryStream(e.Result))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
}
images.Add(image);
if (images.Count == 2) // or whatever
{
// print images
}
}
}
I am writing a C# application for uploading and downloading file. The download uses WebClient object and its DownloadAsycDownload method. The download works fine for multiple files. It downloads as much files as I want.
My problem is I am not able to show the progress of all file in different progress bars which are dynamically added to the form's flowlayout control.
Here is my code:
public ProgressBar[] bar;
public int countBar=0;
...
bar[countBar] = new ProgressBar();
flowLayoutPanel1.Controls.Add(bar[countBar]);
countBar++;
request.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownoadInProgress);
request.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadFileCompleted);
request.DownloadFileAsync(new Uri(this.uri), localPath);
byte[] fileData = request.DownloadData(this.uri);
FileStream file = File.Create(localPath);
file.Write(fileData, 0, fileData.Length);
file.Close();
}
public void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
flowLayoutPanel1.Controls.Remove(bar[countBar]);
countBar--;
MessageBox.Show("Download Completed");
}
public void DownoadInProgress(object sender, DownloadProgressChangedEventArgs e)
{
bar[countBar].Maximum = total_bytes;
bar[countBar].Value = (int)e.BytesReceived;
}
You're using a count to index into progress bars, but once one is complete - you remove the last one, where you really should remove the one associated with the file.
I suggest, in this case, using a Dictionary<WebClient, ProgressBar> (might not be WebCliet - should be the type of sender in the events).
...
var progBar = new ProgressBar();
progBar.Maximum = 100;
flowLayoutPanel1.Controls.Add(progBar);
request.DownloadProgressChanged += DownoadInProgress;
request.DownloadFileCompleted += DownloadFileCompleted;
request.DownloadFileAsync(new Uri(this.uri), localPath);
dic.Add(request, progBar);
// You shouldn't download the file synchronously as well!
// You're already downloading it asynchronously.
// byte[] fileData = request.DownloadData(this.uri);
// FileStream file = File.Create(localPath);
// file.Write(fileData, 0, fileData.Length);
// file.Close();
Then, you can remove countBar altogether, and have the new methods:
public void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
// Can remove (WebClient) cast, if dictionary is <object, ProgressBar>
var request = (WebClient)sender;
flowLayoutPanel1.Controls.Remove(dic[request]);
dic.Remove(request);
MessageBox.Show("Download Completed");
}
public void DownoadInProgress(object sender, DownloadProgressChangedEventArgs e)
{
var progBar = dic[(WebClient)sender];
progBar.Value = e.ProgressPercentage;
}
I would use something like this with lambdas, a bit more concise, no need in dictionary:
var webClient = new WebClient();
var pb = new ProgressBar();
pb.Maximum = 100;
flowLayoutPanel1.Controls.Add(pb);
webClient.DownloadProgressChanged += (o, args) =>
{
pb.Value = args.ProgressPercentage;
};
webClient.DownloadFileCompleted += (o, args) =>
{
flowLayoutPanel1.Controls.Remove(pb);
};
webClient.DownloadFileAsync(new Uri(this.uri), localPath);
I'm trying to download some HTML source code with DownloadStringAsync. My code looks like this:
WebClient client = new WebClient();
client.DownloadStringCompleted +=
new DownloadStringCompletedEventHandler(DownloadStringCallback2);
client.DownloadStringAsync(new Uri(url));
private void DownloadStringCallback2(Object sender, DownloadStringCompletedEventArgs e)
{
source = (string)e.Result;
if (!source.Contains("<!-- Inline markers start rendering here. -->"))
MessageBox.Show("Nope");
else
MessageBox.Show("Worked");
}
If I look at the variable "source" I can see that some of the source is there, but not all. However, if I do something like this it works:
while (true)
{
source = wb.DownloadString(url);
if (source.Contains("<!-- Inline markers start rendering here. -->"))
break;
}
Unfortunately I can't use that approach since WP8 don't have DownloadString.
Does anyone know how to fix this or if there is a better approach?
This function should help you out
public static Task<string> DownloadString(Uri url)
{
var tcs = new TaskCompletionSource<string>();
var wc = new WebClient();
wc.DownloadStringCompleted += (s, e) =>
{
if (e.Error != null) tcs.TrySetException(e.Error);
else if (e.Cancelled) tcs.TrySetCanceled();
else tcs.TrySetResult(e.Result);
};
wc.DownloadStringAsync(url);
return tcs.Task;
}