Xamarin Image not Loading - xamarin

I am trying to display a list of images with different sizes and i want to display them with there full height and width, but the images are not loading on the screen if i didn't specified the height in the image control.
code :
<Grid>
<CollectionView ItemsSource="{Binding ImgList}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,*">
<Label>Image:</Label>
<Image Grid.Row="1" Grid.ColumnSpan="2" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Aspect="AspectFill" Source="{Binding .}"></Image>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
note: ImgList is an observableCollection of Images name string.

You could refer to the code below. You could check the image location and binding. I put the image in Android Resource/Drawable folder.
Xaml: The same xaml with yours.
Code behind:
public ObservableCollection<string> ImgList { get; set; }
public Page10()
{
InitializeComponent();
ImgList = new ObservableCollection<string>() { "image.jpeg", "image.jpeg" };
this.BindingContext = this;
}

Can you please make sure if you load images from your code project than you need to give Whole Path from root.
Can you please also check that Build Action of images.

Related

How can I stack images in a collection view so that they fit within the screen in Xamarin Forms?

Goal: I have 10 images, 4 of which are special. I want the 4 special images to fit within the entire view of the screen. I think I've actually done this, but it does not account for the tabs across the bottom of the app from AppShell.xaml. So it appears to fit properly, but the fourth image is partially behind the tabs and forces the user to scroll.
I had two collection views on the page and it caused the second one to scroll within its own frame, which was strange. So to solve it, I put the 4 special images in the header template of the collection view.
Here's what I have, followed with what I want it to do:
<RefreshView x:DataType="local:SpotlightViewModel" Command="{Binding LoadItemsCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}" Margin="0, 0, 0, 0">
<ScrollView InputTransparent="True" >
<Grid >
<CollectionView x:Name="SpotletsListView"
ItemsSource="{Binding Spotlets}"
EmptyView=""
SelectionMode="None">
<CollectionView.ItemsLayout>
<LinearItemsLayout SnapPointsAlignment="Start" SnapPointsType="Mandatory" Orientation="Vertical"></LinearItemsLayout>
</CollectionView.ItemsLayout>
<CollectionView.Header>
<CollectionView ItemsSource="{Binding SpotlightSpotlets}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="0, .1" x:DataType="model:Spotlet" HorizontalOptions="Center" VerticalOptions="FillAndExpand">
<Image Aspect="AspectFit" HorizontalOptions="Center" Source="{Binding Url, Converter={StaticResource ImageSourceConverter}}" ></Image>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</CollectionView.Header>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="0, .1" x:DataType="model:Spotlet" HorizontalOptions="Center">
<Image Aspect="AspectFit" HorizontalOptions="Center" Source="{Binding Url, Converter={StaticResource ImageSourceConverter}}" ></Image>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</ScrollView>
</RefreshView>
What I want is simple. Making it happen for a newbie to Xamarin like myself is not so simple.
First four images to fill the screen but account for the tabs at the bottom
All subsequent images to fill the screen
When scrolling, snap the image into place so two images cannot be seen at the same time
When I reach the bottom allow continued scrolling to go back to the top
Maybe I need to use carousel for part of this, which I tried, but also ran into some strange behavior. Could someone give me a boilerplate that may help?
Page 1:
[ -- this is an image -- ]
[ -- this is an image -- ]
[ -- this is an image -- ]
[ -- this is an image -- ]
Page 2:
[ -- one image, fills -- ]
Page 3:
[ -- one image, fills --]
When I hit the bottom, I want it to infinitely scroll the same items again. I have two separate collections in the code. One which holds the 4, another which holds the rest.
Some of the answers were staring at me from the examples and I didn't even realize it. I can't full take credit for the answer, but I'll add the relevant snippets to give someone else a head start, with hopefully more precise understanding of what is needed.
Source: https://github.com/xamarin/xamarin-forms-samples/tree/main/Templates/DataTemplateSelector
Although you can abstract these custom templates into complex views with their own .xaml file, this example is all that I needed in my particular case.
First, I created a class which was in charge of selecting the right template for a particular item:
public class SpotlightTemplateSelector : DataTemplateSelector
{
public DataTemplate SingleItemTemplate { get; set; }
public DataTemplate SpotlightItemTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
return ((Spotlet) item).IsSpotlight ? SpotlightItemTemplate : SingleItemTemplate;
}
}
My SpotlightItemTemplate is the one where I wanted to display multilple items within a single frame of the carousel. Now this might not be the best way to go about it, but here's what I did.
My model:
public class Spotlet
{
public string Url { get; set; }
public bool IsSpotlight { get; set; }
public List<Spotlet> Spotlets { get; set; }
}
The key here is the last prop Spotlets. When I fetched all my data, I created a new instance of Spotlet class to represent the first carousel item, and pushed every item that had IsSpotlight to that items Spotlets. So yes, a meaningless instance other than its child spotlets.
I then pushed to my main list the remaining items where IsSpotlight is false. Now there could be a much better way to handle this, but I'm new to Xamarin this is what worked for me (hint: Tell me a better way).
So at this point I have a list of spotlets, with the first one having nested spotlets and its IsSpotlight prop set to true.
Now, within my XAML file, I have my carousel view:
<CarouselView x:Name="SpotletsCarousel"
ItemsSource="{Binding Spotlets}"
ItemTemplate="{StaticResource spotlightDataTemplateSelector}">
</CarouselView>
It's referencing a static resource, which is local to the same file and a child of the wrapping <ContentPage />
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="SpotletItemViewTemplate">
<StackLayout x:DataType="model:Spotlet">
<Image Aspect="AspectFill" Source="{Binding Url, Converter={StaticResource ImageSourceConverter}}" ></Image>
</StackLayout>
</DataTemplate>
<DataTemplate x:Key="SpotletSpotlightItemViewTemplate" x:DataType="model:Spotlet">
<CollectionView x:Name="SpotletsListView" ItemsSource="{Binding Spotlets }">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="0, .1" x:DataType="model:Spotlet" HorizontalOptions="Center" VerticalOptions="FillAndExpand">
<Image Aspect="AspectFit" HorizontalOptions="Center" Source="{Binding Url, Converter={StaticResource ImageSourceConverter}}" ></Image>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</DataTemplate>
<local:SpotlightTemplateSelector x:Key="spotlightDataTemplateSelector" SingleItemTemplate="{StaticResource SpotletItemViewTemplate}" SpotlightItemTemplate="{StaticResource SpotletSpotlightItemViewTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
My carousel item template is referencing the spotlightDataTemplateSelector defined in local resources (i.e. x:Key="spotlightDataTemplateSelector").
This calls the method in the SpotlightTemplateSelector class I created, which does a simple ternary to return which template to use.
Notice in my multiple items template, I have a nested collection view within it. The rest, just a single image.

