How do I bind to an image from an api endpoint? - c#

Currently, I have an app where I want to display a list of image thumbnails on my layout.
I'm able to get the json response from the api endpoint and deserialize it. Now, I have an image object and in that object, I have an image preview url (the thumbnail image). My question is how do I display a list of thumbnail images in my layout?
Here's the method that gets called to display images and some property setup:
private List<string> images;
public List<string> Images
{
get { return images; }
set { SetProperty(ref images, value); }
}
private async Task DisplayImages()
{
var imageObj = await _mediaService.GetCatImages();
//imageObj.Hits[i].PreviewUrl; <-- how to a reference to the previewurl but what property should it bind to?
}
Here's my layout at the moment:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:flv="clr-namespace:DLToolkit.Forms.Controls;assembly=DLToolkit.Forms.Controls.FlowListView"
x:Class="MediaViewer.Views.ContentFolderMedia">
<flv:FlowListView FlowColumnCount="3" SeparatorVisibility="None" HasUnevenRows="false"
FlowItemTappedCommand="{Binding ItemTappedCommand}" FlowLastTappedItem="{Binding LastTappedItem}"
FlowItemsSource="{Binding Images}" >
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
<Label HorizontalOptions="Fill" VerticalOptions="Fill"
XAlign="Center" YAlign="Center"/>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
</ContentPage>
Where the layout should look like an image gallery (hence why I'm using this third party library: https://github.com/daniel-luberda/DLToolkit.Forms.Controls)
So, this line: FlowItemsSource="{Binding Images} should be where the binding occurs but I'm not sure to properly set the property so that it binds to the preview url and displays the image. It also makes me think... usually an image source is a name of a local image but if I'm hitting a url to see an image, do I need to do any conversion in my app to display the image from a url?

What is the structure of the list returned by your service? Let's say it is List<ImageObj>.
First you need to change your Images type:
private List<ImageObj> images;
public List<ImageObj> Images
{
get { return images; }
private set { SetProperty(ref images, value); }
}
private async Task DisplayImages()
{
Images = await _mediaService.GetCatImages()
.Select(x => x.Hit)
.ToList();
}
Then you've correctly bound your list to your Listview.
Now within the DataTemplate you need to add an Image bound to the url:
<DataTemplate>
<Image Source="{Binding PreviewUrl}" />
</DatatTemplate>

You can use FFImageLoading with a FlowListView
This will give you the ability to load the image from a url, caching, and fast loading.
Just add it to your DataTemplate and bind via the CachedImage Source property
Source="{Binding url}"

Related

WPF Image won't update programmatically

I have an application where I want it to load an image when a command is invoked. But the problem is that nothing loads and nothing breaks either. I just dont see my image. I also made sure that I was setting the data context to my view model.
XAML:
<Image Grid.Column="3" Source="{Binding Path=LoadingImage, Mode=TwoWay}" Width="35" Height="35"/>
ViewModel:
private Image _loadingImage = new Image();
public Image LoadingImage
{
get => _loadingImage;
set
{
_loadingImage = value;
RaisePropertyChanged(nameof(LoadingImage));
}
}
//Method called by the command... i debugged it and it gets here just fine
private void GetDirectories()
{
FolderBrowserDialog folderBrowseDialog = new FolderBrowserDialog();
DialogResult result = folderBrowseDialog.ShowDialog();
if (result == DialogResult.OK)
{
//This is how I am getting the image file
LoadingImage.Source = new BitmapImage(new Uri("pack://application:,,,/FOONamespace;component/Resources/spinner_small.png"));
//More code below
}
}
Some other settings, my .png file has the following properties:
Build Action: Resource
Copy to Output Directory: Copy if newer
This is head scratcher for me. What am I doing wrong? Many thanks.
You can't use an Image element as the value of the Source property of another Image element.
Change the property type to ImageSource:
private ImageSource _loadingImage;
public ImageSource LoadingImage
{
get => _loadingImage;
set
{
_loadingImage = value;
RaisePropertyChanged(nameof(LoadingImage));
}
}
and assign the property like this:
LoadingImage = new BitmapImage(
new Uri("pack://application:,,,/FOONamespace;component/Resources/spinner_small.png"));
Besides that, setting the Binding's Mode to TwoWay is pointless
<Image Source="{Binding LoadingImage}" />
and copying to the output directory is also unnecessary, because the Build Action Resource makes the image file an assembly resource that is compiled into the assembly.

Binding image source dynamically on xamarin forms

my question is, could I Binding string image to image source ? I have multiple image and the image will change on if condition. So:
Xaml on Xamarin forms:
<Image Source="{Binding someImage}" Aspect="Fill" Grid.Row="2"></Image>
Codebehind c#
public String someImage;
public String SomeImage
{
set
{
if (someImage != value)
{
someImage = value;
}
}
get
{
return someImage;
}
}
InitializeComponent part:
if(true)
{
someImage = "backgroundListViewGren.png";
}
else
{
someImage = "backgroundListViewRed.png";
}
the image is in "Images" folder on portable project
but this, doesn't work, maybe i wront something but I don't understand.
Any solutions ?
I've tried with ImageSource and not string, but don't work too.
I'll post this answer here because it's the one I was looking for and I suspect most others are (and Google currently directs you here, which currently isn't super helpful).
How to bind an image using XAML:
XAML:
<Image>
<Image.Source>
<FileImageSource File="{Binding SomeImage}" />
</Image.Source>
</Image>
Then in the ViewModel, C#:
public string SomeImage
{
get { return string.Format("prefix-{0}-suffix.png", OtherProperty); }
}
Newer, better, but essentially equivalent c# you can use instead of the above:
public string SomeImage => $"prefix-{OtherProperty}-suffix.png";
This is certainly the easiest way to set it up, IMO :).
EDIT: It should go without saying, but the image should obviously be in the Resources folder of the project for each platform.
EDIT2, much later: In case it's not obvious, "prefix", "suffix" are just random strings, SomeImage just has to return the path of your image. SomeImage and OtherProperty are members of your view model class, OtherProperty is just something you're basing your image name on (because if you know the whole name in advance, you don't need this question).
You said:
the image is in "Images" folder on portable project
Each platform have a different approach for resources and images. Xamarin handles that in every platform for example Xamarin.iOS has embedded resource while Xamarin.Android uses Android resource for images.
You should place your images in every project for Xamarin.Forms to know where to look for them in each platform.
For more information look at this.
For using images from the PCL, check out this part in the Xamarin documentation.
You should make sure the Build action for each image is set to Embedded Resource.
Then, if you want to use them in XAML, specify a MarkupExtension
[ContentProperty ("Source")]
public class ImageResourceExtension : IMarkupExtension
{
public string Source { get; set; }
public object ProvideValue (IServiceProvider serviceProvider)
{
if (Source == null)
{
return null;
}
// Do your translation lookup here, using whatever method you require
var imageSource = ImageSource.FromResource(Source);
return imageSource;
}
}
You should then be able to use your images like this.
<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:WorkingWithImages;assembly=WorkingWithImages"
x:Class="WorkingWithImages.EmbeddedImagesXaml">
<StackLayout VerticalOptions="Center" HorizontalOptions="Center">
<!-- use a custom Markup Extension -->
<Image Source="{local:ImageResource WorkingWithImages.beach.jpg}" />
</StackLayout>
</ContentPage>
Like mentioned in the comments, if you want this to work out of the box place them in the Resources folder of your respective platform project. This way you can also leverage the auto sizing mechanisms that are in place. To read more on this method see this page.
By adding them with the right naming convention in the right folder, you can simply refer to the filename and Xamarin.Forms will translate that to the right format for the platform.
Another way is to use a ValueConverter. This is suitable for a pre-defined set of images, although it is not the most optimal code size compared to the Maverick solution.
Add to resources:
xmlns:conv="clr-namespace:MyProject.Converters"
<ContentPage.Resources>
<col:ArrayList x:Key="States">
<x:String>first.png</x:String>
<x:String>second.png</x:String>
</col:ArrayList>
<conv:IntToImgConverter x:Key="IntToImgConverter" />
</ContentPage.Resources>
Link the ImageSource to the converter:
<Image>
<Image.Source>
<FileImageSource File="{Binding Path=State,
Converter={StaticResource IntToImgConverter},
ConverterParameter={StaticResource States}}"/>
</Image.Source>
</Image>
State is an integer property of your viewmodel. It's the ordinal number of the image in the array. And finally, the converter itself:
public class IntToImgConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is int intVal && parameter is ArrayList strVal && strVal.Count > intVal)
return strVal[intVal];
throw new ArgumentException();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
You can do it a bit simpler I think. This is working on mine.
Put the image in the appropriate place on each platform eg. Resources/drawable on Android
In your Xaml:
<Image Source="{Binding someImage}" Aspect="Fill" Grid.Row="2"></Image>
In your View Model:
private string _someImage = "icon_eye_hidden";
public string someImage
{
get => this._someImage;
set
{
if (this._someImage == value) return;
this._someImage = value;
this.RaisePropertyChanged(() => this.someImage);
}
}
Then just set someImage to the name of the image (without the extension) and it should update.
I think this is not the fastest thing, but I found a way to only include the image resource once, in the shared project.
This also has the advantage that it doesn't matter where the file is located in your project, which is good for me since I tend to move things around fairly frequently.
It's a class called EmbeddedSourceror. It basically just finds and returns the resource as needed:
public class EmbeddedSourceror
{
//NOTE: if there are resources *anywhere* with identical names, this will only return the *first* one it finds, so don't duplicate names
public static Xamarin.Forms.ImageSource SourceFor(string filenameWithExtension)
{
var resources = typeof(EmbeddedSourceror).GetTypeInfo().Assembly.GetManifestResourceNames();
var resourceName = resources.First(r => r.EndsWith(filenameWithExtension, StringComparison.OrdinalIgnoreCase));
return Xamarin.Forms.ImageSource.FromResource(resourceName);
}
}
Usage:
Assuming an image element defined in XAML as centerImageElement:
centerImageElement.Source = EmbeddedSourceror.SourceFor("centerimage.png");
...and you can dynamically assign it however you like during run-time.

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

