How can I stop a click on the ViewCell from changing the background color for a short time?

I have this XAML code:

<TableView x:Name="tableView" Intent="Settings" HasUnevenRows="True"> <TableSection> <TableSection.Title> Card Selection </TableSection.Title> <ViewCell Height="50"> <Grid> <Grid x:Name="deselectGridLink" VerticalOptions="CenterAndExpand" Padding="20, 0"> <Label TextColor="Blue" Style="{DynamicResource ListItemTextStyle}" x:Name="deselectLink" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All" /> </Grid> <Grid x:Name="deselectGridLabel" VerticalOptions="CenterAndExpand" Padding="20, 0"> <Label TextColor="Silver" Style="{DynamicResource ListItemTextStyle}" x:Name="deselectLabel" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All" /> </Grid> </Grid> </ViewCell> <ViewCell Height="50"> <Grid x:Name="selectGridLink" VerticalOptions="CenterAndExpand" Padding="20, 0"> <Label TextColor="Blue" Style="{DynamicResource ListItemTextStyle}" x:Name="selectLink" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Select All" /> </Grid> </ViewCell> </TableSection> </TableView> 

When the other parts of my code call are: SetPageDetails() , then the label in the grid changes to a link or the link changes to a label. Therefore, for this, when this is a shortcut, I would not want to have flash background events and no action.

I am attaching a gesture recognizer like this. Note that all this is on one line, but spans two lines, so it is more noticeable here in the SO question:

 deselectGridLink.GestureRecognizers .Add(NewTapGestureForUpdateCategories(false)); private TapGestureRecognizer NewTapGestureForUpdateCategories(bool val) { return new TapGestureRecognizer() { Command = new Command(() => { App.DB.UpdateAllCategoryGroups(val); App.DB.UpdateAllCategories(val); GetPageData(); RemoveTableViewClickSection(); tableView.Root.Add(CreateTableSection()); }) }; } 

When the user clicks on a row when the deselectGridLink grid is down, then:

  • DeselectGridLink set to false
  • The deselectGridLabel mapping is true

     private void SetPageDetails() { Title = App.cardCountForSelectedCategories + (App.cardCountForSelectedCategories == 1 ? " Card Selected" : " Cards Selected"); if (App.cardCountForSelectedCategories == 0) { deselectGridLink.IsVisible = false; deselectGridLabel.IsVisible = true; } else { deselectGridLink.IsVisible = true; deselectGridLabel.IsVisible = false; } } 

The effect of this is that the text of the mesh link changes to silver, and the link becomes a label.

Nevertheless, despite the fact that when a label is clicked, the label is grayed out, when the label is clicked, there is still a brief change in the background color, from white to dark. I guess this is just the way the view cell works.

Is there any way to suppress this from happening?

+5
source share
1 answer

EDIT 1 . Updated answer in accordance with updates to the question. those. Add support for switching between backlight on / off.

EDIT 2 - restructure your response and add more details.

Option-1: enable / disable view cells through IsEnabled

The simplest option would be to use the IsEnabled property, which in turn enables / disables background flash behavior. The only drawback of this approach is that it will also disable the taps of the child controls, i.e. Event / Gesture Recognizer events will not be fired if the parent view of the IsEnabled cell is false .

For instance:

Xaml

 <!-- Add name attribute to view-cell --> <ViewCell x:Name="deselectCell" ..> <Grid> <Grid x:Name="deselectGridLink" .. .... </ViewCell> 

