自定义Xamarin Forms Picker遇ControlTemplate解析错误求助
解决Xamarin Forms自定义Picker空集合显示“No entries”的问题
错误原因
Xamarin Forms原生Picker控件没有ControlTemplate属性,你尝试在自定义Picker的Style中设置该属性,导致编译器无法识别,从而抛出XFC0001错误。
正确实现方案
我们可以通过ContentView包装原生Picker和提示控件的方式,实现空集合时显示“No entries”的需求,具体步骤如下:
1. 创建自定义Picker控件(XAML)
创建继承自ContentView的自定义控件,内部包含原生Picker和空数据提示Label:
<?xml version="1.0" encoding="UTF-8"?> <ContentView x:Class="v.App.Styling.Controls.vPicker" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:d="http://xamarin.com/schemas/2014/forms/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Name="This" mc:Ignorable="d"> <StackLayout> <!-- 原生Picker:数据存在时显示 --> <Picker x:Name="InnerPicker" IsVisible="{Binding Source={x:Reference This}, Path=ItemsSource, Converter={StaticResource HasItemsToBoolConverter}}" ItemsSource="{Binding Source={x:Reference This}, Path=ItemsSource}" SelectedItem="{Binding Source={x:Reference This}, Path=SelectedItem}" ItemDisplayBinding="{Binding Source={x:Reference This}, Path=ItemDisplayBinding}" /> <!-- 空数据提示:集合为空时显示 --> <Label IsVisible="{Binding Source={x:Reference This}, Path=ItemsSource, Converter={StaticResource HasNoItemsToBoolConverter}}" Text="No entries" TextColor="{StaticResource ColorTextSecondary}" Padding="10,0" VerticalOptions="Center" /> </StackLayout> </ContentView>
2. 自定义Picker后台代码(C#)
对外暴露原生Picker的核心绑定属性,保证和原生Picker用法一致:
using Xamarin.Forms; namespace v.App.Styling.Controls { public partial class vPicker : ContentView { public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(System.Collections.IEnumerable), typeof(vPicker)); public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create(nameof(SelectedItem), typeof(object), typeof(vPicker), BindingMode.TwoWay); public static readonly BindableProperty ItemDisplayBindingProperty = BindableProperty.Create(nameof(ItemDisplayBinding), typeof(BindingBase), typeof(vPicker)); public System.Collections.IEnumerable ItemsSource { get => (System.Collections.IEnumerable)GetValue(ItemsSourceProperty); set => SetValue(ItemsSourceProperty, value); } public object SelectedItem { get => GetValue(SelectedItemProperty); set => SetValue(SelectedItemProperty, value); } public BindingBase ItemDisplayBinding { get => (BindingBase)GetValue(ItemDisplayBindingProperty); set => SetValue(ItemDisplayBindingProperty, value); } public vPicker() { InitializeComponent(); } } }
3. 实现必要的转换器
确保已在资源中注册以下两个转换器:
HasItemsToBoolConverter(集合有数据时返回true)
using System; using System.Collections; using Xamarin.Forms; namespace v.App.Styling.Converters { public class HasItemsToBoolConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is IEnumerable enumerable) { var enumerator = enumerable.GetEnumerator(); return enumerator.MoveNext(); } return false; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } }
HasNoItemsToBoolConverter(集合为空时返回true)
using System; using System.Collections; using Xamarin.Forms; namespace v.App.Styling.Converters { public class HasNoItemsToBoolConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is IEnumerable enumerable) { var enumerator = enumerable.GetEnumerator(); return !enumerator.MoveNext(); } return true; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } }
4. 使用自定义Picker
在页面中像原生Picker一样使用vPicker:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:controls="clr-namespace:v.App.Styling.Controls" xmlns:converters="clr-namespace:v.App.Styling.Converters" x:Class="v.App.Views.MyPage"> <ContentPage.Resources> <converters:HasItemsToBoolConverter x:Key="HasItemsToBoolConverter" /> <converters:HasNoItemsToBoolConverter x:Key="HasNoItemsToBoolConverter" /> </ContentPage.Resources> <StackLayout Padding="20"> <controls:vPicker ItemsSource="{Binding MyItems}" SelectedItem="{Binding SelectedMyItem}" ItemDisplayBinding="{Binding Name}" /> </StackLayout> </ContentPage>
额外说明
- 若坚持使用
ControlTemplate,需自定义继承自TemplatedView的控件,只有TemplatedView及其子类(如ContentView、Frame)支持该属性。 - 用Label代替Entry作为提示控件,更符合“仅展示提示”的交互逻辑,避免用户误操作。
内容的提问来源于stack exchange,提问作者HFrisch
相关产品推荐
相关产品推荐

