Xamarin Forms Shell切换Flyout时IsVisible为false的Frame意外显示问题
问题原因
- 绑定上下文临时失效:Shell执行Flyout关闭动画时,会优先处理动画渲染逻辑,页面控件的绑定更新优先级被压低,甚至会出现绑定上下文临时丢失的情况。Frame控件
IsVisible属性的默认值为True,绑定失效期间就会短暂采用默认值显示。 - 异步方法调度异常:你重写的
OnBackButtonPressed是同步方法,直接添加await关键字会导致方法变成隐式的async void,异步导航操作和UI线程的动画渲染逻辑出现调度冲突,进而触发控件可见性状态异常。 - 布局重绘优先级问题:你的Frame放在
AbsoluteLayout中,Flyout关闭时会触发整个页面的布局重排,布局计算过程中绑定更新还未完成,就会先渲染控件的默认可见状态。
解决方案
- 给IsVisible绑定添加兜底值
给Frame和内部ListView的IsVisible绑定都加上FallbackValue和TargetNullValue,避免绑定失效时采用默认值:
<Frame IsVisible="{Binding ShowPanel, FallbackValue=False, TargetNullValue=False}" BackgroundColor="White" BorderColor="Purple" AbsoluteLayout.LayoutFlags="PositionProportional" WidthRequest="100" HeightRequest="200" AbsoluteLayout.LayoutBounds="0,0,200,250"> <local:ExtendedListView IsVisible="{Binding ShowPanel, FallbackValue=False, TargetNullValue=False}" ItemTapped="ExtendedListView_ItemTapped" ItemsSource="{Binding AllItems}" SelectionMode="None" HorizontalScrollBarVisibility="Always" HorizontalOptions="Fill" VerticalOptions="Fill" BackgroundColor="Transparent"> <!-- 内部控件省略 --> </local:ExtendedListView> </Frame>
- 修复OnBackButtonPressed的异步逻辑
同步方法内不要直接使用await,避免async void导致的UI线程调度异常,修改为通过调度器异步执行导航:
protected override bool OnBackButtonPressed() { Dispatcher.Dispatch(async () => { await Shell.Current.GoToAsync($"//{nameof(MainPage)}"); }); return true; }
- 额外添加可见性兜底校验
可以同时绑定Frame的Opacity属性,即使IsVisible临时异常也不会显示,需要先实现对应的转换器:
public class BoolToOpacityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value is bool isVisible && isVisible ? 1 : 0; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
在页面资源中注册转换器后,修改Frame的绑定逻辑:
<Frame IsVisible="{Binding ShowPanel, FallbackValue=False, TargetNullValue=False}" Opacity="{Binding ShowPanel, Converter={StaticResource BoolToOpacityConverter}, FallbackValue=0}" ...其余属性省略 >
- 监听Flyout状态强制刷新绑定
在页面的代码后台监听Shell的Flyout状态变化,Flyout关闭时强制触发ViewModel的属性变更通知,刷新绑定:
protected override void OnAppearing() { base.OnAppearing(); Shell.Current.PropertyChanged += OnShellPropertyChanged; } protected override void OnDisappearing() { base.OnDisappearing(); Shell.Current.PropertyChanged -= OnShellPropertyChanged; } private void OnShellPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(Shell.FlyoutIsPresented) && !Shell.Current.FlyoutIsPresented) { var vm = BindingContext as 你的ViewModel类名; // 如果ViewModel的OnPropertyChanged是保护方法,直接重新赋值触发通知即可 if (vm != null) vm.ShowPanel = vm.ShowPanel; } }
内容的提问来源于stack exchange,提问作者ZZZ
相关产品推荐
相关产品推荐

