ContentView does not update binding property - xaml

I created a custom control to be used in a listview. It almost works fine except for the custom property viewProgress that returns a boolean value based on the status of my order. Although the status gets his value correctly the boolean never gets reread and so the binding never receives a true value. I tried using breakpoints to detect if OnPropertyChanged is being run but it seems to never get there. The Item property seems to never get set, even though the data is bound. What am I doing wrong?
The ContentView:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:HandTerminal"
x:Class="HandTerminal.OrderItem">
<ContentView.Content>
<StackLayout>
<Frame
Margin="10,6,10,6"
Padding="10"
CornerRadius="10">
<StackLayout>
<StackLayout
Padding="0"
HorizontalOptions="Fill"
Orientation="Horizontal">
<Label
FontAttributes="Bold"
FontSize="Medium"
Text="Ordernumber"
TextColor="{DynamicResource TitleTextColor}" />
<Label
FontSize="Medium"
HorizontalOptions="FillAndExpand"
HorizontalTextAlignment="End"
Text="{Binding Item.Ordernumber}"
TextColor="{DynamicResource SubtleTextColor}" />
</StackLayout>
<StackLayout
IsVisible="{Binding viewProgress}"
Orientation="Horizontal"
HorizontalOptions="FillAndExpand">
<Label
HorizontalOptions="Start"
Text="Picked"
TextColor="{DynamicResource HighlightTextColor}" />
<ProgressBar
HorizontalOptions="FillAndExpand"
Progress="{Binding Item.PickedProgress}" />
<StackLayout
HorizontalOptions="End"
Orientation="Horizontal">
<Label
Text="{Binding Item.TotalPicked}"
TextColor="{DynamicResource HighlightTextColor}" />
<Label
Text="/"
TextColor="{DynamicResource HighlightTextColor}" />
<Label
Text="{Binding Item.TotalAmount}"
TextColor="{DynamicResource HighlightTextColor}" />
</StackLayout>
</StackLayout>
</StackLayout>
</Frame>
</StackLayout>
</ContentView.Content>
</ContentView>
Codebehind:
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace HandTerminal
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class OrderItem : ContentView
{
public OrderItem()
{
InitializeComponent();
Content.BindingContext = this;
}
public static BindableProperty ItemProperty = BindableProperty.Create(
propertyName: "Item",
returnType: typeof(Order),
declaringType: typeof(OrderItem),
defaultValue: new Order(),
defaultBindingMode: BindingMode.OneWay);
public Order Item
{
get
{
return (Order)GetValue(ItemProperty);
}
set
{
SetValue(ItemProperty, value);
OnPropertyChanged("Item");
OnPropertyChanged("viewProgress");
}
}
public static BindableProperty viewProgressProperty = BindableProperty.Create(
propertyName: "viewProgress",
returnType: typeof(bool),
declaringType: typeof(OrderItem),
defaultValue: false,
defaultBindingMode: BindingMode.OneWay);
public bool viewProgress
{
get
{
if (Item.Status == OrderStatus.Picking)
{
return true;
}
else
{
return false;
}
}
}
}
}
The ContentPage that uses the custom control:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="HandTerminal.Orders"
xmlns:controls="clr-namespace:HandTerminal"
xmlns:d="clr-namespace:HandTerminal;assembly=HandTerminal">
<ContentPage.Content>
<StackLayout
Orientation="Vertical"
VerticalOptions="CenterAndExpand">
<ListView
HasUnevenRows="True"
ItemsSource="{Binding Orders}"
VerticalOptions="StartAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<controls:OrderItem Item="{Binding .}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>

I tried your code and my suggestions are:
Create a viewProgressConverter
public class viewProgressConverter : IValueConverter{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
OrderStatus myOrder =(OrderStatus) value ;
if (myOrder == OrderStatus.Picking)
return true;
else return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
} }
You will have to add it to your AppResources in the App.Xaml
2.Modify your OrderItem View, Removing all the "Item." binding directly to your Order
<StackLayout
HorizontalOptions="FillAndExpand"
IsVisible="{Binding Status, Converter={StaticResource progressConverter}}"
Orientation="Horizontal">
You can delete the Content.BindingContext = this; and comment/delete all the bindeable properties

Related

RelativeSource in MAUI control not bound

