Xamarin custom renderer System.ArgumentException when runs - android

I've created a rendered picker's to center center in the middle of the control but at the moment of entering the page, it shows the following error: System.ArgumentException.
I attach the code to see if you can help me.
In project new class...
using Xamarin.Forms;
namespace MaterialFUR
{
public class PickerCentrat : Picker
{
}
}
In Android ...
using MaterialFUR;
using MaterialFUR.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(PickerCentrat), typeof(RendererPicker))]
namespace MaterialFUR.Droid
{
public class RendererPicker : EntryRenderer
{
public RendererPicker(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Gravity = GravityFlags.CenterHorizontal;
}
}
}
}
In XAML page...
<?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="MaterialFUR.AgafarMaterialPage"
xmlns:local="clr-namespace:MaterialFUR"
Title="Agafar Material">
<ContentPage.Content>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center" Spacing="25">
<Button x:Name="btnScan" Clicked="btnScan_Clicked" BackgroundColor="Gray" TextColor="White" Text="Llegir codi de barres" FontSize="30" FontAttributes="Bold" BorderRadius="30"/>
<Entry x:Name="entCodi" TextColor="Black" FontSize="30" FontAttributes="Bold" Keyboard="Default" Placeholder="Codi" HorizontalTextAlignment="Center"/>
<Entry x:Name="entQtat" TextColor="Black" FontSize="30" FontAttributes="Bold" Keyboard="Numeric" Placeholder="Quantitat" HorizontalTextAlignment="Center"/>
<local:PickerCentrat x:Name="pckUbicacio" Title="Ubicació" TextColor="Black" FontSize="30" FontAttributes="Bold">
<Picker.Items>
<x:String>SATMOBIL-1</x:String>
<x:String>SATMOBIL-2</x:String>
<x:String>SATMOBIL-3</x:String>
<x:String>SATMOBIL-4</x:String>
<x:String>SATMOBIL-5</x:String>
<x:String>SATMOBIL-6</x:String>
<x:String>SATMOBIL-7</x:String>
<x:String>SATMOBIL-8</x:String>
<x:String>SATMOBIL-9</x:String>
<x:String>TALLER</x:String>
</Picker.Items>
</local:PickerCentrat>
<StackLayout Orientation="Horizontal" Margin="0,100">
<Button x:Name="btnConfirmar" Clicked="BtnConfirmar_Clicked" BackgroundColor="SteelBlue" TextColor="White" Text="Confirmar" FontSize="30" FontAttributes="Bold" BorderRadius="30"/>
<Button x:Name="btnCancelar" Clicked="BtnCancelar_Clicked" BackgroundColor="Gray" TextColor="White" Text="Cancelar" FontSize="30" FontAttributes="Bold" BorderRadius="30"/>
</StackLayout>
</StackLayout>
</ContentPage.Content>
Error displayed.
I appreciate your help, thanks!

Your class needs to Inherit from PickerRenderer
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(PickerCentrat), typeof(RendererPicker))]
namespace MaterialFUR.Droid
{
public class RendererPicker : PickerRenderer
{
public RendererPicker(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Gravity = GravityFlags.CenterHorizontal;
}
}
}
}
When in doubt about which class you need to Inherit from for your CustomRenderer, you can always check on the Xamarin.Forms source code here (for Android).
Hope this helps.

Related

Xamarin AdvertisingIdClient.GetAdvertisingIdInfo Timeout

