How to disable highlight on ListView in Xamarin.Forms - xamarin

I'm working with Xamarin.Forms and XAML, and I'm trying to create an application that stores a list of products. I put my list of products in a ListView. This works fine. Here is my XAML:
<ListView x:Name="listSushi"
ItemsSource="{x:Static local:myListSushi.All}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
RowHeight="{StaticResource rowHeight}"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Padding="5, 5, 0, 5"
Orientation="Horizontal"
Spacing="15">
<StackLayout>
<Image Source="{Binding ImageSource}" />
</StackLayout>
<StackLayout Padding="0, 0, 0, 0"
VerticalOptions="Center"
HorizontalOptions="FillAndExpand">
<Label Text="{Binding Name}"
Font="Bold, Medium" />
<Label Text="{Binding Description}"
Font="Small"/>
</StackLayout>
<StackLayout Orientation="Horizontal"
Padding="0, 0, 10, 0">
<Button Text=" - "
HorizontalOptions="EndAndExpand"
VerticalOptions="FillAndExpand"
Command="{Binding DeleteSushiCommand}"
CommandParameter="{Binding Name}"
/>
<Label VerticalOptions="Center"
Text="{Binding Number,StringFormat='{0}'}"
TextColor="Black"/>
<Button Text=" + "
HorizontalOptions="EndAndExpand"
VerticalOptions="FillAndExpand"
Command="{Binding AddSushiCommand}"
CommandParameter="{Binding Name}"
/>
</StackLayout>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
I've just the problem that if I click on a cell of my listView, the cell is highlight, and stay highlight. I've try to disable it with this code in the xaml.cs
listSushi.ItemSelected+= (object sender, SelectedItemChangedEventArgs e) => {
// don't do anything if we just de-selected the row
if (e.SelectedItem == null) return;
// do something with e.SelectedItem
((ListView)sender).SelectedItem = null; // de-select the row
};
But when I touch a cell, now my list is scrolling automatically. It's very strange.
Does anyone know if this is a bug, or know a fix, like if there is a property where I can disable the highlight?

You might try using the ItemTapped event instead, i.e.
listSushi.ItemTapped += (object sender, ItemTappedEventArgs e) => {
// don't do anything if we just de-selected the row.
if (e.Item == null) return;
// Optionally pause a bit to allow the preselect hint.
Task.Delay(500);
// Deselect the item.
if (sender is ListView lv) lv.SelectedItem = null;
// Do something with the selection.
...
};
I have tested this on a ListView (on an Android device) that has enough items to bring scrolling into the mix. I see no auto-scroll behavior, and your idea to set SelectedItem null to defeat the highlight works great.

Current we can set ListView.SelectionMode to None to do this.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/interactivity

I just found another method to disable the highlight effect. And i would like to share this with other users.
You can do it directly at xaml. But this method not only disable the highlight effect, it will also disable the click event.
You can set the IsEnabled attribute of ViewCell to false.
<ViewCell IsEnabled="false">
//Your Item Layout Coding
</ViewCell>
In addition, you can also disable/enable each item highlight effect by binding:
<ViewCell IsEnabled="{Binding IsHighlightEnabled}">
//Your Item Layout Coding
</ViewCell>
Hope that helps, thanks.

YourList.ItemSelected+=DeselectItem;
public void DeselectItem(object sender, EventArgs e)
{
((ListView)sender).SelectedItem = null;
}
This should be helpful for your scenario. #dpfauwadel

I am assuming you are using MVVM. In these cases I assign a null to the property after using it. In your viewmodel you seem to have a SelectedItem property since you are binding it to the SelectedItem property of the ListView. So I would do something like this:
private Product _selectedItem;
public Product SelectedItem
{
get
{
return _selectedItem;
}
set
{
_selectedItem = value;
//USE THE VALUE
_selectedItem = null;
NotifyPropertyChanged("SelectedItem");
}
}

