You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 21:05:16