I'm going through simple example explained in video:
https://youtu.be/5Qga2pniN78?t=961
At 16. minute (timestamp in link above), he implements the Delete Command on SwipeItem.
In my local project, everything worked so far, but Delete Command is never triggered. I checked source generators, DeleteCommand exists.
My XAML:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp1.MainPage"
xmlns:viewmodel="clr-namespace:MauiApp1.ViewModel"
x:DataType="viewmodel:MainViewModel">
<Grid RowDefinitions="100, Auto, *"
ColumnDefinitions=".75*, .25*"
Padding="10"
RowSpacing="10"
ColumnSpacing="10">
<Image Grid.ColumnSpan="2" Source="tom.jpg"
BackgroundColor="Transparent"></Image>
<Entry Placeholder="Enter task" Grid.Row="1" Text="{Binding Text}"></Entry>
<Button Text="Add" Grid.Row="1" Grid.Column="1" Command="{Binding AddCommand}"></Button>
<CollectionView Grid.Row="2" Grid.ColumnSpan="2" ItemsSource="{Binding Items}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="{x:Type x:String}">
<SwipeView>
<SwipeView.RightItems>
<SwipeItem Text="Delete" BackgroundColor="Red"
Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=DeleteCommand}"
CommandParameter="{Binding .}">
</SwipeItem>
</SwipeView.RightItems>
<Grid Padding="0,5">
<Frame>
<Label Text="{Binding .}" FontSize="24"></Label>
</Frame>
</Grid>
</SwipeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</ContentPage>
View Model:
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace MauiApp1.ViewModel
{
public partial class MainViewModel : ObservableObject
{
public MainViewModel()
{
Items = new();
Items.Add("test");
}
[ObservableProperty]
private ObservableCollection<string> items;
[ObservableProperty]
private string text;
[RelayCommand]
private void Add()
{
if (string.IsNullOrWhiteSpace(text))
{
return;
}
Items.Add(Text);
Text = string.Empty;
}
[RelayCommand]
private void Delete(string s)
{
if (Items.Contains(s))
{
Items.Remove(s);
}
}
}
}
Why is DeleteCommand not triggering?
try this
Command="{Binding BindingContext.DeleteCommand,
Source={x:Reference myPage}}"
where myPage is your page's name
<ContentPage x:Name="myPage" ...
Resolved,
I forgot to add <SwipeItems> element after <SwipeView.RightItems>.
AncestorType={x:Type viewmodel:MainViewModel}
Your viewmodel is not part of the visual tree, so you can't bind to it with relative source anyway.
You can use your CollectionView's Binding Context and then the specific property you need:
Command="{Binding BindingContext.DeleteCommand, Source={RelativeSource AncestorType={x:Type CollectionView}}}

Xamarin Custom ViewCell

while making custom nested views i faced a problem.
i'm trying to use my custom view(ViewCell) from various pages.
and this is how i used it.
it wont raise any error if i use View for Xaml and codebehind,
but it wont display anything.
if i try ViewCell for xaml and its codebehind, i get some error.
and this is how i used it.
in mainPage xaml
<local:UploaderCell Uploader="{Binding post.uploader}"/>
this is the line causing the error, and
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage
xmlns:local="clr-namespace:EodiRoad;assembly=EodiRoad"
xmlns:ic="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin.Abstractions"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="EodiRoad.SocialPage"
Title="Story">
<ContentPage.ToolbarItems>
<ToolbarItem Icon="plus.png" Order="Primary" Priority="0" Activated="OnCreatePostClicked"/>
</ContentPage.ToolbarItems>
<StackLayout>
<ListView
x:Name="socialPostsListView"
HasUnevenRows="true"
IsPullToRefreshEnabled="true"
Refreshing="Handle_Refreshing"
SeparatorVisibility="None"
ItemSelected="OnSocialPostSelected"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical" Padding="5">
<local:UploaderCell Uploader="{Binding post.uploader}"/>
<Image
x:Name="postImage"
Source="{Binding post.image}"
Aspect="AspectFill"
MinimumHeightRequest="200"
/>
<StackLayout Orientation="Horizontal">
<Switch IsToggled="{Binding post.isLiked}"/>
<Button Text="comment"/>
<Button Text="share"/>
</StackLayout>
<StackLayout Orientation="Vertical" HorizontalOptions="StartAndExpand">
<Label Text="{Binding post.content}"/>
<Label Text="{Binding post.uploadedTime}" TextColor="Gray"/>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
this is my full mainPage xaml code.
in UploderCell.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell
xmlns:ic="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin.Abstractions"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="EodiRoad.UploaderCell">
<StackLayout Orientation="Horizontal" Padding="5">
<Button Clicked="Handle_Clicked" Image="{Binding Uploader.userProfile.userThumbnail}" BorderRadius="5" HeightRequest="50" WidthRequest="50"/>
<ic:CircleImage
x:Name="userProfileImage"
HeightRequest="50"
WidthRequest="50"
Aspect="AspectFill"
Source="{Binding Uploader.userProfile.userThumbnail}"
/>
<Label Text="{Binding Uploader.username}"/>
</StackLayout>
</ViewCell>
and the codeBehind
public partial class UploaderCell : ViewCell
{
public static readonly BindableProperty UploaderProperty =
BindableProperty.Create(
propertyName: "Uploader",
returnType: typeof(UserModel),
declaringType: typeof(UploaderCell)
);
public UserModel Uploader
{
get
{ return GetValue(UploaderProperty) as UserModel; }
set
{ SetValue(UploaderProperty, value); }
}
public UploaderCell()
{
InitializeComponent();
BindingContext = this;
}
}
and i get this error from mainPage
object of type Project.UploaderCell cannot be converted to type 'xamarin.forms.view'
what am i doing wrong here?
how could i possibly fi this..

