Converted BitmapImage not displaying on Page - c#

I am having a problem with my Windows Phone application. When I get the photo from a web service, I want to display it as an image on the page. The web service returns a byte[] as the image data.
Dispatcher.BeginInvoke(() =>
{
tempImage = new BitmapImage();
globalWrapper = (PhotoWrapper)JsonConvert.DeserializeObject(
response.Content, typeof(PhotoWrapper));
tempImage.SetSource(new MemoryStream(globalWrapper.PictureBinary, 0,
globalWrapper.PictureBinary.Length));
globalWrapper.ImageSource = tempImage;
PictureList.Items.Add(globalWrapper);
});
PictureList is a ListBox defined as:
<ListBox Name="PictureList" ItemsSource="{Binding}" Margin="0,0,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Click="details_Click">
<Button.Content>
<Image Source="{Binding ImageSource}"></Image>
</Button.Content>
</Button>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now, my question is, how do you receive a byte[] from a webservice as JSON and display it on the page? I feel like I am pretty close here but am missing something rather elementary.

If you're certain that the byte[] data is valid, it could be something to do with the CacheOption property of the BitmapUmage. This property controls when data is actually loaded from the stream into the bitmap. The default is OnDemand, which only loads data from the stream when the image is displayed. You might want to try the OnLoad option instead, which loads it immediately, allowing you to close the stream.
Dispatcher.BeginInvoke(() =>
{
globalWrapper = (PhotoWrapper)JsonConvert.DeserializeObject(
response.Content, typeof(PhotoWrapper));
tempImage = new BitmapImage();
tempImage.BeginInit();
tempImage.CacheOption = BitmapCacheOption.OnLoad;
tempImage.SetSource(new MemoryStream(globalWrapper.PictureBinary, 0,
globalWrapper.PictureBinary.Length));
tempImage.EndInit();
globalWrapper.ImageSource = tempImage;
PictureList.Items.Add(globalWrapper);
});

Related

Reordering ListView items in UWP messes up the content

I'm using ListViews to make a kanban list in UWP. As the picture below shows, re-ordering items a few times results in the content of one or some of them being wrong.
Further re-ordering will have the content going back and forth being correct and wrong and everything is back to normal when re-loading the page which means there's not data change but just the image control displaying the wrong image. ( It can happen with any other control too )
For reference, The images are local files which I'm loading in the Image control's Loaded event, and the ListView simply has CanReorderItems and AllowDrop set to true.
Here's how the XAML looks
<ListView x:Name="LView" MinWidth="240" Grid.Row="1" ItemsSource="{x:Bind List.Tasks}" ReorderMode="Enabled" CanReorderItems="True" AllowDrop="True" CanDragItems="True" SelectionMode="None" IsItemClickEnabled="True" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollMode="Enabled" ScrollViewer.IsVerticalRailEnabled="True" ItemClick="LView_ItemClick">
<ListView.ItemContainerStyle>
...
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="mongo:Task">
<Grid Padding="12 0" >
<Grid.RowDefinitions>
...
</Grid.RowDefinitions>
<Border CornerRadius="4 4 0 0" Margin="-12 0" >
<Image x:Name="Cover" MaxHeight="160" Stretch="UniformToFill" Tag="{x:Bind Attachments}" Loaded="Image_Loaded" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
</Border>
...
And here's the Loaded event
private async void Image_Loaded(object sender, RoutedEventArgs e)
{
var img = sender as Image;
if (img.Source is object) return;
var attachments = img.Tag as ObservableCollection<TaskAttachment>;
if (attachments is null) return;
var cover = attachments.Where(_a => _a.is_cover).FirstOrDefault();
if (cover is object && cover.type == "image")
{
var path = BrandboxSettings.Instance.server_path + "projects\\" + cover.path;
Output.WriteLine(path);
var file = await StorageFile.GetFileFromPathAsync(path);
using (IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
// Set the image source to the selected bitmap
BitmapImage bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(fileStream);
img.Source = bitmapImage;
}
}
}
Edit: It's worth noting that even if one of the cards does not initially have an image, reordering will cause it to have one.
Any help would be appreciated
Okay so I tried changing the ItemsPanel to a StackPanel and it seems to be working now.
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizationStackPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
Edit:
It also seems to work by setting the Panel to VirtualizationStackPanel

ImageFailed with GridView