Xamarin Forms CollectionView Custom Layout Design

In the app that I'm designing, I use collectionview to show loads of images. And this is my current design.
And this is the design I want.
where the images that are shorter in height will have the next image fill up the white spaces.
Heres my code:
<CollectionView x:Name="cv_WallPapers"
ItemsSource="{Binding Results, Mode=TwoWay}"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
SelectionMode="Single"
SelectionChanged="cv_WallPapers_SelectionChanged"
>
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2"
VerticalItemSpacing="5"
HorizontalItemSpacing="5"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame Padding="4"
BorderColor="#34eba8"
BackgroundColor="#34eba8"
CornerRadius="10"
HasShadow="False">
<Image Source="{Binding urls.regular}"
HorizontalOptions="FillAndExpand"/>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I've no idea how to do that in Xamarin. And appreciate the help!
Unfortunately,CollectionView in Forms still doesn't support such a layout until now .However, we could use Custom Renderer as a workaround in Android.
public class MyCollectionViewRenderer: CollectionViewRenderer
{
public MyCollectionViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<ItemsView> elementChangedEvent)
{
base.OnElementChanged(elementChangedEvent);
if(elementChangedEvent.NewElement!=null)
{
this.SetLayoutManager(new StaggeredGridLayoutManager(2, 1));
}
}
}
In iOS , because the UICollectionView in native iOS doesn't have such a property or API , so we need to implement it manually (re-calculate the size of each item and set the offset) . Which you could refer https://developer.apple.com/documentation/uikit/uicollectionview/customizing_collection_view_layouts?language=objc . And I will post the feature request as soon as possible .

xamarin.forms listview populate by BindingContext and viewmodel

I'm new with Xamarin, but I'm coming from C# background.
I'm trying to set the items source of listview by passing viewmodel to the bindingcontext property. I know I can set the itemssoruce programatically in the code behind but I think setting it through the bindingcontext is the right way to do it, correct me if I'm wrong.
Let me start with what I have currently.
This is the viewmodel I have:
public class AirportSelectVM
{
public int AirportID { get; set; }
public string AirportICAO { get; set; }
public int VolumeGallons { get; set; }
}
In the code behind I'm doing this:
private void SetInitialListView()
{
ObservableCollection<AirportSelectVM> listAirport = new ObservableCollection<AirportSelectVM>();
AirportSelectVM firstAirport = new AirportSelectVM();
listAirport.Add(firstAirport);
BindingContext = listAirport;
}
And in the XAML I have:
<ContentPage.Content>
<StackLayout>
<Picker x:Name="pickerAircraft" ItemDisplayBinding="{Binding TailNumber}" SelectedItem="{Binding Id}" SelectedIndexChanged="PickerAircraft_SelectedIndexChanged" Title="Aircraft Selector"></Picker>
<ListView ItemsSource="{Binding listAirport}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="10,10,10,10">
<Label Text="Leg 1 Arrival" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
Just for comparison the picker items source is set in the code behind but eventually I would like to move that in the bindingcontext as well.
So, my main question will be how to setup the items source of a listview through bindingcontext?
you are setting the BindingContext of the Page to listAirport. So the ItemsSource will be the same as the page binding
<ListView ItemsSource="{Binding .}">
If you want to do it "the right way", you should learn more about the MVVM pattern.
For each page you are binding a page view model, which will be a bridge between your Models (data) and your UI.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm
Now if you just want to have a working code, you need to set your ItemsSource directly like this:
<ContentPage.Content>
<StackLayout>
<Picker x:Name="pickerAircraft" ItemDisplayBinding="{Binding TailNumber}" SelectedItem="{Binding Id}" SelectedIndexChanged="PickerAircraft_SelectedIndexChanged" Title="Aircraft Selector"></Picker>
<ListView x:Name="AirportListView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="10,10,10,10">
<Label Text="Leg 1 Arrival" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
code-behind:
private void SetInitialListView()
{
ObservableCollection<AirportSelectVM> listAirport = new ObservableCollection<AirportSelectVM>();
AirportSelectVM firstAirport = new AirportSelectVM();
listAirport.Add(firstAirport);
AirportListView.ItemsSource = listAirport;
}

