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;
}
Related
The calling thread cannot access this object because a different thread owns it even after adding Dispatcher.Invoke.
The problem is still intact even after adding Dispatcher.Invoke.
async Task capturePredict()
{
await Dispatcher.Invoke( async () =>
{
PngBitmapEncoder image = new PngBitmapEncoder();
image.Frames.Add(BitmapFrame.Create(bitmap));
using (Stream stream = File.Create(#"E:\ImageClassificationTraining\image.png"))
{
await Task.Run(() => image.Save(stream));
}
});
}
In contrast to decoding a BitmapSource (which can be frozen to make it cross-thread accessible), encoding can seemingly not be done in a thread other than the UI thread.
You may however separate the encoding step from writing the file, by something like this:
public async Task SaveImageAsync(BitmapSource bitmap, string path)
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var memoryStream = new MemoryStream())
{
encoder.Save(memoryStream);
memoryStream.Position = 0;
using (var fileStream = File.Create(path))
{
await memoryStream.CopyToAsync(fileStream);
}
}
}
I am trying to save a file(BitmapImage) to a certain location, but as soon as I use async & await I get an error saying the file is in use:
The process cannot access the file 'C:\Users\ ... \image1.jpg' because
it is being used by another process.
My coding:
BitmapImage image = new BitmapImage(new Uri(oldImagePath));
var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
encoder.Frames.Add(BitmapFrame.Create(image));
using (var filestream = new FileStream(GetImageLocation(), FileMode.Create))
await Task.Run(() => encoder.Save(filestream)); //Error here
When I use the code without the await, the code works perfectly fine. I think it might be because another thread might be using it, but can anyone help or explain to me a work around for my issue? Thank you.
In your case when you use Task with await another thread is used to save your encoder. But your encoder is also used by your main thread so new thread can't use it.
Change your code a little:
await Task.Run(() =>
{
using (var filestream = new FileStream(GetImageLocation(), FileMode.Create))
{
BitmapImage image = new BitmapImage(new Uri(oldImagePath));
var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(filestream);
}
}
Now you create and save your encoder in the same task and it will use only one thread.
I think you should move the code inside the Task.Run, because it gets called inside another thread.
You could encode to a MemoryStream, get the byte array, use WriteAsync on the FileStream, and avoid using Task.Run altogether.
BitmapImage image = new BitmapImage(new Uri(oldImagePath));
var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
encoder.Frames.Add(BitmapFrame.Create(image));
using (var mem = new MemoryStream())
using (var filestream = new FileStream(GetImageLocation(), FileMode.Create))
{
encoder.Save(mem);
var data = mem.ToArray();
await filestream.WriteAsync(date, 0, data.Length);
}
Note that this will block your thread during the encoding and will use more memory.
In my application, I allow users to add people from contact using the ContactPicker.
I try to convert IRandomAccessStreamWithContentType to Byte[]
IRandomAccessStreamWithContentType stream = await contactInformation.GetThumbnailAsync();
if (stream != null && stream.Size > 0)
{
Byte[] bytes = new Byte[stream.Size];
await stream.ReadAsync(bytes.AsBuffer(), (uint)stream.Size, Windows.Storage.Streams.InputStreamOptions.None);
My Byte[] is not empty (approximately 10000 bytes)
But when I use my Converter Byte[] to ImageSource, the BitmapImage has 0 for width and heigth.
I use this converter for another application and it work great...
public object Convert(object value, Type targetType, object parameter, string language)
{
try
{
Byte[] bytes = (Byte[])value;
if (bytes == null)
return (new BitmapImage(new Uri((String)parameter)));
BitmapImage bitmapImage = new BitmapImage();
IRandomAccessStream stream = this.ConvertToRandomAccessStream(new MemoryStream(bytes));
bitmapImage.SetSource(stream);
return (bitmapImage);
}
catch
{
return (new BitmapImage(new Uri((String)parameter)));
}
}
private IRandomAccessStream ConvertToRandomAccessStream(MemoryStream memoryStream)
{
var randomAccessStream = new InMemoryRandomAccessStream();
var outputStream = randomAccessStream.GetOutputStreamAt(0);
outputStream.AsStreamForWrite().Write(memoryStream.ToArray(), 0, (Int32)memoryStream.Length);
return randomAccessStream;
}
If anybody know what is the problem...
Thanks in advance.
NeoKript
Edit : I already use my converter with another project and it works great.
The main difference is that the stream has not the same origine:
var reader = await file.OpenReadAsync();
using (DataReader dataReader = new DataReader(reader))
{
var bytes = new byte[reader.Size];
await dataReader.LoadAsync((uint)reader.Size);
dataReader.ReadBytes(bytes);
// Use of bytes
}
I'm pretty sure bytes in your case contains only zeros. You should modify your reading code:
var buffer = await stream.ReadAsync(bytes.AsBuffer(), (uint)stream.Size, InputStreamOptions.None);
bytes = buffer.ToArray();
Or even better, avoid IBuffer alltogether:
await stream.AsStream().ReadAsync(bytes, 0, bytes.Length);
In both cases the byte array will now contain actual values. I still couldn't get your converter to work and I don't have the time to troubleshoot it further now. Maybe somebody else will help you with that.
EDIT:
I'm not really sure you're using the converter in another project but I couldn't get it to work without using async methods which makes it impossible to use inside IValueConverter. After I changed ConvertToRandomAccessStream the bitmap was created as expected without any other modifications (except for the required await):
private async Task<IRandomAccessStream> ConvertToRandomAccessStream(byte[] bytes)
{
var randomAccessStream = new InMemoryRandomAccessStream();
using (var writer = new DataWriter(randomAccessStream))
{
writer.WriteBytes(bytes);
await writer.StoreAsync();
await writer.FlushAsync();
writer.DetachStream();
}
randomAccessStream.Seek(0);
return randomAccessStream;
}
Instead of using a converter I put an additional property in my view model returning the bitmap. Inside the bytes property I called the async method for creating the bitmap:
public Byte[] ImageBytes
{
get { return _imageBytes; }
set
{
if (Equals(value, _imageBytes)) return;
_imageBytes = value;
OnPropertyChanged();
CreateBitmap();
}
}
public BitmapSource Image
{
get { return _image; }
set
{
if (Equals(value, _image)) return;
_image = value;
OnPropertyChanged();
}
}
private async Task CreateBitmap()
{
try
{
BitmapImage bitmapImage = new BitmapImage();
IRandomAccessStream stream = await this.ConvertToRandomAccessStream(ImageBytes);
bitmapImage.SetSource(stream);
Image = bitmapImage;
}
catch
{
Image = null;
}
}
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;
}
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 !