Binding image source dynamically on xamarin forms - c#

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.

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.

How do I bind to an image from an api endpoint?

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}"

Can I access XAML elements in an array in the codebehind?

I've been looking around but I haven't been able to find anything on this. I am trying to get started making Windows 8.1 apps in C# with Visual Studio 2013 Pro. I want to be able to access multiple elements (particularly buttons or text blocks) in an array because this is more convenient for developing things like board games. For instance, if I were developing tic-tac-toe, I might use a series of buttons like this:
<Grid>
<Button Name="Cell00"/>
<Button Name="Cell01"/>
<Button Name="Cell02"/>
<Button Name="Cell10"/>
<Button Name="Cell11"/>
<Button Name="Cell12"/>
<Button Name="Cell20"/>
<Button Name="Cell21"/>
<Button Name="Cell22"/>
<Grid/>
Now for the function that would check for a win, I would have to check all possible combinations like this is in the code behind:
private bool CheckForWin()
{
if((Cell00 == Cell01) && (Cell01 == Cell02) && isNotBlank(Cell02)) return true;
if((Cell10 == Cell11) && (Cell11 == Cell12) && isNotBlank(Cell12)) return true
...
return false; //if none of the win conditions pass
}
This type of code would be extremely cumbersome. I would like to write it instead in a way that lets me check the array with for loops.
I realize that with tic-tac-toe, it is fairly easy to code it using brute force, but this was the first example that came to my head. Other games like Reversi or Go would not work well like this because of either the sheer size or the fact that pieces placed can change other cells than the one they were placed on.
Any help with this would be greatly appreciated.
This is not the correct way to use WPF. WPF is designed to use data binding....creating and manipulating UI elements directly is bad form. There are more posts/discussion/questions about this than you can imagine and I'll leave you to research them for yourself. In the mean time this is how you use WPF "properly":
First use NuGet to add MVVM lite to your project so that you get the ViewModelBase class and create a view model for a single cell:
public class Cell : ViewModelBase
{
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; RaisePropertyChanged(() => this.Text); }
}
}
One level up you'll want a main model to encapsulate an array of these, this is where you will typically do all your game logic:
public class MainModel : ViewModelBase
{
private ObservableCollection<Cell> _Cells;
public ObservableCollection<Cell> Cells
{
get { return _Cells; }
set { _Cells = value; RaisePropertyChanged(() => this.Cells); }
}
public MainModel()
{
this.Cells = new ObservableCollection<Cell>(
Enumerable.Range(1, 100)
.Select(i => new Cell { Text = i.ToString() })
);
}
}
Notice that all I'm doing at the moment is creating a 100-element collection of cells. This main view model becomes the one that you assign to your window's data context:
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainModel();
}
Now your XAML controls need to bind to this data. ItemsControl is used to render a collection of elements so use one of those and bind it to your array. You want them displayed in a 2D grid so replace the ItemsPanelTemplate with a WrapPanel. Finally add a DataTemplate for your Cell class so that a button gets drawn for each cell:
<Window.Resources>
<DataTemplate DataType="{x:Type local:Cell}">
<Button Width="32" Height="32" Content="{Binding Text}"/>
</DataTemplate>
</Window.Resources>
<ItemsControl ItemsSource="{Binding Cells}" Width="320" Height="320" HorizontalAlignment="Center" VerticalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
That's how you use WPF. Your logic is stored entirely in the view model and it's completely decoupled from the view. Here's what this particular code displays, it should be pretty self-evident how flexible this code is and easy to change:
That's very possible. Simply declare an array variable :
private Button[] _buttonArray;
populate the array once, maybe in constructor :
_buttonArray = new[] {Cell00, Cell01, .... , Cell22};
And all of the buttons are now accessible through _buttonArray.

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.

Use WPF styles like themes