I have a problem with GridView and multiple items. Each of the item has image in it, source is online image, bound to property, like this:
<GridView x:Name="gridView" Width="710" ItemTemplate="{StaticResource FirstTemplate}" AllowDrop="True" CanDragItems="True" CanReorderItems="True">
<DataTemplate x:Key="FirstTemplate">
<Grid HorizontalAlignment="Left" Width="306" Height="210">
<Border Background="White" Opacity="0.1"/>
<Image Stretch="Uniform" Width="190" Height="100" Margin="0,50,0,0" ImageFailed="ImageFailed" Source="{Binding ImagePath}"/>
</Grid>
</DataTemplate>
Image paths are like this:
www.example.com/images/1.png
www.example.com/images/2.png
www.example.com/images/3.png
and so on...
If some image not exist, for example www.example.com/images/29.png, I use the event ImageFailed, which change the source of the image to image that is located in my project (default image). Code in this event:
private void ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
var image = sender as Image;
image.Source = new BitmapImage(new Uri("ms-appx:///Images/default.png"));
}
And this is working just fine, the default image is shown in the items that don't have images. But, when I scroll down the gridview, and then return to the beginning, images are messed up. Some items that had their images, now have the default image. Again when I scroll the gridview, and then return, again random changes with images.
Is this some cache problem? What could be the problem here? Or is there any better way of setting the default image source?
The source of your problem could be virtualization, i.e. reuse of item containers. When you replace a failed image by a fallback image in your ImageFailed handler, you are effectively replacing the Binding by a fixed value, so that the item container will later always show only the fallback image.
You may instead implement the ImageFailed handler in the view model, so that replacing the image with a fallback image won't break the Binding.
Add another property, e.g. Image to your item class
public class ImageItem
{
public string ImagePath { get; set; }
private BitmapImage image;
public BitmapImage Image
{
get
{
if (image == null)
{
image = new BitmapImage();
image.ImageFailed += (s, e) =>
image.UriSource = new Uri("ms-appx:///Images/default.png");
image.UriSource = new Uri(ImagePath);
}
return image;
}
}
}
and change the Binding to this:
<Image ... Source="{Binding Image}"/> // no ImageFailed handler here

ImageFailed is called when scrolling in listbox

I have a listbox in which each item consists an image downloaded online.
<ListBox.ItemTemplate >
<DataTemplate>
<StackPanel Margin="10" >
<RelativePanel>
<Image ImageFailed="Image_ImageFailed">
<Image.Source>
<BitmapImage UriSource="{Binding IMG1}" />
</Image.Source>
private void Image_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
((Image)sender).Source = new BitmapImage(new Uri("ms-appx:///assets/StoreLogo.png"));
}
and this is how I bind the data;
data = from query in loadedData.Descendants("item") select new Models.Item
{
IMG1 = "https://example.png",
};
ItemsListBox.ItemsSource = data.Select(grp => grp.FirstOrDefault());
At first, it is working fine. However, when I start to scroll down and up again, all images get replaced by a default one as a result of ImageFailed method. So;
Why listbox tries to reload images when I scroll?
Why ImageFailed gets called even though image url is valid?
Do I have to cache images myself?
Try to change the type of the IMG1 property to ImageSource and set it like
IMG1 = new BitmapImage(new Uri("https://example.png"))
Then remove the BitmapImage from XAML and bind the Image control's Source property directly:
<Image ImageFailed="Image_ImageFailed" Source="{Binding IMG1}" />
Probably also force immediate query evaluation by calling ToList():
ItemsListBox.ItemsSource = data.Select(grp => grp.FirstOrDefault()).ToList();

WPF conversion file from base64 and assignment source - c#