How can i capture the event of a tapped item (located in a template) from the base class?

I have a base grid
<Grid Grid.Row="1" Grid.Column="1" x:Name="GridName">
<StackLayout Orientation="Vertical">
<art:GridOptionsView ItemsSource="{Binding Items}" >
<art:GridOptionsView.ItemTemplate>
<DataTemplate>
<uikit:DashboardItemTemplate />
</DataTemplate>
</art:GridOptionsView.ItemTemplate>
</art:GridOptionsView>
</StackLayout>
</Grid>
which uses the following DashboardItemTemplate
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
BackgroundColor="White">
<ContentView.Content>
<Grid Padding="0">
<StackLayout VerticalOptions="Center" HorizontalOptions="Center" Orientation="Vertical" Spacing="10">
<Grid>
<Label Text="" Style="{StaticResource FontIcon}" HorizontalTextAlignment="Center" Opacity="1" FontSize="130" TextColor="{Binding BackgroundColor}" VerticalOptions="Center" HorizontalOptions="Center" IsVisible="{Binding Source={x:Reference Root}, Path=ShowiconColoredCircleBackground}" />
<Label Text="{Binding Icon}" Style="{StaticResource FontIcon}" Opacity="1" TextColor="White" VerticalOptions="Center" HorizontalOptions="Center" />
</Grid>
<Label Text="{Binding Name}" TextColor="{Binding Source={x:Reference Root}, Path=TextColor}" FontSize="14" HorizontalTextAlignment="Center">
</Label>
</StackLayout>
</Grid>
</ContentView.Content>
<ContentView.GestureRecognizers>
<TapGestureRecognizer Tapped="OnWidgetTapped" />
</ContentView.GestureRecognizers>
</ContentView>
How can i capture the "OnWidgetTapped" event on my base xaml class?
I do this usually with a custom bindable property ParentBindingContext in my template:
public class MyTemplate : ContentPage
{
public static BindableProperty ParentBindingContextProperty = BindableProperty.Create(nameof(ParentBindingContext),
typeof(object), typeof(BasePageTemplate));
public object ParentBindingContext
{
get { return GetValue(ParentBindingContextProperty); }
set { SetValue(ParentBindingContextProperty, value); }
}
}
And then in your page (which contains the template) just set the ParentBindingContext:
<DataTemplate>
<template:MyTemplate ParentBindingContext="{Binding BindingContext, Source={x:Reference Name=MyPageName}}" />
</DataTemplate>
With that you can access the full BindingContext of your page in your template. The following example of a command shows how the template can bind to a command MyCommand, which is in the BindingContext of the page:
Command="{Binding ParentBindingContext.MyCommand, Source={x:Reference Name=MyTemplatePageName}}"
But this presupposes that your page has a BindingContext behind (like a ViewModel). This ViewModel then contains the "global" commands for the whole page. These commands (or just methods) can then be accessed by the template, because they know about the BindingContext of the page.
I changed an answer from flow description to the code. The idea is to create ItemTemplate programatically and pass to its constructor the page with list (or grid). Define a function ItemTemplateTapped and call it from template.
EventOnGridPage
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ButtonRendererDemo.EventOnGridPage">
<ListView x:Name="listView" >
</ListView>
</ContentPage>
EventOnGridPage code behind
public partial class EventOnGridPage : ContentPage
{
public EventOnGridPage()
{
InitializeComponent();
listView.ItemsSource = new List<Contact>
{
new Contact { Name = "Kirti",Status = "True"},
new Contact { Name = "Nilesh",Status = "False"}
};
listView.ItemTemplate = new DataTemplate(loadTemplate);
}
private object loadTemplate()
{
return new ViewCell() { View = new EventOnGridTemplate(this) };
}
public void ItemTemplateTapped(string name)
{
DisplayAlert("ItemTemplateTapped", name, "OK");
}
}
EventOnGridTemplate xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ButtonRendererDemo.EventOnGridTemplate"
BackgroundColor="Green">
<Label Text="{Binding Name}" x:Name="myLabel"></Label>
</ContentView>
EventOnGridTemplate code behind
public partial class EventOnGridTemplate
{
EventOnGridPage parent;
public EventOnGridTemplate(EventOnGridPage parent)
{
this.parent = parent;
InitializeComponent();
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.Tapped += TapGestureRecognizer_Tapped;
myLabel.GestureRecognizers.Add(tapGestureRecognizer);
}
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
parent.ItemTemplateTapped(myLabel.Text);
}
}
If you already defined the tap gesture binding in the XAML code, you don't need to add the TapGestureRecognizer, simply sign your method to an event listener method:
Your XAML:
<ContentView.GestureRecognizers>
<TapGestureRecognizer Tapped="OnWidgetTapped" />
</ContentView.GestureRecognizers>
On C# code behind:
public void OnWidgetTapped(object sender, EventArgs args)
{
// do stuff here
}
You just need to implement your OnWidgetTapped method:
void OnWidgetTapped(object sender, System.EventArgs e)
{
// Do stuff here
}
Another solution. If you implemented OnWidgetTapped as was suggested you can use sender.Parent.Parent... till you get to the object you want - grid or page. Cast it to for example EventOnGridPage and then call the function of that object.

