I have a ListView item which contains datas and images from a http GET request. I can display all of data in the ListView, except the picture. For getting the image I have to make a separate http GET request. I can display an image with this code:
private async void DisplayPicture()
{
var ims = new InMemoryRandomAccessStream();
var dataWriter = new DataWriter(ims);
dataWriter.WriteBytes(App.answer.picture);
await dataWriter.StoreAsync();
ims.Seek(0);
BitmapImage bitmap = new BitmapImage();
bitmap.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bitmap.SetSource(ims);
}
But this doesn't work if I would like to use in a ListView with Binding.
Here is the code what I tried:
public class BinaryToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value != null && value is byte[])
{
var bytes = value as byte[];
var ims = new InMemoryRandomAccessStream();
var dataWriter = new DataWriter(ims);
dataWriter.WriteBytes(bytes);
//await dataWriter.StoreAsync();
ims.Seek(0);
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(ims);
//var ims = new MemoryStream(bytes);
//var image = new BitmapImage();
//image.SetSource(stream);
//stream.Close();
return bitmap;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
The main problem is that I get the image in byte[] (bytearray) from the server, and only the above code can display it on WP8.1. So I have to use the dataWriter.StoreAsync() method, but if I use it, I have to use async, which must be void. But the void return value is not good for me due to the binding.
You can see the original code what I uncommented, but I cannot use it, because the input value for image.SetSource() must be a RandomAccessStream. So I don't have any idea how I can solve this problem.
If you want to make binding and use asynchronous method, then one way to make it work is to set DataContext to Task and bind to its Result. Stepen Cleary wrote a nice article about that. You will also find some useful information in his answer here.
Basing on that answer I've build a sample, which I think you can modify to fulfill your needs. Write a Converter which will return TaskCompletionNotifier (see Stephen's answer above):
public class WebPathToImage : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null) return null;
// the below class you will find in Stephen's answer mentioned above
return new TaskCompletionNotifier<BitmapImage>(GetImage((String)value));
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{ throw new NotImplementedException(); }
private async Task<BitmapImage> GetImage(string path)
{
HttpClient webCLient = new HttpClient();
var responseStream = await webCLient.GetStreamAsync(path);
var memoryStream = new MemoryStream();
await responseStream.CopyToAsync(memoryStream);
memoryStream.Position = 0;
var bitmap = new BitmapImage();
await bitmap.SetSourceAsync(memoryStream.AsRandomAccessStream());
return bitmap;
}
}
then you can define binding in XAML:
<Image DataContext="{Binding ImageFromWeb, Converter={StaticResource WebPathToImage}}" Stretch="Uniform"
Source="{Binding Result}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="2"/>
Everything should work when you set ImageFromWeb:
ImageFromWeb = #"http://www.onereason.org/wp-content/uploads/2012/02/universe-300x198.jpg";
Related
I am struggling to set the image source to the value I receive from the database.
Here are relevant parts of my XAML, its code-behind and its view model.
XAML:
<Label Text="{Binding ViewModel_Fid}" />
<Image Source="{Binding ViewModel_ImageStream}" />
Code-behind:
protected override void OnAppearing() {
base.OnAppearing();
myViewModel = new myViewModel();
myViewModel.PopulateFid();
BindingContext = myViewModel;
}
View Model:
public event PropertyChangedEventHandler PropertyChanged;
private string _fid;
public async void PopulateFid() {
_fid = await getFid();
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ViewModel_Fid)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ViewModel_ImageStream)));
public MemoryStream ViewModel_ImageStream {
get {
byte[] buffer = myGetBytes(_fid);
return null == buffer ? null : new MemoryStream(buffer);
}
}
The problem is, ViewModel_ImageStream systematically executes BEFORE PopulateFid, which means that in ViewModel_ImageStream I always get _fid = null.
I am pretty sure this is due to PopulateFid being async, but I need it to be this way, because getFid() is an external async function.
How can I enforce that PopulateFid executes before ViewModel_ImageStream is set?
Thanks!
PS. See my solution/answer below.
What ultimately worked for me was adding the Image element at runtime (i.e., not specifying it in XAML).
I add it once I've retrieved the necessary data in View Model.
So in Code-behind:
protected override void OnAppearing() {
base.OnAppearing();
myViewModel = new myViewModel();
myViewModel.PopulateFid();
BindingContext = myViewModel;
myViewModel.LoadImage = (obj) => {
var img = new Image();
img.Source = new StreamImageSource() {
Stream = (token) => getstream(token)
};
mainstack.Children.Add(img);
};
}
private async Task<Stream> getstream(object token) {
return new MemoryStream(myViewModel.myGetBytes);
}
in View Model:
private string _fid;
public async void PopulateFid() {
_fid = await getFid();
LoadImg?.Invoke(true);
}
I am working on Windows 8 store application. I am new at it.
I am receiving an image in the form of byte array (byte []).
I have to convert this back to Image and display it in Image Control.
so far I have button and Image control on Screen. When I click button, I call following function
private async Task LoadImageAsync()
{
byte[] code = //call to third party API for byte array
System.IO.MemoryStream ms = new MemoryStream(code);
var bitmapImg = new Windows.UI.Xaml.Media.Imaging.BitmapImage();
Windows.Storage.Streams.InMemoryRandomAccessStream imras = new Windows.Storage.Streams.InMemoryRandomAccessStream();
Windows.Storage.Streams.DataWriter write = new Windows.Storage.Streams.DataWriter(imras.GetOutputStreamAt(0));
write.WriteBytes(code);
await write.StoreAsync();
bitmapImg.SetSourceAsync(imras);
pictureBox1.Source = bitmapImg;
}
This is not working properly. any idea?
When I debug, I can see the byte array in ms. but it is not getting converted to bitmapImg.
I found the following on Codeproject
public class ByteImageConverter
{
public static ImageSource ByteToImage(byte[] imageData)
{
BitmapImage biImg = new BitmapImage();
MemoryStream ms = new MemoryStream(imageData);
biImg.BeginInit();
biImg.StreamSource = ms;
biImg.EndInit();
ImageSource imgSrc = biImg as ImageSource;
return imgSrc;
}
}
This code should work for you.
You can try something like that:
public object Convert(object value, Type targetType, object parameter, string language)
{
byte[] rawImage = value as byte[];
using (InMemoryRandomAccessStream ms = new InMemoryRandomAccessStream())
{
using (DataWriter writer = new DataWriter(ms.GetOutputStreamAt(0)))
{
writer.WriteBytes((byte[])rawImage);
// The GetResults here forces to wait until the operation completes
// (i.e., it is executed synchronously), so this call can block the UI.
writer.StoreAsync().GetResults();
}
BitmapImage image = new BitmapImage();
image.SetSource(ms);
return image;
}
}
I found the following answer in another thread (Image to byte[], Convert and ConvertBack). I used this solution in a Windows Phone 8.1 project, not sure about Windows Store apps, but I believe it will work.
public object Convert(object value, Type targetType, object parameter, string culture)
{
// Whatever byte[] you're trying to convert.
byte[] imageBytes = (value as FileAttachment).ContentBytes;
BitmapImage image = new BitmapImage();
InMemoryRandomAccessStream ms = new InMemoryRandomAccessStream();
ms.AsStreamForWrite().Write(imageBytes, 0, imageBytes.Length);
ms.Seek(0);
image.SetSource(ms);
ImageSource src = image;
return src;
}
I'm very very confuse about Microsoft brand-new framework, ASP.NET MVC WebAPI. I try to create complete solution for cross-site API with JSONP data.
First, I modify their default WebApiConfig to the following code.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}", new {id = RouteParameter.Optional});
// Custom customization
config.Formatters.Clear();
config.Formatters.Add(new JsonpFormatter());
}
}
I use jQuery to create a request to this API website.
// jQuery will create HTTP GET the following URL
// http://localhost:3557/api/FlightAvailability/SearchFlight?callback=jQuery18206342989655677229_1353568617029&origin=JFK&destination=SLC&isOneWayFlight=false&departFlightDate=Wed%2C+28+Nov+2012+17%3A00%3A00+GMT&returnFlightDate=Wed%2C+05+Dec+2012+17%3A00%3A00+GMT&numberOfGuests=1&numberOfChildren=1&numberOfInfants=1&preferredCurrency=USD&query=%7B+Origin%3A+'JFK'+%7D&flightDate=Wed%2C+28+Nov+2012+17%3A00%3A00+GMT&_=1353568618465
$.ajax
({
url: 'http://localhost:3557/api/FlightAvailability/SearchFlight',
dataType: 'jsonp',
data: $.postify(model),
success: processResponse
});
I create action to handle above request. Everything is correct. I can call to this action but WebAPI doesn't use my JSONP formatter to deserialize my query object.
However, I try to directly call ContentNegotiator to get which formatter that handle my request. It's quite surprise that negotiatorResult is my JSONP formatter.
[HttpGet]
public List<FlightInfo> SearchFlight(FlightAvailabilityQuery query)
{
var negotiator = Configuration.Services.GetContentNegotiator();
var negotiatorResult = negotiator.Negotiate(typeof (FlightAvailabilityQuery), Request, Configuration.Formatters);
var flight = new FlightsAvailability();
var result = flight.GetAvailability(WebApiAuthentication.UserInfo.SessionService, query);
return result;
}
Why WebAPI does not use my JSONP formatter to deserialize query FlightAvailabilityQuery object?
PS. I try to break all possible line in JSONP formatter but Visual Studio doesn't hit any break point by it directly go to action method without call at my only one formatter. However, when I directly call ContentNegotiator, it hit at my break point correctly.
Update #1 - Add JSONP formatter source code
public class JsonpFormatter : JsonMediaTypeFormatter
{
private readonly JsonSerializerSettings _serializerSettings;
private string _jsonpCallbackFunction;
public JsonpFormatter()
{
JsonpParameterName = "callback";
_serializerSettings = new JsonSerializerSettings();
_serializerSettings.TypeNameHandling = TypeNameHandling.Objects;
_serializerSettings.Converters.Add(new IsoDateTimeConverter());
MediaTypeMappings.Add(new ExtendedQueryStringMapping(JsonpParameterName, "application/json"));
}
public string JsonpParameterName { get; set; }
public override bool CanReadType(Type type)
{
return true;
}
public override bool CanWriteType(Type type)
{
return true;
}
public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType)
{
var formatter = new JsonpFormatter()
{
_jsonpCallbackFunction = GetJsonCallbackFunction(request)
};
// this doesn't work unfortunately
//formatter.SerializerSettings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
formatter.SerializerSettings.Converters.Add(new StringEnumConverter());
formatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
formatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
return formatter;
}
public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger)
{
// Create a serializer
var serializer = JsonSerializer.Create(_serializerSettings);
// Create task reading the content
return Task.Factory.StartNew(() =>
{
using (var streamReader = new StreamReader(stream, Encoding.UTF8))
{
using (var jsonTextReader = new JsonTextReader(streamReader))
{
return serializer.Deserialize(jsonTextReader, type);
}
}
});
}
public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
{
if (string.IsNullOrEmpty(_jsonpCallbackFunction))
return base.WriteToStreamAsync(type, value, stream, content, transportContext);
StreamWriter writer = null;
// write the pre-amble
try
{
writer = new StreamWriter(stream);
writer.Write(_jsonpCallbackFunction + "(");
writer.Flush();
}
catch (Exception ex)
{
try
{
if (writer != null)
writer.Dispose();
}
catch { }
var tcs = new TaskCompletionSource<object>();
tcs.SetException(ex);
return tcs.Task;
}
return base.WriteToStreamAsync(type, value, stream, content, transportContext)
.ContinueWith(innerTask =>
{
if (innerTask.Status == TaskStatus.RanToCompletion)
{
writer.Write(")");
writer.Flush();
}
}, TaskContinuationOptions.ExecuteSynchronously)
.ContinueWith(innerTask =>
{
writer.Dispose();
return innerTask;
}, TaskContinuationOptions.ExecuteSynchronously)
.Unwrap();
}
private string GetJsonCallbackFunction(HttpRequestMessage request)
{
if (request.Method != HttpMethod.Get)
return null;
var query = HttpUtility.ParseQueryString(request.RequestUri.Query);
var queryVal = query[this.JsonpParameterName];
if (string.IsNullOrEmpty(queryVal))
return null;
return queryVal;
}
}
Your action does not get hit because it cannot model bind your query parameter. Also JsonP is for HTTP GET only, so your formatter will not be selected for deserialization. How do you expect your FlightAvailabilityQuery being deserialized? I saw a lot of query parameters from your URL, do you want that be turned into FlightAvailabilityQuery?
The easiest way to get that is to use FromUri.
public List<FlightInfo> SearchFlight([FromUri]FlightAvailabilityQuery query)
If for some reason that does not work, you can try to add individual query parameter name on the action, such as origin, isOneWay, destination. etc. Then inside your action construct the FlightAvailabilityQuery object.
Also, if you have a lot of actions that you want to reuse this model binding logic, you can register a custom parameter binding to solve that. Please see this link for how to register a custom parameter binding to solve this.
Hope this helps!
I have been using the sql ce to bring some data onto my application. Now I need to add some of the images to make it look pretty. What all I want to know is
There must be some image to byte conversion done,
Retrieve the image byte code and convert back into the image.
I've got stuck at the second part and how am I supposed to continue?
Any links or examples are needed for the reference.
Thanks a lot.
Here's some ideas I have used in the past.
The image column in the DB:
[Column]
public byte[] MyImage
{
get { return _myImage; }
set
{
if (_myImage != value)
{
_myImage = value;
NotifyPropertyChanging("MyImage");
NotifyPropertyChanged("MyImage");
}
}
}
Save image:
public void AddNewImage(Stream image, string url)
{
byte[] byteArray = GetImageBytes(image);
var item = new MyDatabaseItem { Count = 1, ItemImageUrl = url, MyImage = byteArray };
MyDatabaseItemModel.Add(item);
MyDatabaseDB.MyDatabaseItems.InsertOnSubmit(item);
MyDatabaseDB.SubmitChanges();
}
Get image:
private byte[] GetImageBytes(Stream stream)
{
using (var ms = new MemoryStream())
{
var writeableBitmap = PictureDecoder.DecodeJpeg(stream, 200, 200);
writeableBitmap .SaveJpeg(ms, 200, 200, 0, 30);
return ms.ToArray();
}
}
Using a value converter:
public class ImageConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is byte[])
{
var memoryStream = new MemoryStream(value as byte[]);
varwriteBitmap = PictureDecoder.DecodeJpeg(memoryStream, 200, 200);
return writeBitmap;
}
else
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And finally bind it in XAML:
<Image Source="{Binding MyImage, Converter={StaticResource ImageConverter}}" Stretch="UniformToFill"/>
I am doing an application for windows phone 7.
the application is access a image from database(Sql server 2008).
the data is stored in data type 'image'.I want to Display the image.
i use the following code
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
byte[] data;
BitmapImage empImage = new BitmapImage();
Stream mm;
data = (byte[])value;
mm = new MemoryStream(data);
mm.Position = 0;
BinaryReader BR = new BinaryReader(mm);
byte[] image=BR.ReadBytes(data.Length);
mm = new MemoryStream(image);
//empImage.SetSource(mm);
return empImage;
}
But there is a 'Unspecified' error at commented line (empImage.SetSource(mm);).
Please help Me......
BitmapImage.SetSource accepts a Stream (you can leave out the CreateOptions if you don't need to access the bytes immediately afterwards):
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
byte[] data = (byte[])value;
using (MemoryStream stream = new MemoryStream(data))
{
BitmapImage image = new BitmapImage
{
CreateOptions = BitmapCreateOptions.None
};
image.SetSource(stream);
return image;
}
}
Also, I don't think an IValueConverter is the right place for this sort of code.
And finally, the image database type has been deprecated in favour of varbinary(MAX)