BindableBase not working on CollectionView - xamarin

I have a CollectionView on my project with the most stable version of Xamarin Forms that supports CollectionView (4.3.0.908675) with the following code below.
<CollectionView x:Name="ScrollButtons"
ItemsSource="{Binding MenuItems}"
SelectedItem="{Binding SelectedMenuItem, Mode=TwoWay}"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
HeightRequest="90"
SelectionMode="Single"
SelectionChangedCommand="{Binding MenuItemSelectedCommand}"
BackgroundColor="{DynamicResource BackgroundColorShell}">
<CollectionView.Footer>
<!--HACK to keep showing last item on CollectionView -->
<BoxView BackgroundColor="Transparent" HeightRequest="90" WidthRequest="50"/>
</CollectionView.Footer>
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Horizontal"
Span="1" HorizontalItemSpacing="5"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid WidthRequest="90" HeightRequest="90" Padding="1">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Frame Grid.Column="0"
Grid.Row="0"
BorderColor="Black"
BackgroundColor="{Binding BackgroundColor}"
>
</Frame>
<StackLayout Padding="5" Grid.Row="0" Grid.Column="0">
<Label Text="{Binding Text}"
TextColor="{Binding TextColor}"
LineBreakMode="WordWrap"
FontSize="{StaticResource BaseFontSize}"
x:Name="tileLabel">
</Label>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" VerticalOptions="EndAndExpand">
<Image Source="{Binding SecondaryIconSource}"
HorizontalOptions="Start"
VerticalOptions="EndAndExpand"
WidthRequest="25"
HeightRequest="25"
IsVisible="{Binding IsSecondaryIconVisible}"
/>
<Image Source= "{Binding ImageIcon}"
HorizontalOptions="EndAndExpand"
VerticalOptions="EndAndExpand"
WidthRequest="25"
HeightRequest="25"
x:Name="tileIcon">
</Image>
</StackLayout>
</StackLayout>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
OnMenuSelectedItemCommand
private async Task OnMenuItemSelected()
{
Console.WriteLine("OnMenuItemSelected");
await NavigationService.NavigateAsync($"{SelectedMenuItem.NavigationPath}");
HighlightedMenuItem = SelectedMenuItem;
SelectedMenuItem = null;
}
The CollectionView consists of a collection of BottomMenuItem class which inherits from BindableBase for Prism. My goal is to change the properties of a BottomMenuItem as its selected on the CollectionView. However, the collection view is acting weird and it only changes based on BottomMenuItem that is not currently in the screen. As shown below, it only works on the 5th item and beyond, items that are not initially loaded on the screen.
Any help would be greatly appreciated. Thank you!

I figured it out. The problem is that my code relies on the scrolling to an item in order for a selectedItem to be highlighted. I made an assumption that the bug is based on item cells that are initially loaded are not working but in reality it was items that doesn't need scrolling.
Based on my OnMenuItemSelected, I pass the highlighted item to the next page. Handle that logic with the OnNavigatingTo logic below.
public override async void OnNavigatingTo(INavigationParameters parameters)
{
base.OnNavigatingTo(parameters);
Console.WriteLine("OnNavigatingTo");
HighlightedMenuItem = parameters.GetValue<BottomMenuItem>("highlightedMenuItem");
foreach (var item in MenuItems)
{
item.IsActive = false;
}
if (HighlightedMenuItem != null)
{
Console.WriteLine("OnNavigatingTo HighlightedItem - {0}", HighlightedMenuItem.Text);
HighlightedMenuItem.IsActive = true;
}
}

Related

CollectionView Xamarin How to speed rendering

