Reputation: 2735
I have to write a Windows Form application that print a png from url, I'd like to write the code asynchronously. Here is my working code (sync)
public Bitmap GetImage()
{
Bitmap bmp;
var request = WebRequest.Create("http://www.basilicasanmarco.it/wp-content/themes/tema-basilicasmarco/library/images/patriarcato.png");
using (var response = request.GetResponse())
using (var stream = response.GetResponseStream())
{
bmp = new Bitmap(stream);
}
return bmp;
}
private void PrintImage(PaintEventArgs e)
{
using (var imag = GetImage())
{
if (imag == null)
return;
var newHeight = mainImage.Height;
var rapporto = newHeight / (double) imag.Height;
var newWidth = Convert.ToInt32(rapporto * imag.Width);
var imageLocationX = Convert.ToInt32((mainImage.Width - newWidth) / 2.0);
e.Graphics.DrawImage(imag, imageLocationX, 0, newWidth, newHeight);
}
}
Now I'd like to write the same code in a async await way, here is my snippet:
public async Task<Bitmap> GetImage()
{
Bitmap bmp;
var request = WebRequest.Create("http://www.basilicasanmarco.it/wp-content/themes/tema-basilicasmarco/library/images/patriarcato.png");
using (var response = await request.GetResponseAsync())
using (var stream = response.GetResponseStream())
{
bmp = new Bitmap(stream);
}
return bmp;
}
private async Task PrintImageAsync(PaintEventArgs e)
{
using (var imag = await GetImage())
{
var newHeight = mainImage.Height;
var rapporto = newHeight / (double) imag.Height;
var newWidth = Convert.ToInt32(rapporto * imag.Width);
var imageLocationX = Convert.ToInt32((mainImage.Width - newWidth) / 2.0);
e.Graphics.DrawImage(imag, imageLocationX, 0, newWidth, newHeight);
}
}
The first synchronous code works well, when I turn to the async part I have the "Parameter not valid" exception (System.Drawing exception) in the
e.Graphics.DrawImage(imag, imageLocationX, 0, newWidth, newHeight);
code. The Bitmap obtained seems ok, it has the correct Width and Height but something is wrong inside it, only in the asynchronous method.
What am I doing wrong?
Update:
I await the method in the Paint
method of a Panel
in the following way:
this.panel.Paint += new System.Windows.Forms.PaintEventHandler(this.mainImage_Paint);
private async void mainImage_Paint(object sender, PaintEventArgs e)
{
await PrintImageAsync(e);
}
What I want to obtain is to paint the downloaded image in a Panel instead that a PictureBox because I need a more low-level approach.
Upvotes: 1
Views: 48
Reputation: 7187
The Paint event handler is invoked for each re-draw of the control and you probably don't want to download the image every time this happens.
When the event handler is invoked, it seems like accessing the graphics object in the event args (e) parameter succeeds BEFORE an async operation but FAILS after an await.
Therefore;
It is not clear what triggers the download of the image in you source code, but if trying to do it async, get the image asynchronously and keep it in a field, and paint it on your panel if it is available.
The following assumes that you download the image directly, without any user action, and the place for this is the async void Form_Load(): (You can see that the image is downloaded in the background!)
private async void Form1_Load(object sender, EventArgs e)
{
this.panel1.Paint += this.mainImage_Paint;
downloadedBitmap = await GetImage();
// When we got the image, invalidate the panel to trigger a re-paint
this.panel1.Invalidate();
}
// Keep the downloaded image in a field:
private Image downloadedBitmap;
private async void mainImage_Paint(object sender, PaintEventArgs e)
{
if (downloadedBitmap != null)
{
var newHeight = mainImage.Height;
var rapporto = newHeight / (double)downloadedBitmap.Height;
var newWidth = Convert.ToInt32(rapporto * downloadedBitmap.Width);
var imageLocationX = Convert.ToInt32((mainImage.Width - newWidth) / 2.0);
e.Graphics.DrawImage(downloadedBitmap, imageLocationX, 0, newWidth, newHeight);
}
}
Upvotes: 2