自定义Picker渲染器Bug:调用Picker.Focus()时Picker显示两次
解决Xamarin.Forms自定义Picker调用Focus()弹出双弹窗问题
问题场景
基于Xamarin.Forms 5.0.0.2545开发,为Picker实现了自定义渲染器。在Tab控件的SelectionChanged事件中调用myPicker.Focus()触发Picker弹窗时,会同时弹出默认渲染器的弹窗和自定义弹窗,关闭自定义弹窗后默认弹窗仍存在;但直接点击Picker控件时仅显示自定义弹窗,符合预期。
尝试过以下方案但未解决:
- 拦截Focus/Unfocus事件导致跳过自定义弹窗,不符合需求
- 在主线程执行Focus操作
- 在
OnElementChanged中先移除再重新绑定Control.Click事件 - 针对首次点击不渲染的解决方案
推测原因:调用Focus()时,继承的默认PickerRenderer先触发了默认弹窗逻辑,且Control.Click事件在默认弹窗显示后才执行。
可行解决方案
方案1:拦截焦点变化,手动触发自定义弹窗
在自定义渲染器中拦截控件的焦点变化事件,阻止默认弹窗触发,同时手动调用自定义弹窗逻辑。以Android平台为例:
[assembly: ExportRenderer(typeof(CustomPicker), typeof(CustomPickerRenderer))] namespace YourApp.Droid.Renderers { public class CustomPickerRenderer : PickerRenderer { protected override void OnElementChanged(ElementChangedEventArgs<Picker> e) { base.OnElementChanged(e); if (Control != null) { // 移除默认点击事件,绑定自定义点击逻辑 Control.Click -= Control_Click; Control.Click += Control_Click; // 绑定焦点变化事件,拦截默认弹窗 Control.FocusChange += Control_FocusChange; } } private void Control_FocusChange(object sender, Android.Views.View.FocusChangeEventArgs e) { if (e.HasFocus) { // 清除焦点,阻止默认弹窗触发 Control.ClearFocus(); // 手动触发自定义弹窗 ShowCustomPickerDialog(); } } private void Control_Click(object sender, EventArgs e) { ShowCustomPickerDialog(); } // 自定义弹窗实现逻辑 private void ShowCustomPickerDialog() { // 这里放置你之前自定义渲染器中的弹窗代码 // 比如创建AlertDialog或自定义选择器弹窗 } } }
方案2:添加自定义命令直接触发弹窗
绕开Focus()方法,给自定义Picker添加专属命令,直接调用渲染器的弹窗逻辑:
1. 定义自定义Picker类
namespace YourApp.Controls { public class CustomPicker : Picker { public ICommand ShowPickerCommand { get; } public CustomPicker() { ShowPickerCommand = new Command(() => { // 通过消息中心通知渲染器显示弹窗 MessagingCenter.Send(this, "TriggerCustomPicker"); }); } } }
2. 在自定义渲染器中订阅消息
[assembly: ExportRenderer(typeof(CustomPicker), typeof(CustomPickerRenderer))] namespace YourApp.Droid.Renderers { public class CustomPickerRenderer : PickerRenderer { protected override void OnElementChanged(ElementChangedEventArgs<CustomPicker> e) { base.OnElementChanged(e); if (e.OldElement != null) { // 取消旧元素的消息订阅 MessagingCenter.Unsubscribe<CustomPicker>(e.OldElement, "TriggerCustomPicker"); } if (e.NewElement != null) { // 订阅消息,触发自定义弹窗 MessagingCenter.Subscribe<CustomPicker>(e.NewElement, "TriggerCustomPicker", (sender) => { ShowCustomPickerDialog(); }); // 绑定点击事件 Control.Click += (s, args) => ShowCustomPickerDialog(); } } private void ShowCustomPickerDialog() { // 自定义弹窗逻辑 } } }
3. 在Tab切换事件中调用命令
private void TabbedPage_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.NewPosition == 1) { myPicker.ShowPickerCommand.Execute(null); } }
方案3:重写默认渲染器的弹窗触发逻辑
对于Android平台,默认PickerRenderer在控件获得焦点时会通过OnFocusChanged触发默认弹窗。可以重写该方法,替换为自定义弹窗逻辑:
protected override void OnFocusChanged(bool gainFocus, [GeneratedEnum] FocusSearchDirection direction, Rect previouslyFocusedRect) { if (gainFocus) { // 阻止默认焦点处理逻辑 // base.OnFocusChanged(gainFocus, direction, previouslyFocusedRect); ShowCustomPickerDialog(); // 清除焦点,避免重复触发 Control.ClearFocus(); } else { base.OnFocusChanged(gainFocus, direction, previouslyFocusedRect); } }
内容的提问来源于stack exchange,提问作者Balea Andrei - Petru
相关产品推荐
相关产品推荐