I have a list to show where we show repeat information from SQlite database to user. In this we show basically the product price, name and type. Usually a user will have 10-15 of such records.
Problem is it take 10+ sec to display the list. Now, the architecture of application. On page load of Shell application we fetch the information from web request and store that in SQlite and then Bind the Result from Sqlite StackLayout with Bindable Property. But it took more than 10-11 Sec to display us.
So we change it to Grid with Bindable property and results are same. Though I don't like CollectionView (as I found their select property weird. ) We use CollectionView with dataTemplate as Grid (as we have some tabular information to show) . The results still take same 10+ secs to load up.
i.e. I use Stacklayout, Grid, CollectionView, it take that much time. We are not using the ObservableCollection to bind, but our class elements are all INotifyPropertyChanged. So, I try to disable it but still even when I do not inherity INotifyPropertyChanged and remove "OnPropertyChanged" calls it still take same time.
However, when I reduce the information to just show the name of product (through binding) it reduce to 5 secs. Which is still a lot as a Collection View with just 10 records and showing one Bindable Grid showing result in 5 second is lot more than expected. I understand my other data (which call functions) maybe slowing it down but can we reduce those 5 second somehow?
I cannot share much of class, but some of properties are pretty straight forward Property with INotifyPropertyChange on string or int values.
Here is Code for loading CollectionView
public async Task LoadSearch()
{
CurrentState = LayoutState.Loading;
try
{
var o = await PatientMedicine.FetchPatientMedicine();
if (o)
{
var _sr = (await DBCoreAsync.GetDataAsync<PatientMedicine>())?.OrderBy(x => x.PatientMedID).ToList();
if (_sr != null)
{
SearchResults = _sr;
}
CurrentState = LayoutState.Success;
}
else
{
CurrentState = LayoutState.Error;
}
}
catch (Exception ex)
{
CurrentState = LayoutState.Error;
}
}
Here is Collection itself.
<CollectionView ItemsSource="{Binding SearchResults}" ItemSizingStrategy="MeasureFirstItem">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="0,7" ColumnSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<BoxView BackgroundColor="{Binding LowStock, Converter={StaticResource BoolToColorReverse}}" CornerRadius="5,0,5,0" />
<Grid Grid.Column="1" Margin="0"
Padding="10,5" BackgroundColor="{StaticResource PrimaryLight2}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="40" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.Column="0" Grid.Row="0" Source="{Binding Medicine.RxIcon}" ></Image>
<Label Grid.Column ="1" Grid.Row="0" Style="{StaticResource GeneralLabelBlack}" Text="{Binding Medicine.MedicineName}"></Label>
<Button Grid.Column="2" Grid.Row="0" Command="{Binding Path=BindingContext.NavigateToMedicineReminder, Source={x:Reference Name=MedicineListPageName}}" CommandParameter="{Binding .}"
BackgroundColor="Transparent" VerticalOptions="Center" HorizontalOptions="End" ImageSource="{FontImage FontFamily=FontAwesomeSolid, Glyph=, Color={StaticResource Primary},Size=16}" ></Button>
<Image Grid.Column="0" Grid.Row="1"
Source="{StaticResource ImageSourceIconClockOpenHand}"></Image>
<Label Grid.Column ="1" Grid.Row="1" Text="{Binding RepeatStatusFormatted}"></Label>
<Image Grid.Column="0" Grid.Row="2"
Source="{StaticResource ImageSourceIconStock}"></Image>
<Label Grid.Column ="1" Grid.Row="2" Text="{Binding StockAndRequirement}"></Label>
<Image Grid.Column="0" Grid.Row="3"
Source="{StaticResource ImageSourceIconClock}"></Image>
<Label Grid.Column ="1" Grid.Row="3" Text="{Binding NextReminder}"></Label>
<Label Grid.Column ="0" Grid.Row="4" Grid.ColumnSpan="2" Text="{Binding Medicine.AilmentString}"></Label>
<Label Grid.Column ="0" Grid.ColumnSpan="2" Grid.Row="5" IsVisible="{Binding LowStock}" Text="Stock running low. Order a refill now" HorizontalOptions="End"></Label>
<Button Grid.Column="2" Grid.Row="5" Command="{Binding Path=BindingContext.AddtoCart, Source={x:Reference Name=MedicineListPageName}}" CommandParameter="{Binding .}"
BackgroundColor="Transparent" IsVisible="{Binding LowStock}" VerticalOptions="Center" HorizontalOptions="End" ImageSource="{FontImage FontFamily=FontAwesomeSolid, Glyph=, Color={StaticResource Primary},Size=16}" />
</Grid>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
To add more information: I am okay with Internet load time which is <=1.5 seconds the time I calculate and show here is exclusively for When I set the ViewModel to Values from temporary variable to main variable I did it just to see if DB access is slow or not. (to step it out). I have my control inside a StackLayout that use Xamarin Community Toolkit statelayout.
Also I go through MS's Suggestion on optimizing performance about Compile XAML etc, but since it is new App using Xamarin Forms 5.0 I don't think it is problem as it is already set by default.