On iOS mark's solution didn't solve it for me, I had to create a CustomRenderer for the list view and use that instead.
NonSelectableListView.cs (In your Forms Project)
using System;
using Xamarin.Forms;
namespace YourProject
{
public class NonSelectableListView : ListView
{
public NonSelectableListView()
{
}
}
}
NonSelectableListViewRenderer.cs (CustomRenderer in your iOS project)
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using YourProject.iOS;
using YourProject;
[assembly: ExportRenderer(typeof(NonSelectableListView), typeof(NonSelectableListViewRenderer))]
namespace YourProject.iOS
{
public class NonSelectableListViewRenderer : ListViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// Unsubscribe from event handlers and cleanup any resources
}
if (e.NewElement != null)
{
// Configure the native control and subscribe to event handlers
Control.AllowsSelection = false;
}
}
}
}

Related

Xamarin froms Forms horizontal list view with buttons to move the dates to right, left

This is result of what I need to implement. If anyone had contact with something similar ,let me know
StackLayout using BindableLayout inside a ScrollView can be used for your requirement.
XAML code:
<ScrollView
x:Name="calender"
Orientation="Horizontal">
<StackLayout
BackgroundColor="Blue"
BindableLayout.ItemsSource="{Binding Dates}"
Orientation="Horizontal">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Button
TextColor="White"
BackgroundColor="Blue"
Text="{Binding}"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>
Button clicks:
private void LeftButton_Clicked(object sender, EventArgs e)
{
if (calender.ScrollX > 200)
calender.ScrollToAsync(calender.ScrollX - 200, 0, true);
else
calender.ScrollToAsync(0, 0, true);
}
private void RightButton_Clicked(object sender, EventArgs e)
{
if (calender.ScrollX < calender.ContentSize.Width - calender.Width - 200)
calender.ScrollToAsync(calender.ScrollX + 200, 0, true);
else
calender.ScrollToAsync(calender.ContentSize.Width - calender.Width, 0, true);
}
UI result:
Maybe you can use CarouselView or ScrollView(horizontal), take a look in this post
Xamarin Blog, i think you can achieve the best result with Carousel, but, both will work in this situation

How to create listview navigation in shell tab bar using xamarin form cross platform?

Xamarin Forms Cross Platform.
1. I'm using Shell to create Tab Bar, one of the tab bar consist listview.
2. Each cell will navigate to the its details page.
My problem here:
Since I'm using Shell (new feature of xamarin 4.0), I've no idea how to make listview navigation page.
Appreciate those who know using shell navigation to enable my cells in listview able navigate to particular page.
<StackLayout Orientation="Vertical">
<Frame BackgroundColor="DarkOrange" HeightRequest="100" Margin="10,10,10,20" BorderColor="Black" HasShadow="True">
</Frame>
<ListView x:Name="ListView" SeparatorVisibility="Default" SeparatorColor="Red" IsPullToRefreshEnabled="True"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Image Source="{Binding Icon}"/>
<Label Text="{Binding Name}" FontAttributes="Bold" VerticalTextAlignment="Center"
HorizontalOptions="StartAndExpand"/>
<Image Source="{Binding RightArrowIcon}" HorizontalOptions="End"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
async private void ListView_ItemSelected(object sender,
SelectedItemChangedEventArgs e)
{
if(e.SelectedItem != null)
{
var listview = e.SelectedItem as MeListView;
var name = listview.Name.ToString();
if (e.SelectedItem == null)
return;
ListView.SelectedItem = null; //clear the selected item when
back to the listview.
switch (name)
{
case "Payment Methods":
await Navigation.PushAsync(new PaymentPage(listview));
break;
case "Setting":
await Navigation.PushAsync(new SettingPage(listview));
break;
case "FAQ":
await Navigation.PushAsync(new FaqPage(listview));
break;
case "Terms & Conditions":
await Navigation.PushAsync(new TermsPage(listview));
break;
case "About us":
await Navigation.PushAsync(new AboutPage(listview));
break;
case "Contact us":
await Navigation.PushAsync(new ContactPage(listview));
break;
}
}
}
My problem here: Since I'm using Shell (new feature of xamarin 4.0), I've no idea how to make listview navigation page.
First add ItemSelected="OnItemSelected" to ListView in Xaml:
<ListView x:Name="ListView" SeparatorVisibility="Default" SeparatorColor="Red" IsPullToRefreshEnabled="True" ItemSelected="OnItemSelected">
<ListView.ItemTemplate>
//...
</ListView.ItemTemplate>
</ListView>
Then in OnItemSelected func, you can write your navigation function.In shell app, you also can use Navigation.PushAsync to navigat to next page. As follow:
void OnItemSelected(object sender, SelectedItemChangedEventArgs args)
{
var item = args.SelectedItem as Item;
if (item == null)
return;
switch (args.SelectedItemIndex)
{
case 0:
Console.WriteLine("Case 1");
Navigation.PushAsync(new PayMentPage());
break;
case 1:
Console.WriteLine("Case 2");
Navigation.PushAsync(new SettingPage());
break;
//...
default:
Console.WriteLine("Default case");
break;
}
}
In addition, shell app offer new way to navigate ( Shell.Current.GoToAsync ).To do this , you should register page first by Routes. As follow:
Register Page:
Routing.RegisterRoute("setting", typeof(SettingPage));
Perform navigation:
Shell.Current.GoToAsync("setting");
This is the easy way to use shell's navigation , there is other important points about Routes and Navigation .Here is the official document for reference.
===================================Update====================================
If want to navigate to detail page ,and the Tab bar is not visible, you sholud manually do that in Detail Page.As follow:
Shell.SetTabBarIsVisible(this, false);
There is no in-built mechanism for this. But it is fairly simple to implement.
You can either use ItemSelected OR ItemTapped event of ListView as follows:
<ListView x:Name="ListView" SeparatorVisibility="Default" SeparatorColor="Red" IsPullToRefreshEnabled="True" ItemTapped="OnListViewItemTapped">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Image Source="{Binding Icon}"/>
<Label Text="{Binding Name}" FontAttributes="Bold" VerticalTextAlignment="Center"
HorizontalOptions="StartAndExpand"/>
<Image Source="{Binding RightArrowIcon}" HorizontalOptions="End"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In code behind:
async void OnListViewItemTapped(object sender, ItemTappedEventArgs e)
{
await Shell.Current.Navigation.PushAsync(new DetailsPage());
}

