I'am trying to create a BitmapImage in background thread (BackgroundWorker), but my function only returns at once null and doesn't enter into Deployment.Current.Dispatcher.BeginInvoke. When I used this function in UI thread, all was fine. The path to file is correct (it's a .jpg picture)
public static BitmapImage convertFileToBitmapImage(string filePath)
{
BitmapImage bmp = null;
Uri jpegUri = new Uri(filePath, UriKind.Relative);
StreamResourceInfo sri = Application.GetResourceStream(jpegUri);
Deployment.Current.Dispatcher.BeginInvoke(new Action( ( ) =>
{
bmp = new BitmapImage();
bmp.SetSource(sri.Stream);
}));
return bmp;
}
The problem is your using Dispatcher.BeginInvoke which will run the task asynchronously on the UI thread, there is no guarentee that by the time you return from the function the bitmap will be initialized. If you need this to be initialized immediately you should be using Dispatcher.Invoke so it all happens synchronously.
Update
Missed your tag for it being Windows Phone, however, the same problem still exists you are not giving your app enough time to initialize the bitmap. You could perhaps use an AutoResetEvent to wait for the Bitmap to be created before returning from the method e.g.
public static BitmapImage convertFileToBitmapImage(string filePath)
{
BitmapImage bmp = null;
Uri jpegUri = new Uri(filePath, UriKind.Relative);
StreamResourceInfo sri = Application.GetResourceStream(jpegUri);
AutoResetEvent bitmapInitializationEvt = new AutoResetEvent(false);
Deployment.Current.Dispatcher.BeginInvoke(new Action(() => {
bmp = new BitmapImage();
bmp.SetSource(sri.Stream);
bitmapInitializationEvt.Set();
}));
bitmapInitializationEvt.WaitOne();
return bmp;
}
Related
I am trying to make images load in different thread but image is never updated.
public class MyImage : System.Windows.Controls.Image
{
public MyImage()
{
this.Loaded += new RoutedEventHandler(MyImage_Loaded);
}
void MyImage_Loaded(object sender, RoutedEventArgs e)
{
//mypath=#"c:\test.jpg";
var t = Task<ImageSource>.Factory.StartNew(() => GetImage(mypath));
Source = t.Result;
}
Following works but it is the UI thread:
Source = GetImage(mypath);
I tried the same with BackgroundWorker but the result is the same.
Is it possible to do it like this without MVVM?
Because you create ImageSource on different thread then the one you want to use it on you get
The calling thread cannot access this object because a different thread owns it
exception and to solve your issue you should call Freeze() on your ImageSource. However even though you load image on different thread your code blocks UI thread until it's ready:
var t = Task<ImageSource>.Factory.StartNew(() => GetImage(mypath));
Source = t.Result; //this line blocks UI thread until Result is ready
If you want to avoid that change it to:
Task.Factory.StartNew(() =>
{
var source = GetImage(mypath);
source.Freeze();
this.Dispatcher.Invoke(() => this.Source = source);
});
This should load image on different thread and update Source when it's ready without blocking UI thread
Try returning a byte[] or a stream from your GetImage (or from its result), then use Dispatcher.Invoke to call a helper on the UI thread that reconstructs the image from the byte[] or stream and updates MyImage's Source.
This is my working POC. You want to examine it for proper disposal of the stream and so on.
(Note: disp is the Dispatcher to "post" to the UI)
Task.Factory.StartNew(() =>
{
var result = GetImage();
var bitmap = new BitmapImage();
var stream = result;
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
bitmap.Freeze();
stream.Dispose();
disp.Invoke(new Action(()=> MyImage.Source = bitmap));
});
And the GetImage():
private MemoryStream GetImage()
{
System.Threading.Thread.Sleep(35000);
var filePath = #"C:\temp\2013-01-08-235341.jpg";
MemoryStream memoryStream = null;
if (File.Exists(filePath))
{
memoryStream = new MemoryStream();
byte[] fileBytes = File.ReadAllBytes(filePath);
memoryStream.Write(fileBytes, 0, fileBytes.Length);
memoryStream.Position = 0;
}
return memoryStream;
}
I am trying to update an image by setting the source property every second, this works however causes a flicker when updated.
CurrentAlbumArt = new BitmapImage();
CurrentAlbumArt.BeginInit();
CurrentAlbumArt.UriSource = new Uri((currentDevice as AUDIO).AlbumArt);
CurrentAlbumArt.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
CurrentAlbumArt.EndInit();
If I don't set IgnoreImageCache, the image does not update thus no flickering either.
Is there a way around this caveat?
Cheers.
The following code snippet downloads the whole image buffer before setting the Image's Source property to a new BitmapImage. This should eliminate any flicker.
var webClient = new WebClient();
var url = ((currentDevice as AUDIO).AlbumArt;
var bitmap = new BitmapImage();
using (var stream = new MemoryStream(webClient.DownloadData(url)))
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
image.Source = bitmap;
If the download takes some time, it would make sense to run it in a separate thread. You would then have to take care for proper cross-thread access by also calling Freeze on the BitmapImage and assigning Source in the Dispatcher.
var bitmap = new BitmapImage();
using (var stream = new MemoryStream(webClient.DownloadData(url)))
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
bitmap.Freeze();
image.Dispatcher.Invoke((Action)(() => image.Source = bitmap));
I would like to display an image file on the UI from the Assets. I managed to store the item as a StorageFile. How can I display it? I've tried to display it in a XAML <Image> tag's Source. Is it possible to covert StorageFile to Image?
string path = #"Assets\mypicture.png";
StorageFile file = await InstallationFolder.GetFileAsync(path);
Try this function
public async Task<Image> GetImageAsync(StorageFile storageFile)
{
BitmapImage bitmapImage = new BitmapImage();
FileRandomAccessStream stream = (FileRandomAccessStream)await storageFile.OpenAsync(FileAccessMode.Read);
bitmapImage.SetSource(stream);
Image image = new Image();
image.Source = bitmapImage;
return image;
}
Try the following:
public async Task<BitmapImage> GetBitmapAsync(StorageFile storageFile)
{
BitmapImage bitmap = new BitmapImage();
IAsyncOperation<IRandomAccessStream> read = storageFile.OpenReadAsync();
IRandomAccessStream stream = await read;
bitmap.SetSource(stream);
return bitmap;
}
Call the function this way:
Image image = new Image();
image.Source = await GetBitmapAsync (file);
image.Source = new BitmapImage(new Uri("file://"+ storageFile.Path))
I'm developing a simple application in Windows 8 metro applications and I m trying to retrieve files from PicturesLibrary, the code I put is the following :
public async void Initialize()
{
IReadOnlyList<StorageFile> storageFiles = await KnownFolders.PicturesLibrary.GetFilesAsync();
foreach (var storageFile in storageFiles)
{
BitmapImage bitmapImage = new BitmapImage();
FileRandomAccessStream stream = (FileRandomAccessStream)await storageFile.OpenAsync(FileAccessMode.Read);
bitmapImage.SetSource(stream);
Image image = new Image();
image.Source = bitmapImage;
Images.Add(image);
}
}
then I show these images using their ImageSource.
The problem that I am meeting is that sometimes it shows them all, somethimes one
or two , sometimes it deosn't show any image, I don't understand if this is because of the awaitable method GetFileAsync() or other things I may be missing.
Thanks in advance :)
I would guess it's just a timing issue, but doing a breakpoint or trace point in the foreach would tell for sure.
Try changing this to return Task and then await it in the caller of you can
I think your problem will be this line.
FileRandomAccessStream stream = (FileRandomAccessStream)await storageFile.OpenAsync(FileAccessMode.Read);
Awaiting inside a loop is likely going to give you some odd scope issues.
The first thing I would try is switching the first two lines of this loop around.
FileRandomAccessStream stream = (FileRandomAccessStream)await storageFile.OpenAsync(FileAccessMode.Read);
BitmapImage bitmapImage = new BitmapImage();
It could be that the bitmapImage reference is being repointed on you. If this doesn't help though, you might need to look at the .ContinueWith method.
storageFile.OpenAsync(FileAccessMode.Read).ContinueWith(task => {
FileRandomAccessStream stream = (FileRandomAccessStream)task.Result;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
Image image = new Image();
image.Source = bitmapImage;
Images.Add(image);
});
Ok, I found the solution for this thanks to you guys , I had to rearrange the code little bit,
public async Task Initialize()
{
IReadOnlyList<StorageFile> storageFiles = await KnownFolders.PicturesLibrary.GetFilesAsync();
foreach (var storageFile in storageFiles)
{
var image = new Image();
image.Source = await GetBitmapImageAsync(storageFile);
Images.Add(image);
}
}
public async Task<BitmapImage> GetBitmapImageAsync(StorageFile storageFile)
{
BitmapImage bitmapImage = new BitmapImage();
IAsyncOperation<IRandomAccessStream> operation = storageFile.OpenAsync(FileAccessMode.Read);
IRandomAccessStream stream = await operation;
bitmapImage.SetSource(stream);
return bitmapImage;
}
then call all that in the OnNavigateTo event since the constructor can't be async
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
_imageList = new ImagesList();
await _imageList.Initialize();
foreach (var image in _imageList.Images)
{
Image img = new Image() { Source = image.Source };
img.Width = img.Height = 200;
img.Margin = new Thickness(20, 0, 20, 0);
img.ImageFailed += image_ImageFailed;
img.PointerEntered += img_PointerEntered;
img.PointerExited += img_PointerExited;
sp_MainStackPanel.Children.Add(img);
}
}
thanks again guys !
In my app i need to open a local image for cropping. I tried this by setting the path of the image as source to a BitmapImage but it raising an error. How i can read and assign that local image to BitmapImage in WP Mango.
private WriteableBitmap ReadLocalImage(string Uri)
{
StreamResourceInfo sri = null;
Uri uri = new Uri(Uri, UriKind.Relative);
sri = Application.GetResourceStream(uri);
BitmapImage bitmap = new BitmapImage();
bitmap.CreateOptions = BitmapCreateOptions.None;
bitmap.SetSource(sri.Stream);
WriteableBitmap wb = new WriteableBitmap(bitmap);
return wb;
}
use this method for reading local image.