How to set all the item template data into a single view in Xamarin Carousel

I have tried to make all the items in the itemtemplate into a single view as like in the below image, how to achieve this by using Xamarin CarouselView, i am using like this
carousel = new CarouselView();
carousel.BindingContext = this;
carousel.ItemTemplate = itemTemplate;
carousel.SetBinding(CarouselView.ItemsSourceProperty, new Binding(nameof(this.Items), mode: BindingMode.OneWay));
LinearItemsLayout linearItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Horizontal);
linearItemsLayout.SnapPointsAlignment = SnapPointsAlignment.Start;
linearItemsLayout.SnapPointsType = SnapPointsType.Mandatory;
carousel.ItemsLayout = linearItemsLayout;
this.Children.Add(carousel,0,1);
Expected UI
The easiest way of doing something like that would be to use the Horizontal CollectionView
CollectionView can display its items in a horizontal list by setting its ItemsLayout property to HorizontalList:
When you check the documents it gives a similar example
<CollectionView ItemsSource="{Binding Monkeys}"
ItemsLayout="HorizontalList">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="35" />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="140" />
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2"
Source="{Binding ImageUrl}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60" />
<Label Grid.Column="1"
Text="{Binding Name}"
FontAttributes="Bold"
LineBreakMode="TailTruncation" />
<Label Grid.Row="1"
Grid.Column="1"
Text="{Binding Location}"
LineBreakMode="TailTruncation"
FontAttributes="Italic"
VerticalOptions="End" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
Alternatively, this layout can also be accomplished by setting the ItemsLayout property to a LinearItemsLayout object, specifying the Horizontal ItemsLayoutOrientation enumeration member as the Orientation property value:
<CollectionView ItemsSource="{Binding Monkeys}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal" />
</CollectionView.ItemsLayout>
...
</CollectionView>
This results in a single row list, which grows horizontally as new items are added:

List View on binding context change empties the button text