FindViewById on Xamarin Forms?

I need some way to find View/object by its ID. I heared about FindViewById function, but it's not present in my ContentPage class. Where can I find it?
Context: I have ListView with buttons inside. I don't know how many buttons are there. When user clicks on one of those buttons, I get its ID and store globally. What I want to accomplish is to find this specific button, which id was stored in variable.
<StackLayout x:Name="chooseObjectButtons">
<ListView x:Name="SlotsList" ItemsSource="{Binding .}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout>
<Button Text="{Binding Text}" BackgroundColor="Gray" Clicked="SlotChosen" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Change the XAML to:
<ListView ItemsSource="{Binding Slots}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout>
<Button Text="{Binding Title}" BackgroundColor="Gray" Clicked="Handle_Clicked" Command="{Binding Select}" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Handle click:
private Button LastButtonClicked;
void Handle_Clicked(object sender, System.EventArgs e)
{
if (LastButtonClicked != null)
{
// Change background here
}
LastButtonClicked = (Button)sender;
// Do stuff here.
}
To process the specific command for each button use:
public List<SlotsButtons> Slots
{
get
{
return new List<SlotsButtons>
{
new SlotsButtons
{
Title = "T1",
Select = new Command(()=>{
// do stuff here when clicked.
})
},
new SlotsButtons
{
Title = "T2",
Select = new Command(()=>{
// do stuff here when clicked.
})
}
};
}
}
NOTE: Initial question answer.
In Xamarin Forms the class ContentPage is a Partial class.
One part is automatically generated from XAML and the other represents the code behind.
The XAML generated Partial class has the code to find the views by name.
The correct name is FindByName and you should't need to use this in your partial class because it its already made in the generated partial class.
If you want to access a view in your code behind just give it a name in XAML.
There is an XAML example:
<Button x:Name="button" ></Button>
And in your code behind you could do something like:
button.BorderWidth = 3;
If you still need to find a view for some reason, do this:
var button = this.FindByName<Button>("button");

panorama + listbox, wp7, text is truncated and scrolling does not work

i am using a Panorama control. inside each PanoramaItem, i have a ListBox. the ListBox holds a bunch of TextBlock; the reason is because i am displaying very long text and from another post, i found out that wp7 has limitations when displaying long text.
for example, i have two objects defined as follows.
public class TextItem {
public string Text { get; set; }
}
public class DisplayItem {
public string Header { get; set; }
public string FullHeader { get; set; }
public List<TextItem> TextItems { get; set; }
}
my xaml to bind to a List<DisplayItem> is as follows.
<controls:Panorama ItemsSource="{Binding}">
<controls:Panorama.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" TextWrapping="Wrap"/>
</DataTemplate>
</controls:Panorama.HeaderTemplate>
<controls:Panorama.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding FullHeader}" TextWrapping="Wrap"/>
<ListBox ItemsSource="{Binding TextItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</StackPanel>
</DataTemplate>
</controls:Panorama.ItemTemplate>
</controls:Panorama>
all the data bind properly, however, when i attempt to scroll the ListBox, it stops without ever going all the way to the bottom. the effect is to me is that "scrolling isn't working" and "text is truncated."
any idea on what i am doing wrong?
as a side note, i also posted a question on displaying very long text (i.e. an end-user license agreement EULA). a user responded by giving me a link to a control he made to display very long text. the post is at how many characters can a Silverlight TextBlock hold?. when i use that control and/or approach to store my long text, i get the same effect.
If you have a ListBox inside a StackPanel the framework is not able to determine the height of the controls and whether scrolling should be enabled.
Use a Grid instead of a StackPanel inside your DataTemplate.
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding FullHeader}" TextWrapping="Wrap"/>
<ListBox ItemsSource="{Binding TextItems}" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</DataTemplate>
The above will solve your immediate problem but you should also address the design decision to include a large amount of text on a panorama.
A panorama is not intended to display a large amount of text. Think of a panorama as being like a magazine cover. You wouldn't include an article on the cover. You'd include a headline or image to entice the viewer/user to read more within the magazine. The same principle should be applied here. Have content (a headline/title or equivalent image) on the panorma take the user to another page where they can read the full content.

Resources