Code for

 private void SetPageDetails() { if (App.cardCountForSelectedCategories == 0) { deselectCell.IsEnabled = false; //disable background flash ... } else { deselectCell.IsEnabled = true; ... } } 

Recommendation 1 - Use data binding and triggers

Instead of controlling the visibility for each label in the code, you can use triggers and data binding as follows (the view model will have the IsDeselectEnabled property):

 <ViewCell IsEnabled="{Binding IsDeselectEnabled}" Height="50"> <Label Margin="20,0,20,0" Style="{DynamicResource ListItemTextStyle}" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All"> <Label.Triggers> <DataTrigger TargetType="Label" Binding="{Binding IsDeselectEnabled}" Value="true"> <Setter Property="TextColor" Value="Blue" /> </DataTrigger> <DataTrigger TargetType="Label" Binding="{Binding IsDeselectEnabled}" Value="false"> <Setter Property="TextColor" Value="Silver" /> </DataTrigger> </Label.Triggers> </Label> </ViewCell> 

Recommendation 2 - Use triggers with a view as a source

 <ViewCell x:Name="deselectCell" Height="50"> <Label Margin="20,0,20,0" Style="{DynamicResource ListItemTextStyle}" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All"> <Label.Triggers> <DataTrigger TargetType="Label" Binding="{Binding IsEnabled, Source={x:Reference deselectCell}}" Value="true"> <Setter Property="TextColor" Value="Blue" /> </DataTrigger> <DataTrigger TargetType="Label" Binding="{Binding IsEnabled, Source={x:Reference deselectCell}}" Value="false"> <Setter Property="TextColor" Value="Silver" /> </DataTrigger> </Label.Triggers> </Label> </ViewCell> 

Option-2: Turn on / off the backlight, but allow taps

To enable taps when switching ViewCell background-highlight behavior, we will need to implement platform rendering tools.

In the case of iOS, we can use SelectionStyle to switch this behavior, while in the case of android, we can use Clickable .

General control:

 public class CustomViewCell : ViewCell { public static readonly BindableProperty AllowHighlightProperty = BindableProperty.Create( "AllowHighlight", typeof(bool), typeof(CustomViewCell), defaultValue: true); public bool AllowHighlight { get { return (bool)GetValue(AllowHighlightProperty); } set { SetValue(AllowHighlightProperty, value); } } } 

iOS renderer:

 [assembly: ExportRenderer(typeof(CustomViewCell), typeof(CustomViewCellRenderer))] namespace SampleApp.iOS { public class CustomViewCellRenderer : ViewCellRenderer { UITableViewCell _nativeCell; //get access to the associated forms-element and subscribe to property-changed public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv) { _nativeCell = base.GetCell(item, reusableCell, tv); var formsCell = item as CustomViewCell; if (formsCell != null) { formsCell.PropertyChanged -= OnPropertyChanged; formsCell.PropertyChanged += OnPropertyChanged; } //and, update the style SetStyle(formsCell); return _nativeCell; } void OnPropertyChanged(object sender, PropertyChangedEventArgs e) { var formsCell = sender as CustomViewCell; if (formsCell == null) return; //TODO: Trying to find a nicer and more robust way to dispose and unsubscribe :( if (_nativeCell == null) formsCell.PropertyChanged -= OnPropertyChanged; if (e.PropertyName == CustomViewCell.AllowHighlightProperty.PropertyName) { SetStyle(formsCell); } } private void SetStyle(CustomViewCell formsCell) { //added this code as sometimes on tap, the separator disappears, if style is updated before tap animation finishes //https://stackoverflow.com/questions/25613117/how-do-you-prevent-uitableviewcellselectionstylenone-from-removing-cell-separato Device.StartTimer(TimeSpan.FromMilliseconds(50), () => { Device.BeginInvokeOnMainThread(() => { if (formsCell.AllowHighlight) _nativeCell.SelectionStyle = UITableViewCellSelectionStyle.Default; else _nativeCell.SelectionStyle = UITableViewCellSelectionStyle.None; }); return false; }); } } } 

Android rendering tool:

 [assembly: ExportRenderer(typeof(CustomViewCell), typeof(CustomViewCellRenderer))] namespace SampleApp.Droid { public class CustomViewCellRenderer : ViewCellRenderer { Android.Views.View _nativeCell; protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, Android.Views.ViewGroup parent, Android.Content.Context context) { _nativeCell = base.GetCellCore(item, convertView, parent, context); SetStyle(); return _nativeCell; } // this one is simpler as the base class has a nice override-able method for our purpose - so we don't need to subscribe protected override void OnCellPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { base.OnCellPropertyChanged(sender, e); if(e.PropertyName == CustomViewCell.AllowHighlightProperty.PropertyName) { SetStyle(); } } private void SetStyle() { var formsCell = Cell as CustomViewCell; if (formsCell == null) return; _nativeCell.Clickable = !formsCell.AllowHighlight; } } } 

Case Study 1 - Through Data Binding

 <local:CustomViewCell AllowHighlight="{Binding IsHighlightEnabled}" ..> <Grid> <Grid x:Name="deselectGridLink" .. ... </local:CustomViewCell> 

Case Study 2 - Using Code

Xaml

 <!-- Add name attribute to view-cell --> <local:CustomViewCell x:Name="deselectCell" ..> <Grid> <Grid x:Name="deselectGridLink" .. ... </local:CustomViewCell> 

Code for

 private void SetPageDetails() { if (App.cardCountForSelectedCategories == 0) { deselectCell.AllowHighlight= false; //disable background flash ... } else { deselectCell.AllowHighlight= true; ... } } 

Option-3: Disable selection, selection for all elements

This is especially true for ListView . The updated question now indicates that the cells are part of the TableView , so this parameter is no longer valid in the current context of the question.

You will need to implement platform rendering tools to turn off highlighted colors, and add an ItemTapped handler to the ListView to turn off the selection, setting SelectedItem to null always. Used links:

code

To get started, create a custom view cell:

 public class NoSelectViewCell : ViewCell { } 

Implementing the iOS renderer as:

 [assembly: ExportRenderer(typeof(NoSelectViewCell), typeof(NoSelectViewCellRenderer))] namespace SampleApp.iOS { public class NoSelectViewCellRenderer : ViewCellRenderer { public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv) { var nativeCell = base.GetCell(item, reusableCell, tv); nativeCell.SelectionStyle = UITableViewCellSelectionStyle.None; return nativeCell; } } } 

Implementing a renderer for Android as:

 [assembly: ExportRenderer(typeof(NoSelectViewCell), typeof(NoSelectViewCellRenderer))] namespace SampleApp.Droid { public class NoSelectViewCellRenderer : ViewCellRenderer { protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, Android.Views.ViewGroup parent, Android.Content.Context context) { var cell = base.GetCellCore(item, convertView, parent, context); cell.Focusable = false; cell.FocusableInTouchMode = false; var listView = parent as Android.Widget.ListView; if (listView != null) { listView.SetSelector(Android.Resource.Color.Transparent); listView.CacheColorHint = Xamarin.Forms.Color.Transparent.ToAndroid(); } return cell; } } } 

Sample Usage:

Xaml

 <ListView ItemTapped="Handle_ItemTapped"> <ListView.ItemTemplate> <DataTemplate> <local:NoSelectViewCell Height="50"> <Grid> <Grid x:Name="deselectGridLink" VerticalOptions="CenterAndExpand" Padding="20, 0"> <Label TextColor="Blue" Style="{DynamicResource ListItemTextStyle}" x:Name="deselectLink" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All" /> </Grid> <Grid x:Name="deselectGridLabel" VerticalOptions="CenterAndExpand" Padding="20, 0"> <Label TextColor="Silver" Style="{DynamicResource ListItemTextStyle}" x:Name="deselectLabel" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All" /> </Grid> </Grid> </local:NoSelectViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> 

Code for

 void Handle_ItemTapped(object sender, Xamarin.Forms.ItemTappedEventArgs e) { // don't do anything if we just de-selected the row if (e.Item == null) return; // do something with e.SelectedItem ((ListView)sender).SelectedItem = null; // de-select the row } 
+6
source

Source: https://habr.com/ru/post/1271872/


All Articles