I have been facing this weird behaviour for a while now and I am unable to understand what exactly is causing the problem.
I am using FreshMvvm and I have a ListView with two buttons inside of it.
Now the problem is one of the buttons gets its text from Binding, Secondly to assign the button with a click command I had to follow this.
Now after adding this, the click event works perfectly but the text binding is not working I suspected this happened because of binding context change which I am sure is the whole reason but I am not able to find a way to fix this my listview code is as follows:
<ListView Grid.Row="1"
ItemsSource="{Binding CategoryAndActivities}"
x:Name="WishListName"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame>
<Grid VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!--Image and Title-->
<AbsoluteLayout Grid.Row="0"
HeightRequest="70"
IsClippedToBounds = "true">
<ffimageloading:CachedImage Source="{Binding ActivityImage}"
Aspect="AspectFill"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.0, 0.0, 0.3, 0.85"
Margin="5, 0, 5, 5"
ErrorPlaceholder="nopreviewlandscape"
LoadingPlaceholder="loadingicon"/>
<Label x:Name="ActivityNameLabel"
Text="{Binding ActivityName}"
FontAttributes="Bold"
VerticalTextAlignment="Start"
TextColor="{StaticResource price_text_color}"
FontSize="Small"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="1.0, 0.0, 0.7, 0.85"
Margin="5, 5, 5, 5">
</Label>
</AbsoluteLayout>
<!--Descp-->
<StackLayout Grid.Row = "1"
IsClippedToBounds="true">
<Label Text="{Binding AcitivityDescription}"
FontSize="Small"
LineBreakMode="WordWrap"
Margin="5, 0, 5, 5"/>
</StackLayout>
<Grid BackgroundColor="White"
Grid.Row = "2"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<Button BackgroundColor="{StaticResource ColorBrandingYellow}"
HorizontalOptions="FillAndExpand"
Command="{Binding AddToWishListCommand}"
Grid.Column="0"
BindingContext="{Binding Source={x:Reference ListName}, Path=BindingContext}"
CommandParameter="{Binding Source={x:Reference ActivityNameLabel},Path=BindingContext}"
TextColor="Black"
Text="{resourceLocal:Translate addToWishlist}"
FontSize = "Small" />
<Button BackgroundColor="{StaticResource ColorBrandingYellow}"
HorizontalOptions="FillAndExpand"
Grid.Column="1"
TextColor="Black"
Text="{Binding ActivityAmount}"
FontSize = "Small"
Command="{Binding GoFeatureActivityDetail}"
BindingContext="{Binding Source={x:Reference ListName}, Path=BindingContext}"
CommandParameter="{Binding Source={x:Reference ActivityNameLabel},Path=BindingContext}"/>
</Grid>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The problem is in the button with binding as text what happems is the text just show empty even though the data actually exist.
The Click event code is as follows:
public ICommand GoFeatureActivityDetail { get; set; }
public BrowseFeaturesPageModel()
{
AddToWishListCommand = new Command(WishListCommand);
GoFeatureActivityDetail = new Command(FeatureActivityDetailCommand);
}
private async void FeatureActivityDetailCommand(object obj)
{}
Right thinking, bad implementation.
What's going on here is that you've changed the Button's BindingContext and now it's not anymore able to "see" the ActivityAmount property of the item 'cause it's 'looking' to the BrowseFeaturesPageModel object.
You can keep the things simpler, changing the BindingContext only where you'll use, not the whole View (the button in this case):
<Button BackgroundColor="{StaticResource ColorBrandingYellow}"
HorizontalOptions="FillAndExpand"
Grid.Column="1"
TextColor="Black"
Text="{Binding ActivityAmount}"
FontSize = "Small"
Command="{Binding BindingContext.GoFeatureActivityDetail, Source={x:Reference ListName}}"
CommandParameter="{Binding .}"/>

How to add sub menu items clicking on MenuItems in xamarin.forms MasterDetailPage

Hi I am trying to add submenu items When clicking on the Menuitems in MasterDatailPage.My master page xaml is like
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="HomeMaster"
Title="Master"
Icon="hamburger.png">
<StackLayout>
<ListView x:Name="ListViewMenuItems"
SeparatorVisibility="None"
HasUnevenRows="true"
ItemTapped="ListViewMenuItems_ItemTapped"
ItemsSource="{Binding MenuItems}">
<ListView.Header>
<Grid BackgroundColor="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>
<Image
Grid.Column="1"
Grid.Row="2"
Source="User.png" HeightRequest="100" x:Name="userImg"/>
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<StackLayout VerticalOptions="FillAndExpand"
Orientation="Horizontal"
Padding="20,10,0,10"
Spacing="20">
<Image Source="{Binding Icon}"
WidthRequest="40"
HeightRequest="40"
VerticalOptions="Center" />
<Label Text="{Binding Title}"
FontSize="Medium"
VerticalOptions="Center"
TextColor="Black"/>
</StackLayout>
<StackLayout Orientation="Vertical" Margin="10" Padding="20,10, 0, 0" IsVisible="{Binding IsExtraControlsVisible}">
<Label Text="Test Definition" FontSize="Medium" VerticalOptions="Center" TextColor="Black"/>
<Label Text="Maptest" FontSize="Medium" VerticalOptions="Center" TextColor="Black"/>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
in my .cs file ItemTapped is like
private void ListViewMenuItems_ItemTapped(object sender, ItemTappedEventArgs e)
{
var item = e.Item as MRPHomeMenuItem;
if (item.Id == 0)
{
item.IsExtraControlsVisible = true;
}
}
When i click on submenu items total cell is calling because of I given stack layout viewcell.How to add these submenu items in separate cells when clicking on Menuitems like this
What you need is a expandable ListView, to achieve this, we need to group the items of our list, means implementing the GroupHeaderTemplate of the ListView.
You may check the blogs here:
Xamarin.Forms Expandable ListView.
Expandable ListView in Xamarin Forms using Grouping.