Xamarin Forms Rotating Listview adjust row width

In Xamarin Forms, I want to implement a horizontal listview (like shown in the image below). Via Rotation this is possible, but the I cannot change the row width. Is there also a possibility the let the second layout start under the first one?
Thanks in advance!
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Recipe.Pages.SearchPage"
Title="Search">
<ContentPage.Content>
<StackLayout Spacing="5" x:Name="layout" Orientation="Vertical" >
<StackLayout x:Name="layoutButtons" HorizontalOptions="FillAndExpand" Orientation="Horizontal" Spacing="5" BackgroundColor="Red">
<Button x:Name="btn1" BackgroundColor="White" Image="#drawable/scan" />
<Button x:Name="btn2" BackgroundColor="White" Image="#drawable/menu" />
<Button x:Name="btn3" BackgroundColor="White" Image="#drawable/search" />
</StackLayout>
<StackLayout x:Name="layoutList" >
<ListView x:Name="listView" Rotation="270" RowHeight="75" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="#eee" Orientation="Vertical" >
<StackLayout Orientation="Horizontal" >
<Button BackgroundColor="White" Rotation="90" Image="{Binding Recipe}" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
EDIT
I also tried with a grid in the listview. Having the same issue.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Recipe.Pages.SearchPage"
Title="Search">
<ContentPage.Content>
<StackLayout Spacing="5" x:Name="layout" Orientation="Vertical" >
<StackLayout x:Name="layoutButtons" HorizontalOptions="FillAndExpand" Orientation="Horizontal" Spacing="5" BackgroundColor="Red">
<Button x:Name="btn1" BackgroundColor="White" Image="#drawable/scan" />
<Button x:Name="btn2" BackgroundColor="White" Image="#drawable/menu" />
<Button x:Name="btn3" BackgroundColor="White" Image="#drawable/search" />
</StackLayout>
<StackLayout x:Name="layoutList" >
<ListView x:Name="listView" Rotation="270" RowHeight="75" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" BackgroundColor="White" Rotation="90" Image="{Binding Recipe}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
I have also facing the same issue. I used below xaml code to manage the height and width of ListView.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="VCRoom.HorizontalScroll">
<ContentPage.Content >
<RelativeLayout>
<ListView x:Name="TestListView"
RowHeight="80"
Rotation="270"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.5, Constant=-40}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=-0.5, Constant=40}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=Constant, Constant=80}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1}"
>
</ListView>
</RelativeLayout>
</ContentPage.Content>
</ContentPage>
Change RowHeight and Constant in XConstraint and YConstraint to manage Width and height of horizontal ListView accordingly.
Just for reference Below is custom cell I used to populate ListView items. I displayed vertical labels in each list item.
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="VCRoom.TestCell">
<ViewCell.View>
<StackLayout Orientation="Horizontal" HorizontalOptions="End" VerticalOptions ="Center">
<Label Rotation="90" Text="{Binding Day}" TextColor="#000000" HorizontalOptions="StartAndExpand" VerticalOptions="Start"/>
<Label Rotation="90" Text="{Binding mDate}" TextColor="#000000" HorizontalOptions="StartAndExpand" VerticalOptions="Start"/>
</StackLayout>
</ViewCell.View>
</ViewCell>
Hope this will help future users.
The best solution that i found to resolve this problem is making a CustomScrollView with properties from Listview, like is showed in the tutorial from the Fabio Cozzolino.
Pay attention that he has a updated version in the comments.
He created a custom scrollview:
public class TLScrollView : ScrollView
{ public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(CustomScrollView), default(IEnumerable),
BindingMode.Default, null, new BindableProperty.BindingPropertyChangedDelegate(HandleBindingPropertyChangedDelegate));
private static object HandleBindingPropertyChangedDelegate(BindableObject bindable, object value)
{
throw new NotImplementedException();
}
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemTemplateProperty =
BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(CustomScrollView), default(DataTemplate));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public event EventHandler<ItemTappedEventArgs> ItemSelected;
public static readonly BindableProperty SelectedCommandProperty =
BindableProperty.Create("SelectedCommand", typeof(ICommand), typeof(CustomScrollView), null);
public ICommand SelectedCommand
{
get { return (ICommand)GetValue(SelectedCommandProperty); }
set { SetValue(SelectedCommandProperty, value); }
}
public static readonly BindableProperty SelectedCommandParameterProperty =
BindableProperty.Create("SelectedCommandParameter", typeof(object), typeof(CustomScrollView), null);
public object SelectedCommandParameter
{
get { return GetValue(SelectedCommandParameterProperty); }
set { SetValue(SelectedCommandParameterProperty, value); }
}
static void HandleBindingPropertyChangedDelegate(BindableObject bindable, object oldValue, object newValue)
{
var isOldObservable = oldValue?.GetType().GetTypeInfo().ImplementedInterfaces.Any(i => i == typeof(INotifyCollectionChanged));
var isNewObservable = newValue?.GetType().GetTypeInfo().ImplementedInterfaces.Any(i => i == typeof(INotifyCollectionChanged));
var tl = (CustomScrollView)bindable;
if (isOldObservable.GetValueOrDefault(false))
{
((INotifyCollectionChanged)oldValue).CollectionChanged -= tl.HandleCollectionChanged;
}
if (isNewObservable.GetValueOrDefault(false))
{
((INotifyCollectionChanged)newValue).CollectionChanged += tl.HandleCollectionChanged;
}
if (oldValue != newValue)
{
tl.Render();
}
}
private void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Render();
}
}
Created a method to render the scrollview:
public void Render ()
{ if (ItemTemplate == null || ItemsSource == null)
{
Content = null;
return;
}
var layout = new StackLayout();
layout.Orientation = Orientation == ScrollOrientation.Vertical ? StackOrientation.Vertical : StackOrientation.Horizontal;
foreach (var item in ItemsSource)
{
var command = SelectedCommand ?? new Command((obj) =>
{
var args = new ItemTappedEventArgs(ItemsSource, item);
ItemSelected?.Invoke(this, args);
});
var commandParameter = SelectedCommandParameter ?? item;
var viewCell = ItemTemplate.CreateContent() as ViewCell;
viewCell.View.BindingContext = item;
viewCell.View.GestureRecognizers.Add(new TapGestureRecognizer
{
Command = command,
CommandParameter = commandParameter,
NumberOfTapsRequired = 1
});
layout.Children.Add(viewCell.View);
}
Content = layout; }
Then, a customRenderer to each platform (Here iOS):
[assembly: ExportRenderer(typeof(TLScrollView), typeof(TLScrollViewRenderer))]
namespace TitiusLabs.Forms.iOS.Controls
{
class CustomScrollViewRenderer : ScrollViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
var element = e.NewElement as CustomScrollView;
element?.Render();
}
} }
You can use the FlowListView plugin to archive horizontal listview easily on xamarin forms.
NuGet - DLToolkit.Forms.Controls.FlowListView

