Xamarin display list of imagesources in XAML - xaml

Is it possible to display a list of ImageSources in XAML with Xamarin like you do with an List of Strings? In string you would do:
<ListView x:Name="newsList" ItemsSource="{Binding newsList}" ItemTapped="listView_ItemTapped" IsPullToRefreshEnabled="True" RefreshCommand="{Binding RefreshNewsCommand}" IsRefreshing="{Binding IsCurrentlyRefreshing}" />
However if you do this with an list of ImageSources it will not work.
Does anyone know an appropriate way to load this list of images into my few? Preferably so that i can load them next to the items of my NewsList?

Yes, you can define an "ItemTemplate" on the ListView (xaml / c#). There are 2 predefined ViewCells that you can assign to the ItemTemplate. One of them is "ImageCell". But ImageCell displays an Image + Text.
Also you might be interested in this:
https://developer.xamarin.com/guides/xamarin-forms/user-interface/listview/customizing-cell-appearance/
You will have to create a custom ViewCell.
Something like this (pseudo code):
ViewCell:
<ViewCell>
<ViewCell.View>
<Image Source="{Binding CoolImageSource}" />
</ViewCell.View>
</ViewCell>
//Your ListView, bring the namespace in, to reference your viewcell
//You can also define the item directly inside of the DataTemplate
//But keep your code clean and hold the viewcells separately
<ListView xmlns:vc=YourNameSpace.ViewCellsFolder>
<ListView.ItemTemplate>
<DataTemplate>
<vc:MyCoolViewCell />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
//And finally assign a List of such model:
public class ImageItem
{
public ImageSource CoolImageSource { get; set; }
}

Related

TargetNullValue not working in ListView in Xamarin app

I'm trying to display some text -- or an image -- if there's no data for my ListView to display in my Xamarin Forms 5 app but I can't get it to work.
Here's what my ListView looks like:
<ListView
x:Name="CustomersList"
ItemsSource="{Binding Customers, TargetNullValue='No customers!'}"
BackgroundColor="Transparent">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell
Text="{Binding FullName}"
TextColor="{StaticResource PrimaryDark}"
Command="{Binding Source={x:Reference Name=CustomersList}, Path=BindingContext.CustomerSelectedCommand}"
CommandParameter="{Binding .}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The source for the ListView is set as follows in my ViewModel
ObservableRangeCollection<CustomerModel> customers;
public ObservableRangeCollection<CustomerModel> Customers
{
get => customers;
set
{
if (customers == value)
return;
customers = value;
OnPropertyChanged();
}
}
If my API call returns nothing, I do a Customers = null;
When I use TargetNullValue as shown above, I acutally get errors where I bind the TextCell to FullName. It shows:
'FullName' property not found on 'N', target property:
'Xamarin.Forms.TextCell.Text'
It then repeats this error message for every character in No Customers!.
I then tried using FallbackValue. This time I don't get an error but it simply doesn't show the "No Customers!" message.
How do I handle a ListView having no data to show? At the very least, I'd like to show some simple text message but it would be even better to show something nicer, maybe an image.
If you are ok to switch from ListView to CollectionView, you can use the built-in CollectionView.EmptyView:
<CollectionView ItemsSource="{Binding Customers}"
EmptyView="No customers!" ... />
With the same EmptyView you can define whatever view:
<CollectionView>
<CollectionView.EmptyView>
<ContentView>
<image ... />
</ContentView>
</CollectionView.EmptyView>
</CollectionView>
Docs: CollectionView EmptyView

Bind CommandParameter to SelectedItem of GridView

I work an a Windows 8 application which shows a GridView on one page. When ever the user selects an item of this grid and clicks on a button, the next page is loaded with detail information of the selected item.
I am using MVVM for this and have a DelegateCommand from Prims:
public DelegateCommand<Route> ShowRouteDetailsCommand { get; private set; }
This command is initialized inside the constructor:
this.ShowRouteDetailsCommand = new DelegateCommand<Route>(this.ShowRouteDetails);
The navigation is done by Prisms navigation service:
private void ShowRouteDetails(Route route)
{
this.NavigationService.Navigate(PageNames.RouteDetails, route.Id);
}
The routes are shown inside a GridView:
<GridView x:Name="RouteGrid"
ItemsSource="{Binding Routes}"
SelectionMode="Single">
<GridView.ItemTemplate>
<DataTemplate> ...
The command is currently added inside the app bar (just for testing):
<AppBarButton Command="{Binding ShowRouteDetailsCommand}"
CommandParameter="{Binding SelectedValue,
ElementName=RouteGrid, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Icon="Forward" />
My problem is, that the parameter of ShowRouteDetails is allways empty. It doesn't even matter if I try GridViews SelectedValue or SelectedItem property.
I know that I could easily add a SelectedRoute property, bind the SelectedItem to it and use it in ShowRouteDetails but this seems dirty to me.
Why don't you just create a var in your viewModel and bind it to the SelectedItem of the gridView? In this way, when you run the command, you have only to read the value of that var.
<GridView x:Name="RouteGrid" ItemsSource="{Binding Routes}"
SelectionMode="Single" SelectedItem="{Binding myVar}">
<GridView.ItemTemplate>
<DataTemplate>

RepeaterView<T> in Xamarin Forms Labs

I'm trying to figure out the RepeaterView in Xamarin Forms Labs.
THE BACKGROUND
I have a simple entity called Entry that has property called Notes
class Entry
{
public string Notes { get; set; }
}
In the common PCL, I've inherited from the RepeaterView to make it non-generic.
class EntryRepeater : Xamarin.Forms.Labs.Controls.RepeaterView<MyKids.Core.Entities.Entry>
{
}
And the XAML of the page where I'm trying to use the repeater looks like this
<rep:EntryRepeater ItemsSource="{Binding Entries}">
<rep:EntryRepeater.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout>
<Label Text="{Binding Notes}" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</rep:EntryRepeater.ItemTemplate>
</rep:EntryRepeater >
where Entries is an ObservableCollection.
THE ISSUE
The problem is that the binding in the DataTemplate seems to point to the main model and not to one item in the collection. BUT it is repeated the correct number of times.
[0:] Binding: 'Notes' property not found on 'MyApp.ViewModels.EntryListViewModel', target property: 'Xamarin.Forms.Label.Text'
To circle back to this question, this was actually an error in earlier versions of Xlabs. It's now fixed and the code above does work.

Silverlight Find Control in Data Template

I have the following xaml code:
<dxb:BarButtonItem Name="btnPrev" Cursor="Hand" ItemClick="btnPrev_ItemClick">
<dxb:BarButtonItem.ContentTemplate>
<DataTemplate>
<Image x:Name="imgSkipLeft" Source="/ProjectTool;component/images/arrowleft.png" Height="16" Width="16">
<ToolTipService.ToolTip>
<TextBlock x:Name="txtBlockTip_Left"/>
</ToolTipService.ToolTip>
</Image>
</DataTemplate>
</dxb:BarButtonItem.ContentTemplate>
</dxb:BarButtonItem>
How can I find txtBlockTip_Left and modify the text
Rather that trying to find the TextBlock on the DataTemplate to change the text it would be better to set the text through a binding and then change the property that the Text property is bound to. The following resource has an example on how to use data binding within a DataTemplate: http://www.silverlight.net/learn/data-networking/binding/data-binding-to-controls-%28silverlight-quickstart%29
Using the VisualTreeHelperExtensions class and put a grid inside the data template and give it a name, in the example below the name is grdTemplate.
var bttn= btnPrev.ItemContainerGenerator.ContainerFromItem(btnPrev);
var dataTemplate = bttn.GetDescendantsOfType<Grid>().FirstOrDefault(g => g.Name == ("grdTemplate"));
var textBlocks= VisualTreeHelperExtensions.GetDescendantsOfType<TextBlock>(dataTemplate);
TextBlock txtBlockTip_left = textBlocks.ElementAt(0);

Why can't get validation error display in validationSummary?

I have a form with some validations set in entity metadata class. and then binding entity instance to UI by VM. Something as below:
Xaml like:
<Grid x:Name="LayoutRoot">
<StackPanel VerticalAlignment="Top">
<input:ValidationSummary />
</StackPanel>
<TextBox Text="{Binding Name, Mode=TwoWay}" />
<ComboBox x:Name="xTest" ItemsSource="{Binding MyList}"
SelectedItem="{Binding MyItem,Mode=TwoWay,
DisplayMemberPath="MyName"
ValidatesOnDataErrors=True,
ValidatesOnNotifyDataErrors=True,
ValidatesOnExceptions=True,
NotifyOnValidationError=True,UpdateSourceTrigger=Explicit}" />
</Grid>
Code-behind like:
public MyForm()
{
InitializeComponent();
this.xTest.BindingValidationError +=new EventHandler<ValidationErrorEventArgs>((s,e)=>{
BindingExpression be = this.xTest.GetBindingExpression(ComboBox.SelectedItemProperty);
be.UpdateSource();
if (e.Action == ValidationErrorEventAction.Added)
((ComboBox)s).Foreground = new SolidColorBrush(Colors.Red);
});
}
Metadata like:
[Required]
public string Name { get; set; }
[RequiredAttribute]
public int MyItemID { get; set; }
But when running the app, I got nothing display in valudationSummary.
For CombBox, even there is error, looks like BindingValidationError event is never fired.
How to resolve it?
Why are you using an Explicit UpdateSourceTrigger?
Silverlight validation happens inside the binding framework, when the binding is updating the source object. The way you have this, there won't be a binding validation error because you never tell the binding to update the source object. Well, actually you do, but it happens inside the validation error event handler. You've written chicken-and-egg code.
Remove your UpdateSourceTrigger on your binding or set it to Default.
Remove the explicit call to BindingExpression.UpdateSource.
Remove setting the ComboBox foreground to red - you are using NotifyOnValidationError=True, which eliminates any need to manually color the control.
Remove the DisplayMemberPath from the binding
So your XAML:
<Grid x:Name="LayoutRoot">
<StackPanel VerticalAlignment="Top">
<input:ValidationSummary />
<ComboBox x:Name="xTest" ItemsSource="{Binding MyList}"
SelectedItem="{Binding MyItem,
Mode=TwoWay,
ValidatesOnDataErrors=True,
ValidatesOnNotifyDataErrors=True,
ValidatesOnExceptions=True,
NotifyOnValidationError=True}" />
</StackPanel>
</Grid>
And your code:
public MyForm()
{
InitializeComponent();
// you don't need anything here to have the validations work
}