Can I use C# code to add a call to a command instead of a <Grid.GestureRecognizers>?

I have this code:
<ViewCell x:Name="co">
<Grid VerticalOptions="CenterAndExpand" Padding="20, 0">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OpenPickerCommand}" CommandParameter="{x:Reference coPicker}" NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>
<Picker x:Name="coPicker" IsVisible="false" HorizontalOptions="End" SelectedIndexChanged="coPickerSelectedIndexChanged" ItemsSource="{Binding Order}"></Picker>
<Label x:Name="coLabel" HorizontalOptions="End"/>
</Grid>
</ViewCell>
Is there a way that I can in C# connect up a command to the tapping of the cell rather than have to use the XAML <Grid.GestureRecognizers> ?
Adding a GestureRecognizer to a ViewCell is a big no-no. A ViewCell exists within a ListView or TableView which have more than enough tapped options of their own. Adding a GestureRecognizer might confuse the OS as to which tap it should handle.
Your options for the GestureRecognizer are basically the following 3, but I advise against them in a scenario where you have a ListView/TableView.
Check out some of the ListView/ViewCell based alternatives I mention below in your situation.
1. GestureRecognizer - Add it in code
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.Tapped += (s, e) => {
// handle the tap
};
myGrid.GestureRecognizers.Add(tapGestureRecognizer);
2. GestureRecognizer - Use a command
When you use MVVM you can also use a command binding in C#:
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.SetBinding (TapGestureRecognizer.CommandProperty, "TapCommand");
myGrid.GestureRecognizers.Add(tapGestureRecognizer);
Which can then be bound in XAML:
<Grid>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapCommand}" />
</Grid.GestureRecognizers>
</Grid>
3. GestureRecognizer - Add it in XAML as you have done
<Grid>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OpenPickerCommand}" CommandParameter="{x:Reference coPicker}" NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>
</Grid>
4. ViewCell - Tapped event
For the ViewCell you have a Tapped event:
<ViewCell Height="100" Tapped="OnTapped">
<ViewCell.View>
<StackLayout BackgroundColor="White" >
</StackLayout>
</ViewCell.View>
</ViewCell>
Which you can implement in code-behind:
void OnTapped (object sender, System.EventArgs e) { //your code}
5. ViewCell - Tapped command
When using MVVM you don't want to put a lot of business logic in the code-behind for your pages. In that case you can use a Behavior to convert the event to a command. A sample of that can be found here:
https://github.com/xamarin/xamarin-forms-samples/tree/master/Behaviors/EventToCommandBehavior/EventToCommandBehavior
6. ListView - ItemSelected
ListView itself also has an ItemSelected event. This can be handled the same way as the ViewCell Tapped event with both an event in code-behind or a Behavior to delegate it to a Command.
7. ListView - SelectedItem property
You can bind the SelectedItem to a property in your view model. On the setter you can perform your custom code.
<ListView
ItemsSource="{Binding YourItems}"
SelectedItem="{Binding YourSelectedItem, Mode=TwoWay}" >
</ListView>
And in code:
string _yourSelectedItem;
public string YourSelectedItem
{
get { return _yourSelectedItem; }
set {
_yourSelectedItem = value;
// Perform your custom functionality
}
}

Xamarin Forms ActivityIndicator UWP Always Running

I have a problem with the ActivityIndicator in a Xamarin UWP project. The indicator is always running. I have to set the property IsVisible to hide the indicator. I want to do a platform specific condition on ActivityIndicator and to set the property IsVisible only when platform is Windows.
This is what I tried:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Views.LoginPage">
<StackLayout Padding="10" Spacing="10">
<Label Text="User" />
<Entry Text="{Binding Email}" Placeholder="User" />
<Label Text="Pass" />
<Entry Text="{Binding Password}" Placeholder="Pass" />
<Button Text="Autentificare" />
<ActivityIndicator IsRunning="{Binding IsBusy}">
<OnPlatform x:TypeArguments="x:Boolean">
<On Platform="Windows" Value="IsVisible">{Binding IsBusy}</On>
</OnPlatform>
</ActivityIndicator>
</StackLayout>
</ContentPage>
I tried to use the OnPlatform property, but I don't know how to do it correctly. Any idea?
I have tested your code and reproduce your issue. You could find the cause from source code.
void UpdateIsRunning()
{
Control.ElementOpacity = Element.IsRunning ? Element.Opacity : 0;
}
The IsRunning property is only a condition for setting the transparency of the
native Control rather than changing Active property for native control . But it does not work as expected. I will report this issue to related team. Currently there is a workaround. You could bind IsBusy to IsVisible and IsRunning just like the following.
<ActivityIndicator IsVisible="{Binding IsBusy}" IsRunning="{Binding IsBusy}"/>
UPDATE
You could create CustomActivityIndicator class that inherit ActivityIndicator. And then implement the custom renderer for it within native client project. For more please refer to the following code.
CustomActivityIndicator.cs
public class CustomActivityIndicator : ActivityIndicator
{
public CustomActivityIndicator()
{
}
}
CustomActivityIndicatorRenderer.cs
[assembly: ExportRenderer(typeof(CustomActivityIndicator), typeof(CustomActivityIndicatorRenderer))]
namespace XamarinActivatorTest.UWP
{
public class CustomActivityIndicatorRenderer : ActivityIndicatorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<ActivityIndicator> e)
{
base.OnElementChanged(e);
if (Control != null)
{
UpdateStatus();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(Element.IsRunning))
{
UpdateStatus();
}
}
private void UpdateStatus()
{
Control.ShowPaused = !Element.IsRunning;
Control.Opacity = Element.IsRunning ? 1 : 0;
}
}
}
You could bind IsRunning property directly. Because the function of IsRunningproperty was changed in your custom renderer.
<StackLayout Padding="10" Spacing="10">
<Button Text="Autentificare" Clicked="Button_Clicked"/>
<local:CustomActivityIndicator IsRunning="{Binding IsBusy}" >
</local:CustomActivityIndicator>
</StackLayout>
I have uploaded the code sample to git hub.

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");

Resources