Xamarin Forms iOS中Picker背景色与标题色自定义及点击高亮需求
我之前也碰到过类似的问题——直接改Picker的BackgroundColor经常不生效,大概率是因为原生控件的默认样式在状态变化时(比如焦点改变、弹窗关闭)把我们设置的样式给覆盖了。下面给你两个可行的方案,其中自定义渲染器的方法最稳定,推荐优先尝试:
这个方法通过创建自定义Picker控件,添加绑定属性控制选中状态,再借助原生渲染器直接操作Android/iOS的底层控件,从根源上避免样式被覆盖的问题。
步骤1:创建自定义Picker控件
先定义一个带IsSelected绑定属性的Picker,用来标记当前是否被选中:
using Xamarin.Forms; namespace YourApp.Controls { public class HighlightablePicker : Picker { public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create(nameof(IsSelected), typeof(bool), typeof(HighlightablePicker), false); public bool IsSelected { get => (bool)GetValue(IsSelectedProperty); set => SetValue(IsSelectedProperty, value); } } }
步骤2:Android端自定义渲染器
Android的Picker底层是Spinner,我们需要监听点击事件和IsSelected属性变化,直接修改原生控件的背景和文字颜色:
using Android.Content; using Android.Widget; using YourApp.Controls; using YourApp.Droid.Renderers; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; [assembly: ExportRenderer(typeof(HighlightablePicker), typeof(HighlightablePickerRenderer))] namespace YourApp.Droid.Renderers { public class HighlightablePickerRenderer : PickerRenderer { private Color _defaultBgColor; private Color _defaultTextColor; private readonly Color _selectedBg = Color.LightSkyBlue; private readonly Color _selectedText = Color.White; public HighlightablePickerRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<Picker> e) { base.OnElementChanged(e); if (Control != null) { // 保存原生默认颜色 _defaultBgColor = Control.BackgroundTintList?.DefaultColor ?? Color.Transparent; _defaultTextColor = Control.TextColors.DefaultColor; // 监听Picker点击事件 Control.Click += OnPickerClicked; // 监听IsSelected属性变化 ((HighlightablePicker)Element).PropertyChanged += OnPickerPropertyChanged; // 初始化样式 UpdatePickerStyle(); } } private void OnPickerClicked(object sender, System.EventArgs e) { var picker = (HighlightablePicker)Element; picker.IsSelected = true; // 这里需要通知其他Picker取消选中,建议在视图模型里统一处理 } private void OnPickerPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == nameof(HighlightablePicker.IsSelected)) { UpdatePickerStyle(); } } private void UpdatePickerStyle() { var picker = (HighlightablePicker)Element; if (picker.IsSelected) { Control.SetBackgroundColor(_selectedBg); Control.SetTextColor(_selectedText); } else { Control.SetBackgroundColor(_defaultBgColor); Control.SetTextColor(_defaultTextColor); } } protected override void Dispose(bool disposing) { if (Control != null) { Control.Click -= OnPickerClicked; ((HighlightablePicker)Element).PropertyChanged -= OnPickerPropertyChanged; } base.Dispose(disposing); } } }
步骤3:iOS端自定义渲染器
iOS的Picker底层是UITextField+UIPickerView,我们监听编辑开始事件(点击Picker时触发),再根据IsSelected属性修改样式:
using YourApp.Controls; using YourApp.iOS.Renderers; using UIKit; using Xamarin.Forms; using Xamarin.Forms.Platform.iOS; [assembly: ExportRenderer(typeof(HighlightablePicker), typeof(HighlightablePickerRenderer))] namespace YourApp.iOS.Renderers { public class HighlightablePickerRenderer : PickerRenderer { private UIColor _defaultBgColor; private UIColor _defaultTextColor; private readonly UIColor _selectedBg = UIColor.FromRGB(135, 206, 250); private readonly UIColor _selectedText = UIColor.White; protected override void OnElementChanged(ElementChangedEventArgs<Picker> e) { base.OnElementChanged(e); if (Control != null) { // 保存原生默认颜色 _defaultBgColor = Control.BackgroundColor; _defaultTextColor = Control.TextColor; // 监听Picker点击(编辑开始)事件 Control.EditingDidBegin += OnPickerEditingBegin; // 监听IsSelected属性变化 ((HighlightablePicker)Element).PropertyChanged += OnPickerPropertyChanged; // 初始化样式 UpdatePickerStyle(); } } private void OnPickerEditingBegin(object sender, System.EventArgs e) { var picker = (HighlightablePicker)Element; picker.IsSelected = true; // 同样,其他Picker的取消选中逻辑放在视图模型里 } private void OnPickerPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == nameof(HighlightablePicker.IsSelected)) { UpdatePickerStyle(); } } private void UpdatePickerStyle() { var picker = (HighlightablePicker)Element; if (picker.IsSelected) { Control.BackgroundColor = _selectedBg; Control.TextColor = _selectedText; } else { Control.BackgroundColor = _defaultBgColor; Control.TextColor = _defaultTextColor; } } protected override void Dispose(bool disposing) { if (Control != null) { Control.EditingDidBegin -= OnPickerEditingBegin; ((HighlightablePicker)Element).PropertyChanged -= OnPickerPropertyChanged; } base.Dispose(disposing); } } }
步骤4:视图模型中管理选中状态
为了实现“点击其他Picker时,之前的恢复默认”,需要在视图模型里维护一个选中的Picker实例,当新的Picker被选中时,取消之前的选中状态:
using System.ComponentModel; using System.Runtime.CompilerServices; using YourApp.Controls; namespace YourApp.ViewModels { public class MainViewModel : INotifyPropertyChanged { private HighlightablePicker _selectedPicker; public HighlightablePicker Picker1 { get; set; } public HighlightablePicker Picker2 { get; set; } public HighlightablePicker Picker3 { get; set; } public MainViewModel() { // 初始化三个Picker Picker1 = new HighlightablePicker { Title = "选项组1" }; Picker2 = new HighlightablePicker { Title = "选项组2" }; Picker3 = new HighlightablePicker { Title = "选项组3" }; // 监听每个Picker的IsSelected变化 Picker1.PropertyChanged += OnPickerSelectedChanged; Picker2.PropertyChanged += OnPickerSelectedChanged; Picker3.PropertyChanged += OnPickerSelectedChanged; } private void OnPickerSelectedChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName != nameof(HighlightablePicker.IsSelected)) return; var currentPicker = (HighlightablePicker)sender; if (currentPicker.IsSelected) { // 取消之前选中的Picker状态 _selectedPicker?.SetValue(HighlightablePicker.IsSelectedProperty, false); _selectedPicker = currentPicker; } else if (_selectedPicker == currentPicker) { // 取消当前选中时,清空选中标记 _selectedPicker = null; } } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }
步骤5:XAML中使用自定义Picker
在页面里引用自定义控件,绑定到视图模型即可:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:controls="clr-namespace:YourApp.Controls" xmlns:viewmodels="clr-namespace:YourApp.ViewModels" x:Class="YourApp.MainPage"> <ContentPage.BindingContext> <viewmodels:MainViewModel /> </ContentPage.BindingContext> <StackLayout Padding="20" Spacing="10"> <controls:HighlightablePicker x:Name="Picker1" BindingContext="{Binding Picker1}" /> <controls:HighlightablePicker x:Name="Picker2" BindingContext="{Binding Picker2}" /> <controls:HighlightablePicker x:Name="Picker3" BindingContext="{Binding Picker3}" /> </StackLayout> </ContentPage>
如果不想写自定义渲染器,可以用Behavior来监听Picker的焦点事件,动态修改样式。但这个方法在复杂页面里可能不够灵活,且容易被原生样式覆盖:
using System.Linq; using Xamarin.Forms; namespace YourApp.Behaviors { public class PickerHighlightBehavior : Behavior<Picker> { private Color _defaultBg; private Color _defaultText; public Color HighlightBgColor { get; set; } = Color.LightSkyBlue; public Color HighlightTextColor { get; set; } = Color.White; protected override void OnAttachedTo(Picker bindable) { base.OnAttachedTo(bindable); _defaultBg = bindable.BackgroundColor; _defaultText = bindable.TextColor; bindable.Focused += OnPickerFocused; bindable.Unfocused += OnPickerUnfocused; } private void OnPickerFocused(object sender, FocusEventArgs e) { var currentPicker = (Picker)sender; // 取消页面中其他Picker的高亮状态 var page = currentPicker.Parent as ContentPage; if (page != null) { foreach (var picker in page.Content.Descendants().OfType<Picker>()) { if (picker != currentPicker) { var behavior = picker.Behaviors.OfType<PickerHighlightBehavior>().FirstOrDefault(); if (behavior != null) { picker.BackgroundColor = behavior._defaultBg; picker.TextColor = behavior._defaultText; } } } } // 设置当前Picker高亮 currentPicker.BackgroundColor = HighlightBgColor; currentPicker.TextColor = HighlightTextColor; } private void OnPickerUnfocused(object sender, FocusEventArgs e) { var picker = (Picker)sender; picker.BackgroundColor = _defaultBg; picker.TextColor = _defaultText; } protected override void OnDetachingFrom(Picker bindable) { base.OnDetachingFrom(bindable); bindable.Focused -= OnPickerFocused; bindable.Unfocused -= OnPickerUnfocused; } } }
XAML使用方式:
<Picker Title="选项组" Margin="0,10,0,0"> <Picker.Behaviors> <behaviors:PickerHighlightBehavior HighlightBgColor="LightSkyBlue" HighlightTextColor="White" /> </Picker.Behaviors> </Picker>
自定义渲染器的方案最稳定,因为直接操作原生控件,不会被Xamarin.Forms的样式逻辑干扰;Behavior方案更轻量,但在页面元素复杂时,遍历控件的方式效率不高,且可能出现样式不生效的情况。根据你的需求,推荐优先使用方案一。
内容的提问来源于stack exchange,提问作者Riyas