I'm working on a Xamarin.Form App (Android and iOs) which need to get AAID and IDFA.
I started with Android part, I implemented AdvertisingIdClient.GetAdvertisingIdInfo but this function return Timeout error (about after 10s)
I use DependencyService from Form to get AAID from Android project and I compile with Android 11.0
I followed internet examples to achieve it but I'm quite sure I missed something or did something wrong, but what?
What I did :
Added nuget Xamarin.GooglePlayServices.Ads (120.3.0.3) and Xamarin.GooglePlayServices.Basement (117.6.0.4), should be latest compliant with Android 11
Created Interface for dependency service and Created cs in Android project
In Form I call my Dependencyservice function using Task.Run to run i in background (not sure it's the rigth way, but function run, otherwise function raise an error)
IDeviceAdIdentifier service = DependencyService.Get<IDeviceAdIdentifier>();
var tadid = Task.Run(() =>
{
var t = service.GetAdvertisingIdentifier();
t.Wait();
return t.Result;
});
tadid.Wait();
Idfa = tadid.Result;
In Android I save Context from MainActivity
In Android Service I have :
[assembly: Dependency(typeof(TestAdId.Droid.Services.DeviceAdIdentifier))]
namespace TestAdId.Droid.Services
{
public class DeviceAdIdentifier : TestAdId.Services.IDeviceAdIdentifier
{
public async Task<string> GetAdvertisingIdentifier()
{
if (MainActivity.Context == null) return "";
string id = "";
try
{
AdvertisingIdClient.SetShouldSkipGmsCoreVersionCheck(true);
var adinfo = AdvertisingIdClient.GetAdvertisingIdInfo(MainActivity.Instance); // Also tested with MainActivity.Context which is Application context saved during init
if (adinfo != null)
return adinfo.Id;
}
catch (IOException ioerr)
{
return ioerr.Message;
}
catch (Exception err)
{
return err.Message;
}
return "";
}
}
}
In my manifest :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.testadid" android:installLocation="auto">
<uses-sdk android:minSdkVersion="25" android:targetSdkVersion="30" />
<application android:label="TestAdId.Android" android:theme="#style/MainTheme" android:icon="#drawable/xamarin_logo">
<meta-data android:name="com.google.android.gms.ads.AD_MANAGER_APP" android:value="true" />
<meta-data android:name="com.google.android.gms.version" android:value="#integer/google_play_services_version" />
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
The error raised on my Samsung Galaxy A71 is :
Message : java.util.concurrent.TimeoutException: Timed out waiting for the service connection
StackTrace :
at Java.Interop.JniPeerMembers+JniStaticMethods.InvokeObjectMethod (System.String encodedMember, Java.Interop.JniArgumentValue* parameters) [0x00018] in <1921523bc22e407fa9a0855bdae29335>:0
at Google.Ads.Identifier.AdvertisingIdClient.GetAdvertisingIdInfo (Android.Content.Context context) [0x00027] in /Users/runner/work/1/s/generated/com.google.android.gms.play-services-ads-identifier/obj/Release/monoandroid90/generated/src/Google.Ads.Identifier.AdvertisingIdClient.cs:171
at TestAdId.Droid.Services.DeviceAdIdentifier.GetAdvertisingIdentifier () [0x00032] in C:\Working\Digital\Sources\TestAdId\TestAdId\TestAdId.Android\Services\DeviceAdIdentifier.cs:30
--- End of managed Java.IO.IOException stack trace ---
java.io.IOException: java.util.concurrent.TimeoutException: Timed out waiting for the service connection
at com.google.android.gms.ads.identifier.AdvertisingIdClient.zzb(com.google.android.gms:play-services-ads-identifier##17.0.1:16)
at com.google.android.gms.ads.identifier.AdvertisingIdClient.getAdvertisingIdInfo(com.google.android.gms:play-services-ads-identifier##17.0.1:3)
Caused by: java.util.concurrent.TimeoutException: Timed out waiting for the service connection
at com.google.android.gms.common.BlockingServiceConnection.getServiceWithTimeout(com.google.android.gms:play-services-basement##17.6.0:4)
at com.google.android.gms.ads.identifier.AdvertisingIdClient.zzb(com.google.android.gms:play-services-ads-identifier##17.0.1:14)
... 1 more
I tested an application to retrieve AAID and it works on my phone.
So if someone can help me it will be great. Tell me if you need more information/code
Thanks
Hervé
It's just a POC so nothing really special in MainActivity. I created a standard xamarin.Forms project and put my code in about page in a button
Android MainActivity.cs
using System;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.OS;
using Android.Content;
namespace TestAdId.Droid
{
[Activity(Label = "TestAdId", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static Context Context =null;
public static MainActivity Instance = null;
protected override void OnCreate(Bundle savedInstanceState)
{
Instance = this;
base.OnCreate(savedInstanceState);
Context = Android.App.Application.Context;
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
The About Screen 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"
x:Class="TestAdId.Views.AboutPage"
xmlns:vm="clr-namespace:TestAdId.ViewModels"
Title="{Binding Title}">
<ContentPage.BindingContext>
<vm:AboutViewModel />
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<Color x:Key="Accent">#96d1ff</Color>
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackLayout BackgroundColor="{StaticResource Accent}" VerticalOptions="FillAndExpand" HorizontalOptions="Fill">
<StackLayout Orientation="Horizontal" HorizontalOptions="Center" VerticalOptions="Center">
<ContentView Padding="0,40,0,40" VerticalOptions="FillAndExpand">
<Image Source="xamarin_logo.png" VerticalOptions="Center" HeightRequest="64" />
</ContentView>
</StackLayout>
</StackLayout>
<ScrollView Grid.Row="1">
<StackLayout Orientation="Vertical" Padding="30,24,30,24" Spacing="10">
<Label Text="Start developing now" FontSize="Title"/>
<Label Text="Make changes to your XAML file and save to see your UI update in the running app with XAML Hot Reload. Give it a try!" FontSize="16" Padding="0,0,0,0"/>
<Label FontSize="16" Padding="0,24,0,0">
<Label.FormattedText>
<FormattedString>
<FormattedString.Spans>
<Span Text="Learn more at "/>
<Span Text="https://aka.ms/xamarin-quickstart" FontAttributes="Bold"/>
</FormattedString.Spans>
</FormattedString>
</Label.FormattedText>
</Label>
<Label x:Name="IdfaLabel" Text="Unknown" TextColor="Black" FontSize="16" ></Label>
<Button Margin="0,10,0,0" Text="Get IDFA"
Clicked="Button_Clicked"
BackgroundColor="{StaticResource Primary}"
TextColor="White" />
</StackLayout>
</ScrollView>
</Grid>
</ContentPage>
The About Form Code
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using TestAdId.Services;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using System.Net;
namespace TestAdId.Views
{
public partial class AboutPage : ContentPage
{
public string Idfa = "";
public AboutPage()
{
InitializeComponent();
}
private void Button_Clicked(object sender, EventArgs e)
{
IdfaLabel.Text = "Getting IDFA...";
IDeviceAdIdentifier service = DependencyService.Get<IDeviceAdIdentifier>();
var tadid = Task.Run(() =>
{
var t = service.GetAdvertisingIdentifier();
t.Wait();
return t.Result;
});
tadid.Wait();
Idfa = tadid.Result;
IdfaLabel.Text = Idfa;
}
}
}```
Tell me if you need another thing. This is a really simple sample just to try to making AAID working
Thanks
Regards
Hervé

Xamarin.Android Camera Permissions with ZXing.Net.Mobile only works after app restart

I am using ZXing.Net.Mobile in a Xamarin.Forms project. It works fine on iOS, but on Android, the first time I use the scanner, I get the camera permissions popup, and grant them.
There is no image from the camera immediately after the permissions. If I quit the app and re-launch, the camera works fine from that point forward. How can I get it to work right away? Even if I close my view, dispose and re-initialize it, it still doesn't work until the app restarts.
Here is relevant code:
public partial class CEUScanModalPage : ContentPage, INotifyPropertyChanged {
private bool _isScanning;
public CEUScanModalPage() {
InitializeComponent();
IsScanning = false;
BindingContext = this;
Indicator.Start();
}
public bool IsScanning {
get {
return _isScanning;
}
set {
_isScanning = value;
OnPropertyChanged();
}
}
private async void OnCloseButtonClicked(object sender, EventArgs args) {
await Navigation.PopModalAsync();
}
protected override void OnAppearing() {
base.OnAppearing();
IsScanning = true;
}
protected override void OnDisappearing() {
base.OnDisappearing();
_scanView.OnScanResult -= Handle_OnScanResult;
_scanView.IsAnalyzing = false;
_scanView.IsScanning = false;
ParentGrid.Children.Remove(_scanView);
_scanView = null;
}
public async void Handle_OnScanResult(Result result) {
IsScanning = false;
}
}
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:local="clr-namespace:BehaviorLive" xmlns:grial="clr-namespace:UXDivers.Grial;assembly=UXDivers.Grial" xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms" xmlns:fftransformations="clr-namespace:FFImageLoading.Transformations;assembly=FFImageLoading.Transformations" xmlns:zxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"
x:Class="BehaviorLive.Views.CEUScanModalPage"
NavigationPage.HasNavigationBar="False"
Style="{ StaticResource MainMenuOrModalBackgroundStyle }">
<ContentPage.Resources>
<ResourceDictionary
Source="/Styles/TabControl/SimpleTabResources.xaml" />
</ContentPage.Resources>
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition
Height="145" />
<RowDefinition
Height="*" />
</Grid.RowDefinitions>
<Grid
RowSpacing="0"
ColumnSpacing="0"
Margin="0,0,0,10"
VerticalOptions="EndAndExpand">
<Grid.RowDefinitions>
<RowDefinition
Height="10" />
<RowDefinition
Height="Auto" />
<RowDefinition
Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="120" />
<ColumnDefinition
Width="*" />
</Grid.ColumnDefinitions>
<!-- LOGO -->
<Label
Grid.Row="0"
Grid.RowSpan="3"
Margin="20,0"
TextColor="{ DynamicResource InverseTextColor }"
FontSize="74"
FontFamily="{StaticResource FontAwesomeSolid}"
Text="{ x:Static local:FontawesomeSolidFont.Qrcode }"
Style="{ StaticResource FontIcon }"
HorizontalOptions="Start"
VerticalTextAlignment="Center" />
<!-- WELCOME -->
<Label
Grid.Row="1"
Grid.Column="1"
Text="CEU Checkin"
FontSize="22"
TextColor="{ DynamicResource InverseTextColor }"
Style="{ StaticResource LabelBoldStyle }"
VerticalOptions="End"
HorizontalOptions="Start" />
<!-- SUB -->
<Label
Grid.Row="2"
Grid.Column="1"
Text="Scan QR Code to check in/out of your event"
FontSize="16"
TextColor="{ DynamicResource InverseTextColor }"
VerticalOptions="Start"
HorizontalOptions="Start" />
</Grid>
<Grid
Grid.Row="1"
BackgroundColor="{ DynamicResource MainWrapperBackgroundColor }"
grial:Effects.ApplyIOSSafeAreaAsPadding="Bottom" x:Name="ParentGrid">
<!-- TAB -->
<StackLayout x:Name="LoadingIndicator" Grid.Row="0" VerticalOptions="CenterAndExpand">
<local:CustomActivityIndicator
Grid.Row="0"
x:Name="Indicator"
HorizontalOptions="Center"
VerticalOptions="Start" />
<Label
Grid.Row="1"
FontSize="Large"
HorizontalTextAlignment="Center"
Text="{ grial:Translate StringLoading }" />
</StackLayout>
<zxing:ZXingScannerView Grid.Row="0" Grid.RowSpan="2" x:Name="_scanView" OnScanResult="Handle_OnScanResult" IsScanning="{Binding IsScanning}"
WidthRequest="200" HeightRequest="200" />
</Grid>
<!-- CLOSE ICON -->
<Label
Style="{StaticResource IconCloseLabelStyle}">
<Label.GestureRecognizers>
<TapGestureRecognizer
Tapped="OnCloseButtonClicked" />
</Label.GestureRecognizers>
</Label>
</Grid>
</ContentPage.Content>
</ContentPage>
MainActivity.cs
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults) {
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
The best way to fix the issue would be to handle the permission part yourself
A generic permission helper:
using System.Threading.Tasks;
using Xamarin.Essentials;
using static Xamarin.Essentials.Permissions;
public static async Task<PermissionStatus> CheckAndRequestPermissionAsync<TPermission>()
where TPermission : BasePermission, new()
{
TPermission permission = new TPermission();
var status = await permission.CheckStatusAsync();
if (status != PermissionStatus.Granted)
{
status = await permission.RequestAsync();
}
return status;
}
Then use it like
var status = await PermissionsHelper.CheckAndRequestPermissionAsync<Permissions.Camera>();
if(status != PermissionStatus.Granted)
{
return;
}

Is it possible to show EmptyView after loader(ActivityIndicator) in CarouselView using Xamarin forms?

I have a lot of tried to show EmptyView after Loader but did not get success might i missed some code configuration for the EmptyView and Loader.
I'm using ActivityIndicator loader for the loading view if it will hide once data will come.
Below code is for EmptyView for the CarouselView
<CarouselView.EmptyView>
<Frame BackgroundColor="Transparent" Margin="20,150">
<Frame BackgroundColor="White" CornerRadius="20" HeightRequest="150" WidthRequest="300" HorizontalOptions="Center" VerticalOptions="Center">
<Label Text="Data not Found" FontSize="Medium" HorizontalOptions="Center" VerticalOptions="Center"/>
</Frame>
</Frame>
</CarouselView.EmptyView>
Below code is for ActivityIndicator
<StackLayout IsVisible="{Binding IsBusy}" Padding="12" AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutBounds="0.5,0.5,-1,-1">
<ActivityIndicator IsRunning="{Binding IsBusy}" Color="#BFA464" x:Name="PaymentLoader"/>
<Label Text="Data is Loading..." HorizontalOptions="Center" TextColor="#BFA464"/>
</StackLayout>
Below image is my whole code screenshot
here is full code image
If anybody have idea, please share your great and valuable knowledge.
According to your description and code, I guess that you want to display CarouselView's EmptyView and ActivityIndicator when no data found, am I right?
From Xamarin.Forms CarouselView EmptyView, we can see that EmptyView, of type object, the string, binding, or view that will be displayed when the ItemsSource property is null, or when the collection specified by the ItemsSource property is null or empty. The default value is null.
I do one sample that you can take a look:
<StackLayout>
<!-- Place new controls here -->
<CarouselView ItemsSource="{Binding models}">
<CarouselView.EmptyView>
<Frame Margin="20,150" BackgroundColor="Transparent">
<Frame
BackgroundColor="White"
CornerRadius="20"
HeightRequest="150"
HorizontalOptions="Center"
VerticalOptions="Center"
WidthRequest="300">
<Label
FontSize="Medium"
HorizontalOptions="Center"
Text="Data not Found"
VerticalOptions="Center" />
</Frame>
</Frame>
</CarouselView.EmptyView>
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Frame
Margin="8"
BorderColor="Gray"
CornerRadius="20"
HasShadow="True"
HeightRequest="250"
VerticalOptions="CenterAndExpand"
WidthRequest="300">
<StackLayout>
<Image Source="{Binding ImagePath}" />
<Label
FontAttributes="Bold"
FontSize="24"
HorizontalTextAlignment="Center"
Text="{Binding Title}" />
</StackLayout>
</Frame>
</StackLayout>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<StackLayout
Padding="12"
AbsoluteLayout.LayoutBounds="0.5,0.5,-1,-1"
AbsoluteLayout.LayoutFlags="PositionProportional"
IsVisible="{Binding IsBusy}">
<ActivityIndicator
x:Name="PaymentLoader"
IsRunning="{Binding IsBusy}"
Color="#BFA464" />
<Label
HorizontalOptions="Center"
Text="Data is Loading..."
TextColor="#BFA464" />
</StackLayout>
</StackLayout>
public partial class MainPage : ContentPage, INotifyPropertyChanged
{
public ObservableCollection<BirdsModel> models { get; set; }
private Boolean _IsBusy;
public Boolean IsBusy
{
get { return _IsBusy; }
set
{
_IsBusy = value;
RaisePropertyChanged("IsBusy");
}
}
public MainPage()
{
InitializeComponent();
IsBusy = true;
models = new ObservableCollection<BirdsModel>();
this.BindingContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected override async void OnAppearing()
{
base.OnAppearing();
await Task.Delay(2000);
models.Add(new BirdsModel() { Title = "title 1", ImagePath = "a5.jpg" });
models.Add(new BirdsModel() { Title = "title 2", ImagePath = "a6.jpg" });
models.Add(new BirdsModel() { Title = "title 3", ImagePath = "a7.jpg" });
models.Add(new BirdsModel() { Title = "title 4", ImagePath = "a8.jpg" });
IsBusy = false;
}
}
public class BirdsModel
{
public string Title { get; set; }
public ImageSource ImagePath { get; set; }
}
This is the sample at github, you can take a look:
https://github.com/CherryBu/CarouselViewSample

How to remove "horizontal scroll bar" of one or more entry in collectionview?

I have updated a Xamarin Forms proyect to 4.2 and I'm usign a CollectionView with an entry for each data template. The "problem" is that when I tap on a entry I get a weird little underline under all the entries in the list.
Honestly I don't know from where this underline is come from!
Here you can see a little grey underline under the "00"
EDIT:
I have discovered that is a small horizontal scroll bar, here I have created another collection view (yeah, the binding is totally random, but I want a fast example) and I have set HorizontalScrollBarVisibility="Never" but it is still showed (it's a CollectionView property not entry...).
Code:
<StackLayout>
<CollectionView x:Name="ItemsListView"
ItemsSource="{Binding Items}"
VerticalOptions="FillAndExpand"
HorizontalScrollBarVisibility="Never"
ItemsUpdatingScrollMode="KeepScrollOffset">
<d:CollectionView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>First Item</x:String>
<x:String>Second Item</x:String>
<x:String>Third Item</x:String>
<x:String>Fourth Item</x:String>
<x:String>Fifth Item</x:String>
<x:String>Sixth Item</x:String>
</x:Array>
</d:CollectionView.ItemsSource>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<StackLayout Orientation="Horizontal"
BackgroundColor="AntiqueWhite"
Padding="10"
Margin="10">
<Entry WidthRequest="50" />
<Entry WidthRequest="50" />
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
There is a way to remove it? or at least make it appears only in the entry selected?
P.S. I think I haven't mentioned it, it appears only for some second (when tap on an entry) and then disappears.
you could use custom render to custom the entry,for example:
custom an Entry MyEntry :
public class MyEntry :Entry
{
}
then in Android project,create a entry render AndroidEntry :
[assembly: ExportRenderer(typeof(MyEntry), typeof(AndroidEntry))]
namespace App18.Droid
{
class AndroidEntry:EntryRenderer
{
public AndroidEntry(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control !=null)
{
Control.SetSingleLine(true);
Control.HorizontalScrollBarEnabled = false;
}
}
}
}
then in axml use your custom entry :
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<StackLayout Orientation="Horizontal"
BackgroundColor="AntiqueWhite"
Padding="10"
Margin="10">
<local:MyEntry WidthRequest="50" />
<local:MyEntry WidthRequest="50" />
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
Leo's solution is great, but it is an overuse of CustomRenderers in my opinion. This is not something you use a CustomRenderer for.
An Effect is more than enough to achieve what you want - and is reusable across other types.
Create a NoHorizontalScrollbarEffect.cs file in your shared project:
namespace YourAppName.Effects
{
public class NoHorizontalScrollbarEffect : RoutingEffect
{
public NoHorizontalScrollbarEffect() : base($"MyApp.{nameof(NoHorizontalScrollbarEffect)}")
{
}
}
}
Note that "MyApp" above and below need to match but it can be any kind of text.
Now add NoHorizontalScrollbarEffect_Android.cs in the corresponding platform-specific project:
[assembly: ResolutionGroupName("MyApp")]
[assembly: ExportEffect(typeof(NoHorizontalScrollbarEffect), nameof(NoHorizontalScrollbarEffect))]
namespace YourAppName.Android.Effects
{
public class NoHorizontalScrollbarEffect : PlatformEffect
{
protected override void OnAttached()
{
Android.Views.View view = this.Control ?? this.Container //Control is null for Layouts
view.HorizontalScrollBarEnabled = false;
}
protected override void OnDetached()
{
}
}
}
And then you can use this effect on any XAML element deriving from View (including Entry) after adding the xmlns:effects="clr-namespace:YourAppName.Effects" (the namespace of the shared project's Effect class) at the top of the page:
...
<AnyControl ...>
...
<AnyControl.Effects>
<effects:NoHorizontalScrollbarEffect />
</AnyControl.Effects>
</AnyControl>
...

Tap Gesture Gesture Recognizer in ListView not working

I have this in my ViewModel:
public class MyClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int taps = 0;
ICommand tapCommand;
public MyClass()
{
tapCommand = new Command(OnTapped);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
void OnTapped(object s)
{
taps++;
Debug.WriteLine("parameter: " + s);
}
}
and this in the xaml:
<Image Source="delete.jpg" HeightRequest="20" WidthRequest="20">
<Image.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="Image1" />
</Image.GestureRecognizers>
</Image>
but when the image is clicked nothing appears in output log. What I'm missing ?
Note 1: I've followed the guide here
Note 2: I'm debugging on Android Device
Update 1: full xaml here
You have not provided any code behind, so I had to create my own (see below).
You also would save me 15 minutes if commented out ListView IsEnabled="False"
When you set Command="{Binding TapCommand} the TapCommand corresponds to the TapCommand of the list item, not to the model itself. So, you have 2 options
You can implement click in MyItem (I don't think you want that, so partial code for it commented below in MyItem class)
Define context binding on image itself.
Because of that our view model can be created few times - we don't want that, so the best solution is to define static resource of view model and use it everywhere on page.
The solution is below (you will just need to modify some namespaces and change delte.png to your delete.jpg):
page xml
<?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:local="clr-namespace:ButtonRendererDemo;assembly=ButtonRendererDemo"
x:Class="ButtonRendererDemo.ImageTapComplexPage"
BindingContext="{StaticResource viewModel}">
<ContentPage.Resources>
<ResourceDictionary>
<local:TapComplexViewModel x:Key="viewModel"/>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<Label Text="text2" VerticalOptions="Center" HorizontalOptions="Center" />
<StackLayout Padding="0,20,0,20">
<Button Text="text" Clicked="onclick" BackgroundColor="#009688"></Button>
</StackLayout>
<Label Text="List Type" VerticalOptions="Center" HorizontalOptions="Center" />
<ListView x:Name="lstItems" RowHeight="60" ItemsSource="{Binding Items}" > <!--IsEnabled="False"-->
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" HorizontalOptions="Fill" Padding="0,2,0,2">
<StackLayout Padding="5,5,5,5">
<Image Source="{Binding source}" HorizontalOptions="End" HeightRequest="40" WidthRequest="40" />
</StackLayout>
<StackLayout Orientation="Vertical" Spacing="1">
<Label Text = "{Binding name}" HeightRequest="20" FontAttributes="Bold"/>
<Label Text = "{Binding data, StringFormat='{0:F0}'}" />
</StackLayout>
<StackLayout HorizontalOptions="EndAndExpand" Padding="0,0,15,0" Orientation="Horizontal">
<Image Source="delete.png" HeightRequest="20" WidthRequest="20">
<Image.GestureRecognizers>
<!--<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="Image1" />-->
<TapGestureRecognizer Command="{Binding Source={StaticResource viewModel}, Path=TapCommand}" CommandParameter="{Binding name}" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
Code behind
namespace ButtonRendererDemo
{
public partial class ImageTapComplexPage : ContentPage
{
public ImageTapComplexPage()
{
InitializeComponent();
}
void onclick(object sender, EventArgs args)
{
}
}
public class TapComplexViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int taps = 0;
ICommand tapCommand;
public ObservableCollection<MyItem> Items { get; private set; }
public TapComplexViewModel()
{
Items = new ObservableCollection<MyItem>()
{
new MyItem { name="First", source="Icon.png", data=0.5f },
new MyItem { name="Second", source="Icon.png", data=0.6f },
new MyItem { name="Third", source="Icon.png", data=0.7f }
};
tapCommand = new Command(OnTapped);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
void OnTapped(object s)
{
taps++;
Debug.WriteLine("parameter: " + s);
}
}
public class MyItem
{
public string name { get; set; }
public string source { get; set; }
public float data { get; set; }
//public ICommand TapCommand
//{
// get { return new Command(() => { }); }
//}
}
}
In the back end of the xaml file make sure u set the BindingContext to the view model: Something like this:
xaml.cs
public partial class TapInsideFrameXaml : ContentPage
{
public TapInsideFrameXaml ()
{
InitializeComponent ();
// The TapViewModel contains the TapCommand which is wired up in Xaml
BindingContext = new TapViewModel ();
}
}
Also check:
On Menu > tools > options > debugging > General:
Ensure "Redirect all output window text to the immediate window" is NOT checked
On Project Properties > Build:
Configuration: Debug
"Define DEBUG constant" is checked
"Define TRACE constant" is checked
On the Output window:
Show output from: Debug
Right-click in the output window and ensure "Program output" is checked
Also make sure you are debugging using F5 and not Ctrl f5
You declared the PropertyChangedEvent event but you never actually use it. Try this:
public class MyClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int taps = 0;
ICommand tapCommand;
public MyClass()
{
tapCommand = new Command(OnTapped);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
void OnTapped(object s)
{
taps++;
Debug.WriteLine("parameter: " + s);
OnPropertyChanged();
}
protected virtual void OnPropertyChanged ([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler (this, new PropertyChangedEventArgs (propertyName));
}
}

Categories

Resources