I gather from json files that the user clicks on a wpf list ( c # ) .
Click to call a json pass me the file content in Base64 . I would convert it and have it displayed immediately without writing it on the user's hard .
And ' possible to convert it and leave it on the ram can then see immediately , as well as assign it to the object's source imageViewer?
Thank you
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock FontFamily="{StaticResource Lato Thin}" Margin="10,0,0,0" Foreground="#007AFF" Text="{x:Static res:strings.indietroPage}" MouseDown="GoBackFrame_MouseDown" FontSize="20" Grid.Row="0" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<Image Grid.Row="1" Source="{Binding uriImageSource}" Name="imageViewer"></Image>
</Grid>
first I was saving to disk first and then visualized , but I need that is not saved to disk , and you receive immediately as Source
File.WriteAllBytes(System.IO.Path.GetTempPath() + attachmentDownload.Name + "." + attachmentDownload.Extension, Convert.FromBase64String(attachmentDownload.B64Content));
i have try this
fileSourceBytes = Convert.FromBase64String(attachmentDownload.B64Content);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = new MemoryStream(fileSourceBytes);
bi.EndInit();
imageViewer.Source = bi;
but receive an error:
Object reference not set to an instance of an object.
solution:
codebehind
public Byte[] fileSourceBytes { get; set; }
fileSourceBytes = Convert.FromBase64String(attachmentDownload.B64Content);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.StreamSource = new MemoryStream(fileSourceBytes);
bi.EndInit();
uriImageSource = bi;
xaml:
<Image Grid.Row="1" Source="{Binding uriImageSource}" Name="imageViewer"></Image>
Maybe the best solution would be if you would just bind the BitmapImage to the ImageViewer's Source property.
ViewModel:
BitmapImage _imageViewerSource;
public BitmapImage ImageViewerSource {
get { return _imageViewerSource; }
private set
{
_imageViewerSource = value;
OnPropertyChanged("ImageViewerSource"); // or OnPropertyChanged(nameof(ImageViewerSource)); if you are using VS2015+
}
}
Xaml:
<Image Source="{Binding ImageViewerSource, Mode=OneWay}"/>
This should solve your problem issue
using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
{
using (DataWriter writer = new DataWriter(stream.GetOutputStreamAt(0)))
{
writer.WriteBytes(Convert.FromBase64String(base64););
writer.StoreAsync().GetResults();
}
BitmapImage image = new BitmapImage();
image.SetSource(stream);
}

XAML, Image control, local storage image

The first is the first, I'm very newbie on .NET, I was developed un Visual Basic 6, but now I'm trying to make an application to Windows Phone 8.0.
At this time I'm trapped with a (maybe simple) problem.
I have a xaml page with control, and it is part of LongListSelector thats implements an Observable Collection of "Prenda" class.
...
var prendasData = from r in db.Prendas select r;
PrendasItems = new ObservableCollection<Prenda>(prendasData);
llsPrendas.ItemsSource = PrendasItems;
...
The XAML portion is the code bellow, please, i know that severals things maybe are wrong, but I'm learning alone, be patient with me :D
<phone:LongListSelector x:Name="llsPrendas" Margin="0,0,-12,0" ItemsSource="{Binding Prendas}" SelectionChanged="llsPrendasSelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Width="100" Height="100" Margin="5,0,0,0" Source="{Binding PrendaImageURI}" ImageFailed="errcargaimg"/>
<StackPanel Orientation="Vertical">
<TextBlock FontWeight="Normal" Text="{Binding Nombre}" Margin="10,0,0,0" />
<TextBlock FontWeight="Normal" Text="{Binding Precio}" Margin="10,0,0,0" />
</StackPanel>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
The problem is the Image Control. It doesn't show anything and if I debug it, the error the message is: "AG_E_NETWORK_ERROR", googled this error and I know that is (in this case) same as "File not found." But I'm sure thats file exists. because I seen it with IsoStoreSpy, at /Shared/Media/ShellContent/WP_XXX.jpg, i think that the root of the Isolated Storage is called isostore:/ and the complete URI must be: isostore:/Shared/Media/ShellContent/WP_XXX.jpg.
This string is saved as string column in the class, and I create a property thats use this string to make an Uri to use to bind the Source property of Image control at design time.
(portion of class declaration)
[Column]
public string Foto
{
get
{
return foto;
}
set
{
if (foto != value)
{
foto = value;
NotifyPropertyChanged("Foto");
}
}
}
public Uri PrendaImageURI
{
get
{
return new Uri(this.Foto, UriKind.Absolute);
}
}
I'm going crazy, because I cannot understand why it's doesn't work. Can somebody help me? (Sorry for my bad english)
You can not read from Isolated storage using URI, You have to read using IsolatedStorageFile class:
private static BitmapImage GetImageFromIsolatedStorage(string imageName)
{
var bimg = new BitmapImage();
using (var iso = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var stream = iso.OpenFile(imageName, FileMode.Open, FileAccess.Read))
{
bimg.SetSource(stream);
}
}
return bimg;
}
You can find more details from below Posts:
How get image from isolated storage
How to load an image from isolated storage into image control on windows phone?
I solved it with Pratik Goyal help (thank you, very much!), making a BitmapImage Property in the class "Prendas", taking The Foto string data. Later I will take more careful with the exception control, but is a good start, I think.
public BitmapImage ImageFoto
{
get
{
return GetImageFromIsolatedStorage(Foto);
}
}
public BitmapImage GetImageFromIsolatedStorage(string imageName)
{
var bimg = new BitmapImage();
using (var iso = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var stream = iso.OpenFile(imageName, FileMode.Open, FileAccess.Read))
{
bimg.SetSource(stream);
}
}
return bimg;
}
Just for the children:
Silverlight notifies this with an AG_E_NETWORK_ERROR / HRESULT=0x80131500. Which of course can't be found anywhere on the web and of course means jack and shit to me and not only to me.
Is the UI being notified of the PrendaImageURI property changing?
Try adding
NotifyPropertyChanged("PrendaImageURI");
when your Foto property is set.

Categories