Xamarin.Forms Add a GestureRecognizer to an Image in a ListView

I am trying to add a tap gesture to an Image within a ListView
The following Image renders correctly in the ListView without the Image.GestureRecognizers section, but with it, the ListView does not render anything at all (no error message). To clarify this, there is also a Label in the ListView and that does not render either.
<Image x:Name="newsImage" VerticalOptions="End" HeightRequest="200" WidthRequest="200" Aspect="AspectFill" Source="{Binding Imageurllarge}">
<Image.GestureRecognizers>
<TapGestureRecognizer
Tapped="OnTapGestureRecognizerTapped"
NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
I took this from - http://developer.xamarin.com/guides/cross-platform/xamarin-forms/working-with/gestures/ (assume this example is for not listview image, but assumed it should work within a listview).
Also (as per comment suggestion)
<Image.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="newsImage" />
Does not seem to fair any better.
If anyone has an example of how to add this in the code behind (without a viewmodel is fine) then that will do.
You can use the DataTemplate in the ListView and inside the DataTemplate have a Grid then add the UI elements. In the given sample, I am showing the Name, contact number and Image, I have used the GestureRecognizers on the Image. Try this:
<ListView x:Name="myListView" ItemsSource="{Binding Contacts}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell Height="75">
<Grid Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="80"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Source="user_img.png" Grid.Column="0" Grid.RowSpan="2" VerticalOptions="CenterAndExpand"/>
<Label Grid.Row="0" Grid.Column="1" Font="16" Text="{Binding DisplayName}" LineBreakMode="TailTruncation"></Label>
<Label Grid.Row="1" Grid.Column="1" Font="12" Text="{Binding Number}" LineBreakMode="TailTruncation"></Label>
<Image Grid.Row="0" Grid.RowSpan="3" Grid.Column="2" Source="add.png" Aspect="AspectFill">
<Image.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding AddCommand}"
CommandParameter="{Binding Number}" />
</Image.GestureRecognizers>
</Image>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have had success with TapGestureRecognizer in uses like this one by specifying it in XAML with its own x:Name attribute, then adding a tap handler in code.
Example markup:
<Image.GestureRecognizers>
<TapGestureRecognizer x:Name="tapImage" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
Then in code something like:
this.tapImage.Tapped += async (object sender, EventArgs e) => {
... // Do whatever is wanted here
}
The handler need not necessarily be marked async, it is just common for my uses that something async is happening in there, like a confirm dialog or scanning a bar code.
You can also attach a gesture recognizer to an image inside a listview. The gesture recognizer can bind to a command in a view model
<ListView x:Name="ExampleList" SeparatorVisibility="None" VerticalOptions="Start" HeightRequest="{Binding HeightRequest}"
HasUnevenRows="True"
CachingStrategy="RecycleElement"
ItemsSource="{Binding FeedItems}"
IsPullToRefreshEnabled="True"
RefreshCommand="{Binding LoadItemsCommand}"
IsRefreshing="{Binding IsBusy, Mode=OneWay}">
<ListView.ItemTemplate >
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<StackLayout Orientation="Vertical">
<Label Text="{Binding TimeAgo}" FontSize="8"></Label>
<StackLayout Orientation="Horizontal">
<Image Source="Accept.png" HeightRequest="30" WidthRequest="45" IsVisible="{Binding IsAccepted, Converter={StaticResource inverse}}">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={StaticResource sampleViewModel}, Path=AcceptCommand}" CommandParameter="{Binding RequestID}" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Resources