Xamarin.Forms: How to diplay a modal when an item in ListView is clicked?

Good Day everyone. I'm currently doing a simple application in Xamarin.Forms that allows me to CRUD record of an Employee. The created records are displayed on a ListView. Here's my screenshot.
What I want to do is whenever I click an Item on the ListView, is it will display a modal with a more detailed information of an Employee e.g (Birthday, Address, Gender, Work Experience). How can I do that? Is that even possible? Can you show me how?
This is my code that displays the ListView.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamarinFormsDemo.EmployeeRecordsPage"
xmlns:ViewModels="clr-namespace:XamarinFormsDemo.ViewModels;assembly=XamarinFormsDemo"
xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin.Abstractions"
BackgroundImage="bg3.jpg"
Title="List of Employees">
<ContentPage.BindingContext>
<ViewModels:MainViewModel/>
</ContentPage.BindingContext>
<StackLayout Orientation="Vertical">
<ListView ItemsSource="{Binding EmployeesList, Mode=TwoWay}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10" RowSpacing="10" ColumnSpacing="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<controls:CircleImage Source="icon.png"
HeightRequest="66"
HorizontalOptions="CenterAndExpand"
Aspect="AspectFill"
WidthRequest="66"
Grid.RowSpan="2"
/>
<Label Grid.Column="1"
Text="{Binding Name}"
TextColor="#24e97d"
FontSize="24"/>
<Label Grid.Column="1"
Grid.Row="1"
Text="{Binding Department}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackLayout Orientation="Vertical"
Padding="30,10,30,10"
HeightRequest="20"
BackgroundColor="#24e97d"
VerticalOptions="Center"
Opacity="0.5">
<Label Text="© Copyright 2015 smesoft.com.ph All Rights Reserved "
HorizontalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center" />
</StackLayout>
</StackLayout>
</ContentPage>
NOTE: Records that are displayed are CREATED in ASP.NET Web Application and just displayed on a ListView in UWP. If you need to see more codes, just please let me know.
Thanks a lot Guys.
To bind a command to item selected property see the example bellow otherwise ItemSelected will bind to a model property only
For full example see https://github.com/TheRealAdamKemp/Xamarin.Forms-Tests/blob/master/RssTest/View/Pages/MainPage.xaml.cs
Now you can bind an Icommand which could have something like
private Command login;
public ICommand Login
{
get
{
login = login ?? new Command(DoLogin);
return login;
}
}
private async void DoLogin()
{
await Navigation.PopModalAsync(new MySampXamlPage());
//await DisplayAlert("Hai", "thats r8", "ok");
}
and view :
[Navigation.RegisterViewModel(typeof(RssTest.ViewModel.Pages.MainPageViewModel))]
public partial class MainPage : ContentPage
{
public const string ItemSelectedCommandPropertyName = "ItemSelectedCommand";
public static BindableProperty ItemSelectedCommandProperty = BindableProperty.Create(
propertyName: "ItemSelectedCommand",
returnType: typeof(ICommand),
declaringType: typeof(MainPage),
defaultValue: null);
public ICommand ItemSelectedCommand
{
get { return (ICommand)GetValue(ItemSelectedCommandProperty); }
set { SetValue(ItemSelectedCommandProperty, value); }
}
public MainPage ()
{
InitializeComponent();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
RemoveBinding(ItemSelectedCommandProperty);
SetBinding(ItemSelectedCommandProperty, new Binding(ItemSelectedCommandPropertyName));
}
protected override void OnAppearing()
{
base.OnAppearing();
_listView.SelectedItem = null;
}
private void HandleItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null)
{
return;
}
var command = ItemSelectedCommand;
if (command != null && command.CanExecute(e.SelectedItem))
{
command.Execute(e.SelectedItem);
}
}
}
XAML:
<?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:ValueConverters="clr-namespace:RssTest.ValueConverters;assembly=RssTest"
x:Class="RssTest.View.Pages.MainPage"
Title="{Binding Title}">
<ContentPage.Resources>
<ResourceDictionary>
<ValueConverters:BooleanNegationConverter x:Key="not" />
</ResourceDictionary>
</ContentPage.Resources>
<Grid VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<ListView x:Name="_listView"
IsVisible="{Binding IsLoading, Converter={StaticResource not}" ItemsSource="{Binding Items}"
ItemSelected="HandleItemSelected"
VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Title}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ActivityIndicator IsVisible="{Binding IsLoading}" IsRunning="{Binding IsLoading}"
VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" />
</Grid>
</ContentPage>