C# WPF binding bitmapImage to Image control [duplicate] - c#

I'm trying to load a image asynchronously.
MainWindow code
public partial class MainWindow : Window
{
private Data data = new Data();
public MainWindow()
{
InitializeComponent();
this.DataContext = data;
}
private async void button_Click(object sender, RoutedEventArgs e)
{
data.Image = await Data.GetNewImageAsync();
}
}
Data class
public class Data : INotifyPropertyChanged
{
private BitmapImage _Image = new BitmapImage();
public BitmapImage Image { get { return _Image; } set { _Image = value; OnPropertyChanged("Image"); } }
public static BitmapImage GetNewImage()
{
return new BitmapImage(new Uri("http://www.diseno-art.com/news_content/wp-content/uploads/2012/09/2013-Jaguar-F-Type-1.jpg"));
}
public async static Task<BitmapImage> GetNewImageAsync()
{
return await Task.Run(() => GetNewImage());
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
WPF code
<Button Name="button" Click="button_Click">Image</Button>
<Image Grid.Row="1" Source="{Binding Path=Image, UpdateSourceTrigger=PropertyChanged}"></Image>
Problem
I get the exception:
System.ArgumentException: "Must create DependencySource on same Thread
as the DependencyObject."
... in this row:
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
But if i change BitmapImage to string this code works fine.
What am I doing wrong?

When a BitmapImage is created in a background thread, you have to make sure that it gets frozen before it is used in the UI thread.
You would have to load it yourself like this:
public static async Task<BitmapImage> GetNewImageAsync(Uri uri)
{
BitmapImage bitmap = null;
var httpClient = new HttpClient();
using (var response = await httpClient.GetAsync(uri))
{
if (response.IsSuccessStatusCode)
{
using (var stream = new MemoryStream())
{
await response.Content.CopyToAsync(stream);
stream.Seek(0, SeekOrigin.Begin);
bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
bitmap.Freeze();
}
}
}
return bitmap;
}
Or shorter with BitmapFrame.Create, which returns an already frozen BitmapSource:
public static async Task<BitmapSource> GetNewImageAsync(Uri uri)
{
BitmapSource bitmap = null;
var httpClient = new HttpClient();
using (var response = await httpClient.GetAsync(uri))
{
if (response.IsSuccessStatusCode)
{
using (var stream = new MemoryStream())
{
await response.Content.CopyToAsync(stream);
stream.Seek(0, SeekOrigin.Begin);
bitmap = BitmapFrame.Create(
stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}
}
return bitmap;
}
Note that the second method requires to change the type of your Image property to BitmapSource (or even better, ImageSource), which would provide greater flexibility anyway.
An alternative method without any manual download might look like shown below. It also does not require to freeze the BitmatImage, because it is not created in a Task thread.
public static Task<BitmapSource> GetNewImageAsync(Uri uri)
{
var tcs = new TaskCompletionSource<BitmapSource>();
var bitmap = new BitmapImage(uri);
if (bitmap.IsDownloading)
{
bitmap.DownloadCompleted += (s, e) => tcs.SetResult(bitmap);
bitmap.DownloadFailed += (s, e) => tcs.SetException(e.ErrorException);
}
else
{
tcs.SetResult(bitmap);
}
return tcs.Task;
}

Related

Serial Port Communication WPF MVVM [duplicate]

The following is part of my View in which I have bound an Image to a property in my ViewModel:
<Image Source="{Binding Image}" Grid.Column="2" Grid.ColumnSpan="2"/>
My ViewModel is this:
public class MainWindowViewModel : INotifyPropertyChanged
{
public BitmapImage Image
{
get { return _image; }
set
{
_image = value;
OnPropertyChanged();
}
}
Action _makeScannerAlwaysOnAction;
private BitmapImage _image;
public MainWindowViewModel()
{
AddNewPersonCommand = new RelayCommand(OpenFrmAddNewPerson);
FingerPrintScannerDevice.FingerPrintScanner.Init();
MakeScannerAlwaysOn(null);
}
private void MakeScannerAlwaysOn(object obj)
{
_makeScannerAlwaysOnAction = MakeScannerOn;
_makeScannerAlwaysOnAction.BeginInvoke(Callback, null);
}
private void Callback(IAsyncResult ar)
{
FingerPrintScannerDevice.FingerPrintScanner.UnInit();
var objFingerPrintVerifier = new FingerPrintVerifier();
objFingerPrintVerifier.StartVerifingProcess();
var ms = new MemoryStream();
ms.Position = 0;
objFingerPrintVerifier.MatchPerson.Picture.Save(ms, ImageFormat.Png);
var bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
Thread.Sleep(2000);
Dispatcher.CurrentDispatcher.Invoke(() => Image = bi);
//Image = bi;
_makeScannerAlwaysOnAction.BeginInvoke(Callback, null);
}
private void MakeScannerOn()
{
while (true)
{
if (FingerPrintScannerDevice.FingerPrintScanner.ScannerManager.Scanners[0].IsFingerOn)
{
return;
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
My Problem:
The problem is when I want to bind the Image it gives me the error
Must create DependencySource on same Thread as the DependencyObject
I have googled a lot and I have seen the post in SO but neither of them worked for me.
any kind of help would be very appreciated.
BitmapImage is DependencyObject so it does matter on which thread it has been created because you cannot access DependencyProperty of an object created on another thread unless it's a Freezable object and you can Freeze it.
Makes the current object unmodifiable and sets its IsFrozen property to true.
What you need to do is call Freeze before you update Image:
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
bi.Freeze();
Dispatcher.CurrentDispatcher.Invoke(() => Image = bi);
as pointed out by #AwkwardCoder here is Freezable Objects Overview
While bi.Freeze(); worked for me in one case, I've seen no difference from adding/removing
Dispatcher.CurrentDispatcher.Invoke(() => Image = bi);
The second time I used DataTemplate in xaml and all the same classes as in the first case, but I kept getting the same Error.
This was a thing that helped:
Application.Current.Dispatcher.Invoke(() => Image = bi);
Maybe accepted answer can be improved because Dispatcher.CurrentDispatcher don't actually give you UI thread.

filling an ImageSource with a selector

i'm trying to fill up an image from a selector based on this https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/dependency-service/photo-picker
the selector works fine and the stream fills up. however i do not get the picture on my screen.
public Command SelectPictureCommand { get; }
public ImageSource ItemPic { get; set; }
SelectPictureCommand = new Command(execute: async () =>
{
if (IsBusy)
{
return;
}
IsBusy = true;
try
{
Stream stream = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
if (stream != null)
{
ItemPic = ImageSource.FromStream(() => stream);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
});
xaml
<StackLayout Orientation="Vertical" HorizontalOptions="End">
<Button Text="Select Picture" Command="{Binding SelectPictureCommand}"/>
<Image Source="{Binding ItemPic}" WidthRequest="300" HeightRequest="300"/>
</StackLayout>
You should implement the INotifyPropertyChanged interface in your ViewModel or Model:
ViewModels generally implement the INotifyPropertyChanged interface,
which means that the class fires a PropertyChanged event whenever one
of its properties changes. The data binding mechanism in Xamarin.Forms
attaches a handler to this PropertyChanged event so it can be notified
when a property changes and keep the target updated with the new
value.
public class myViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
public Command SelectPictureCommand { get; }
public ImageSource itemPic { get; set; }
public ImageSource ItemPic
{
set
{
if (itemPic != value)
{
itemPic = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ItemPic"));
}
}
}
get
{
return itemPic;
}
}
public myViewModel() {
SelectPictureCommand = new Command(execute: async () =>
{
//if (IsBusy)
//{
// return;
//}
//IsBusy = true;
try
{
Stream stream = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
if (stream != null)
{
ItemPic = ImageSource.FromStream(() => stream);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
//IsBusy = false;
}
});
}
}
I uploaded a sample here and you can check. Feel free to ask me any problem if you have.
You can use CrossMedia Plugin, example:
await CrossMedia.Current.Initialize();
var file = await CrossMedia.Current.PickPhotoAsync();
var filestream = file.GetStream();
byte[] buff = ConverteStreamToByteArray(filestream);
image.Source = ImageSource.FromStream(buff);
filestream.Dispose();

Image edit and save Out of Memory Exception C#

I'm working in a WPF application where I show my images in two places which means the same image gets loaded in two places. In one of the place the image will be shown along with other few images in a slider where it will be able to edit and save. If there is no image available in the location I should be showing a separate image Image not found which is not editable.
When I started working on the functionality I got the Used by another process exception during edit and save. So after searching I came up with a solution and now at a rare time I get the Out of memory exception when I click the Next or Previous or First or Last in slider. The slider is just an Image control with 4 buttons. When the buttons are clicked the below method is called. I'm not sure if there is any memory leaks.
bool NoImage = true;
private static readonly object _syncRoot = new object();
private BitmapSource LoadImage(string path)
{
lock (_syncRoot) //lock the object so it doesn't get executed more than once at a time.
{
BitmapDecoder decoder = null;
try
{
//If the image is not found in the folder, then show the image not found.
if (!File.Exists(path) && (path != null))
{
System.Drawing.Bitmap ss = XXXX.Resources.ImageNotFound;
var stream = new System.IO.MemoryStream();
if (!File.Exists(Path.GetTempPath() + "ImageNotFound.jpg"))
{
FileStream file = new FileStream(Path.GetTempPath() + "ImageNotFound.jpg", FileMode.Create, FileAccess.Write);
ss.Save(stream, ImageFormat.Jpeg);
stream.Position = 0;
stream.WriteTo(file);
file.Close();
stream.Close();
}
path = Path.Combine(Path.GetTempPath(), "ImageNotFound.jpg");
NoImage = false;
}
else
{
if (!EnableForEdit)
NoImage = false;
else
NoImage = true;
}
if (!string.IsNullOrEmpty(path) && (!NoImage || File.Exists(path)))
{
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
return decoder.Frames.FirstOrDefault();
}
else
return null;
}
catch (OutOfMemoryException ex)
{
MessageBox.Show("Insufficient memory to handle the process. Please try again later.", "Application alert");
return null;
}
catch (Exception ex)
{
// Error handling.
throw new ApplicationException(ex.Message);
}
finally
{
decoder = null;
GC.WaitForFullGCComplete(1000);
GC.Collect(0, GCCollectionMode.Forced);
}
}
}
<Image x:Name="viewImage" Grid.Row="2" Height="100" Width="135" Source="{Binding DisplayImage, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" />
If my approach is wrong, let me know where should I do the change or if there is any simpler way to do. Kindly help.
Note: The images which are loaded is above 5Mb
Firstly when ever you create a stream you need to dispose of it once you are finished with it (Note Close does not Dispose, but Dispose does close), if not then the stream stays in memory consuming resources
so your code should look as follows
using(var stream = new System.IO.MemoryStream())
{
if (!File.Exists(Path.GetTempPath() + "ImageNotFound.jpg"))
{
using(FileStream file = new FileStream(Path.GetTempPath() + "ImageNotFound.jpg", FileMode.Create, FileAccess.Write))
{
ss.Save(stream, ImageFormat.Jpeg);
stream.Position = 0;
stream.WriteTo(file);
}
}
}
Second you need to reduce your apps memory impact
to do that i would suggest leveraging the functionality already in WPF here is a quick example of how you should do this
Your Model
public class ImageItem
{
public Uri URI{ get; set; }
private BitmapSource _Source;
public BitmapSource Source
{
get
{
try
{
if (_Source == null) _Source = new BitmapImage(URI);//lazy loading
}
catch (Exception)
{
_Source = null;
}
return _Source;
}
}
public void Save(string filename)
{
var img = BitmapFrame.Create(Source);
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(img);
using(var saveStream = System.IO.File.OpenWrite(filename))
encoder.Save(saveStream)
}
}
Your View Model
public class ImageList : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<ImageItem> Images { get; } = new ObservableCollection<ImageItem>();
private int _SelectedIndex;
// c# >= 6
public static readonly PropertyChangedEventArgs SelectedIndexProperty = new PropertyChangedEventArgs(nameof(SelectedIndex));
// c# < 6
// public static readonly PropertyChangedEventArgs SelectedIndexProperty = new PropertyChangedEventArgs("SelectedIndex");
public int SelectedIndex
{
get { return _SelectedIndex; }
set
{
_SelectedIndex = value;
// c# >= 6
PropertyChanged?.Invoke(this, SelectedIndexProperty);
PropertyChanged?.Invoke(this, CurrentImageProperty);
// c# < 6
// var handler = PropertyChanged;
// if(handler !=null)
// {
// handler (this, SelectedIndexProperty);
// handler (this, CurrentImageProperty);
// }
}
}
// c# >= 6
public static readonly PropertyChangedEventArgs CurrentImageProperty = new PropertyChangedEventArgs(nameof(CurrentImage));
// c# < 6
// public static readonly PropertyChangedEventArgs CurrentImageProperty = new PropertyChangedEventArgs("CurrentImage");
public ImageItem CurrentImage => Images.Count>0 ? Images[SelectedIndex] : null;
public void Next()
{
if (SelectedIndex < Images.Count - 1)
SelectedIndex++;
else
SelectedIndex = 0;
}
public void Back()
{
if (SelectedIndex == 0)
SelectedIndex = Images.Count - 1;
else
SelectedIndex--;
}
public void Add(string Filename)
{
Images.Add(new ImageItem() { URI= new Uri(Filename) });
// c# >= 6
PropertyChanged?.Invoke(this, CurrentImageProperty);
// c# < 6
// var handler = PropertyChanged;
// if(handler !=null)
// {
// handler (this, CurrentImageProperty);
// }
}
}
and Finally your View
<Window x:Class="ImageDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ImageDemo"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<BitmapImage x:Key="NotFound" UriSource="C:\...\NotFound.png"/>
</Window.Resources>
<Window.DataContext>
<local:ImageList/>
</Window.DataContext>
<DockPanel>
<Button Content="<" Click="Back_Click"/>
<Button DockPanel.Dock="Right" Content=">" Click="Next_Click"/>
<Image Source="{Binding CurrentImage.Source, Mode=OneWay,
TargetNullValue={StaticResource NotFound},
FallbackValue={StaticResource NotFound}}"/>
</DockPanel>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
//c# >= 6
private ImageList list => DataContext as ImageList;
//c# < 6
//private ImageList list {get{ return DataContext as ImageList;}}
private void Next_Click(object sender, RoutedEventArgs e)
{
list.Next();
}
private void Back_Click(object sender, RoutedEventArgs e)
{
list.Back();
}
}
note:
because the Model is separate to the View you can show the same image in several places with no issue at all
also System.Drawing.Bitmap is not WPF compatible so you should use the WPF classes in System.Windows.Media.Imaging

I want to get the image data from a stream in Xamarin Forms using XLABS

I am trying to get the raw image data from a stream and I am not sure where to go from here. I have a viewmodel and a page where I use the function where I select a picture from a gallery using XLABS.
My viewmodel:
public ImageSource ImageSource
{
get { return _ImageSource; }
set { SetProperty (ref _ImageSource, value); }
}
private byte[] imageData;
public byte[] ImageData { get { return imageData; } }
private byte[] ReadStream(Stream input)
{
byte[] buffer = new byte[16*1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
public async Task SelectPicture()
{
Setup ();
ImageSource = null;
try
{
var mediaFile = await _Mediapicker.SelectPhotoAsync(new CameraMediaStorageOptions
{
DefaultCamera = CameraDevice.Front,
MaxPixelDimension = 400
});
VideoInfo = mediaFile.Path;
ImageSource = ImageSource.FromStream(() => mediaFile.Source);
}
catch (System.Exception ex)
{
Status = ex.Message;
}
}
private static double ConvertBytesToMegabytes(long bytes)
{
double rtn_value = (bytes / 1024f) / 1024f;
return rtn_value;
}
The page where I use the code:
MyViewModel photoGallery = null;
photoGallery = new MyViewModel ();
private async void btnPickPicture_Clicked (object sender, EventArgs e)
{
await photoGallery.SelectPicture ();
imgPicked.Source = photoGallery.ImageSource; //imgPicked is my image x:name from XAML.
}
this will initialize the ImageData property you already have defined on your ViewModel
VideoInfo = mediaFile.Path;
ImageSource = ImageSource.FromStream(() => mediaFile.Source);
// add this line
imageData = ReadStream(mediaFile.Source);

How to pass data from callback function to MainPage(another class) in Windows Phone 8

There are two class MainPage and Callback. I used C++ library, so I get byte array via CLI. When I get data callback class, I want pass the data callback class to MainPage class(It's UI thread). Finally, I want to draw Image after getting byte array.
<Image x:Name="ImageScreen" x:FieldModifier="public" Height="357" Width="456" Margin="0,33,0,201"/>
== MainPage.xaml.cs ==
public class Callback : ComponentCallback
{
static byte[] byteArray;
...
// I get data via this function. When I get the data,
// I want to pass the data to updateImage function in MainPage class.
public void onFrameLoaded(int camera, String title, UInt32 bufferSize)
{
System.Collections.Generic.List<byte> buffer = MainPage._watcher.getBuffer().ToList();
byteArray = buffer.ToArray();
// I want pass byteArray to updateImage.
}
}
public partial class MainPage : PhoneApplicationPage
{
public static Component _watcher;
public static Callback _watcherCallback;
public MainPage()
{
InitializeComponent();
CoreComponent _core = new CoreComponent();
int fenport = _core.fenServerPort();
_core.setFenServer("", );
fenport = _core.fenServerPort();
_watcher = new Component();
_watcherCallback = new Callback();
_watcher.setCallback(_watcherCallback);
_watcher.connect(0, "", 0, "", "");
}
public void updateImage(byte[] byteArray)
{
BitmapImage bi = new BitmapImage();
MemoryStream stream = new MemoryStream(byteArray);
bi.SetSource(stream);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
//Update the UI controls here
ImageScreen.Source = bi;
});
}
}
var frame = Window.Current as Frame;
if (frame == null)
{
return; //?
}
var mainPage = frame.Content as MainPage;
if (mainPage == null)
{
return; //?
}
mainPage.updateImage(byteArray);
I solved this problem. Thanks for your help Filip.
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
if (frame == null) {
return; //?
}
var mainPage = frame.Content as MainPage;
if (mainPage == null) {
return; //?
}
mainPage.updateImage(byteArray);
});

Categories

Resources