I am now creating an application using WPF. Now I want to change the appearance of the application depend on the user input. Its mean that through a configuration window users can decide how the application looks like and depend on the selection it should change the styles. How can I achieve that rather use several styles per configuration.
For example-
Following rectangle consists of several texts. When restart the application, depend on the user selection it should display the content (changes saved in a some where and it can be easily get the current configuration details and depend on the saved details it should draw the appearance using WPF)
IF user select some option to display all 4 text it should display like in first image
If User select some option to to display only 3 or 2 texts, depend on the inner context it will re-size the rectangle(image 3/4).
For instance if this rectangle contain image it should re-size the rectangle accordingly. If user change the settings to remove the picture from the rectangle it should remove it and re-size the rectangle accordingly(image 4)
Place the texts (TextBox) and the image (Image) in a Grid to create the desired layout. The resizing will take place automatically.
Then, bind the Visibility property of each of your texts and your image to a property of some object that stores which state is selected in the options. (The best solution is to store this information in some new class of your own and assign an instance of that class to the DataContext property of your window.
For each of the bindings, create a value converter that returns either Visibility.Visible or Visibility.Collapsed, based on whether the respective element should be visible or invisible with the current options.
EDIT: Here is some exemplary code:
Assuming your very simple settings object looks like this:
public enum GuiMode {
FourTexts,
ThreeTexts,
OneText,
ThreeTextsAndImage
}
public class GuiSettings : INotifyPropertyChanged
{
public PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private GuiMode mode = GuiMode.FourTexts;
public GuiMode Mode {
get {
return mode;
}
set {
if (mode != value) {
switch (value) {
case GuiMode.FourTexts:
case GuiMode.ThreeTexts:
case GuiMode.OneText:
case GuiMode.ThreeTextsAndImage:
mode = value;
OnPropertyChanged("Mode");
break;
default:
throw new InvalidEnumArgumentException("value", (int)value, typeof(GuiMode));
}
}
}
}
}
This stores the mode of your GUI. Note the implementation of INotifyPropertyChanged, so when binding to the Mode property, changes of the Mode property will automatically update anything bound to it.
Then, for example, for text2, you could write the following value converter:
public class Text2VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
switch ((GuiMode)value) {
case GuiMode.OneText:
return Visibility.Collapsed;
default:
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException("This converter does not convert back.");
}
}
As text2 is always visible, except for the state when there is only one text displayed - GuiMode.OneText -, the respective Visibility values are returned by the converter. Also note that this converter simply assumes that the incoming value is a GuiMode value. To do this properly, you should check both what you are getting in value, as well as the targetType.
Once this is done, you can import the converter as a static resource into your Xaml:
<Window.Resources>
<Text2VisibilityConverter x:Key="text2vis"/>
</Window.Resources>
Depending on which namespaces you have imported, you might need to add the appropriate namespace prefix in front of Text2VisibilityConverter there.
The Visibility property of your text2 can then be bound to the Mode property from the GuiSettings class by using the Text2VisibilityConverter, assuming that the GuiSettings instance where you store your settings has been assigned to the DataContext property of the window:
<TextBlock Text="Text 2" Visibility="{Binding Mode, Converter={StaticResource text2vis}}"/>
Once that works, you can add more value converter classes for the visibilities of the other controls.
The question is pretty general so I'll refer you to a general how on using styles and templates to control how your WPF controls look.
http://msdn.microsoft.com/en-us/magazine/cc163497.aspx
There are several way you can change how your controls look and act at run time.
A direct and easy to understand way(if you've come from winforms) to interact wpf templates is is by overriding the OnApplyTemplate method and then setting the template you want to use from a library of templates you've created or procured.
http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.onapplytemplate.aspx
But what the best approach for you really depends on how you are loading your users preferences and the fundamental design of your UI, MVVM vs MVC vs Custom Controls, etc.
You can try something similar to this:
<Grid>
<StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" Height="30">
<Button Content="Option1" Name="Option1" Click="Option1_OnClick"></Button>
<Button Content="Option2" Name="Option2" Click="Option2_OnClick"></Button>
<Button Content="Option3" Name="Option3" Click="Option3_OnClick"></Button>
<Button Content="Full" Name="Full" Click="Full_OnClick"></Button>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Image Source="/WpfApplication3;component/Resources/vaca.png" HorizontalAlignment="Left" VerticalAlignment="Top" Width="150" Height="150" Name="Image"></Image>
<StackPanel Orientation="Vertical" >
<Label Content="Text1" Name="Text1" />
<Label Content="Text2" Name="Text2" />
<Label Content="Text3" Name="Text3" />
<Label Content="Text4" Name="Text4" />
</StackPanel>
</StackPanel>
</Grid>
Code behind:
private void Option1_OnClick(object sender, RoutedEventArgs e)
{
Image.Visibility = Visibility.Collapsed;
}
private void Option2_OnClick(object sender, RoutedEventArgs e)
{
Image.Visibility = Visibility.Collapsed;
Text4.Visibility = Visibility.Collapsed;
}
private void Option3_OnClick(object sender, RoutedEventArgs e)
{
Image.Visibility = Visibility.Collapsed;
Text4.Visibility = Visibility.Collapsed;
Text3.Visibility = Visibility.Collapsed;
Text2.Visibility = Visibility.Collapsed;
}
private void Full_OnClick(object sender, RoutedEventArgs e)
{
Image.Visibility = Visibility.Visible;
Text4.Visibility = Visibility.Visible;
Text3.Visibility = Visibility.Visible;
Text2.Visibility = Visibility.Visible;
}

Categories