Image is not updating in UI when changing URI at run time using wpf mvvm

Here is my code
XAML:
<Image x:Name="profileimage" Grid.Row="0" Grid.Column="0" Style="{StaticResource ProfileViewListboxImagestyle}" Margin="0,10,2,10">
<Image.Source>
<BitmapImage UriSource="{Binding ImageUri}"></BitmapImage>
</Image.Source>
</Image>
Model:
class Contact
private Uri _imageUri;
public Uri ImageUri
{
get
{
return _imageUri;
}
set
{
base.Set<Uri>(() => ImageUri, ref _imageUri, value);
RaisePropertyChanged(()=>ImageUri);
}
}
ViewModel
UpdateImage(Uri uri)
{
Dispatcher.CurrentDispatcher.BeginInvoke(() =>
{
Contact.ImageUri=uri; //This is not called from UI thread.so i used dispatcher.
});
}
I don't know what is the mistake i have done but i am not getting any error while running this.
Image is not updated in UI but value is updated.
What is the problem?
As WPF has built-in type conversion from string or Uri to ImageSource, you may simply declare your Image Source binding in XAML like this:
<Image ... Source="{Binding ImageUri}" />
It is of course also necessary that you set the source object of the binding. If you do not specify it explicitly in the binding declaration, the DataContext of the Image control (or its container) must be set to the Contact instance.

How do I bind an Image dynamically in XAML?

The following displays an image correctly in a silverlight usercontrol:
Image image = pagingManager.BaseApp.DatasourceManager.GetImage("helloWorld1");
ContentArea.Content = image;
...
<ContentControl x:Name="ContentArea"/>
However, I want to dynamically bind the image to XAML, e.g. like this:
#region ViewModelProperty: MainImage
private Image _mainImage;
public Image MainImage
{
get
{
return _mainImage;
}
set
{
_mainImage = value;
OnPropertyChanged("MainImage");
}
}
#endregion
...
MainImage = pagingManager.BaseApp.DatasourceManager.GetImage("helloWorld1");
And my XAML is this but the result is that it shows nothing:
<Image Source="{Binding MainImage}"/>
What do I need to put in my XAML to make it display the image object I have in my ViewModelProperty?
The Image.Source property is of type ImageSource, so your ViewModel should expose an ImageSource. Image is a control, it has nothing to do in the ViewModel.
If you do expose an Image control in your ViewModel (which is definitely a bad idea), then you should display it in a ContentControl, not an Image control :
<ContentControl Content="{Binding MainImage}" />

Categories