如何在ListView中隐藏禁用的MenuItem?技术解决方案求助
我明白你现在的困扰——ListView的ContextActions里的MenuItem确实没有自带的IsVisible属性,只能靠IsEnabled控制禁用状态,但你想要完全隐藏它对吧?下面给你几个可行的解决方案,都是经过实践验证的:
方案1:自定义带IsVisible属性的MenuItem
这是最直接的方法,我们扩展原生MenuItem,给它加上IsVisible属性,通过属性变化事件来动态添加/移除ContextActions里的项:
首先创建自定义MenuItem类:
public class VisibleMenuItem : MenuItem { public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(VisibleMenuItem), true, propertyChanged: OnIsVisibleChanged); public bool IsVisible { get => (bool)GetValue(IsVisibleProperty); set => SetValue(IsVisibleProperty, value); } private static void OnIsVisibleChanged(BindableObject bindable, object oldValue, object newValue) { var menuItem = (VisibleMenuItem)bindable; var viewCell = menuItem.Parent as ViewCell; if (viewCell == null) return; if ((bool)newValue) { if (!viewCell.ContextActions.Contains(menuItem)) viewCell.ContextActions.Add(menuItem); } else { viewCell.ContextActions.Remove(menuItem); } } }
然后需要一个反转布尔值的转换器,因为你的IsCommandDissabled是“禁用时为true”,我们需要把它转成“隐藏时为false”:
public class InverseBooleanConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return !(bool)value; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return !(bool)value; } }
在XAML资源里声明转换器:
<ContentPage.Resources> <local:InverseBooleanConverter x:Key="InverseBooleanConverter"/> </ContentPage.Resources>
最后替换原来的MenuItem为自定义控件:
<BaseControls:MenuItemsDisablingViewCell.ContextActions> <MenuItem IsDestructive="True" Command="{Binding EntViewModel.DeleteCom}" Text="Delete"/> <local:VisibleMenuItem Command="{Binding DissableAndHidenCommand}" Text="Command which is Dissabled and hiden" IsVisible="{Binding IsCommandDissabled, Converter={StaticResource InverseBooleanConverter}}"/> </BaseControls:MenuItemsDisablingViewCell.ContextActions>
方案2:在ViewCell后台动态管理ContextActions
如果你不想自定义控件,也可以在MenuItemsDisablingViewCell的代码后台监听绑定上下文变化,手动添加/移除MenuItem:
public partial class MenuItemsDisablingViewCell : ViewCell { private MenuItem _hiddenMenuItem; public MenuItemsDisablingViewCell() { InitializeComponent(); // 提前创建要隐藏的MenuItem _hiddenMenuItem = new MenuItem { Text = "Command which is Dissabled and hiden", Command = new Command(() => { if (BindingContext is YourItemType item) item.DissableAndHidenCommand?.Execute(null); }) }; } protected override void OnBindingContextChanged() { base.OnBindingContextChanged(); if (BindingContext is YourItemType item) { // 初次加载时设置可见性 UpdateMenuItemVisibility(item.IsCommandDissabled); // 监听IsCommandDissabled的变化 item.PropertyChanged += Item_PropertyChanged; } } private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(YourItemType.IsCommandDissabled)) { var item = (YourItemType)sender; UpdateMenuItemVisibility(item.IsCommandDissabled); } } private void UpdateMenuItemVisibility(bool isDisabled) { if (isDisabled) { ContextActions.Remove(_hiddenMenuItem); } else { if (!ContextActions.Contains(_hiddenMenuItem)) ContextActions.Add(_hiddenMenuItem); } } // 解绑事件防止内存泄漏 protected override void OnDisappearing() { base.OnDisappearing(); if (BindingContext is YourItemType item) item.PropertyChanged -= Item_PropertyChanged; } }
注意把YourItemType替换成你绑定的Items集合里的实体类型。
方案3:用Behavior管理MenuItem可见性
如果想保持XAML的整洁,可以用Behavior来封装这个逻辑:
public class MenuItemVisibilityBehavior : Behavior<ViewCell> { public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(MenuItemVisibilityBehavior), true); public static readonly BindableProperty MenuItemTextProperty = BindableProperty.Create(nameof(MenuItemText), typeof(string), typeof(MenuItemVisibilityBehavior)); public bool IsVisible { get => (bool)GetValue(IsVisibleProperty); set => SetValue(IsVisibleProperty, value); } public string MenuItemText { get => (string)GetValue(MenuItemTextProperty); set => SetValue(MenuItemTextProperty, value); } private MenuItem _targetMenuItem; protected override void OnAttachedTo(ViewCell bindable) { base.OnAttachedTo(bindable); _targetMenuItem = bindable.ContextActions.FirstOrDefault(mi => mi.Text == MenuItemText); UpdateVisibility(); bindable.BindingContextChanged += OnBindingContextChanged; } private void OnBindingContextChanged(object sender, EventArgs e) => UpdateVisibility(); private void UpdateVisibility() { if (_targetMenuItem == null || AssociatedObject == null) return; if (IsVisible) { if (!AssociatedObject.ContextActions.Contains(_targetMenuItem)) AssociatedObject.ContextActions.Add(_targetMenuItem); } else { AssociatedObject.ContextActions.Remove(_targetMenuItem); } } protected override void OnPropertyChanged([CallerMemberName] string propertyName = null) { base.OnPropertyChanged(propertyName); if (propertyName == nameof(IsVisible)) UpdateVisibility(); } protected override void OnDetachingFrom(ViewCell bindable) { base.OnDetachingFrom(bindable); bindable.BindingContextChanged -= OnBindingContextChanged; _targetMenuItem = null; } }
XAML里使用Behavior:
<BaseControls:MenuItemsDisablingViewCell> <BaseControls:MenuItemsDisablingViewCell.ContextActions> <MenuItem IsDestructive="True" Command="{Binding EntViewModel.DeleteCom}" Text="Delete"/> <MenuItem x:Name="HiddenMenuItem" Command="{Binding DissableAndHidenCommand}" Text="Command which is Dissabled and hiden"/> </BaseControls:MenuItemsDisablingViewCell.ContextActions> <BaseControls:MenuItemsDisablingViewCell.Behaviors> <local:MenuItemVisibilityBehavior MenuItemText="Command which is Dissabled and hiden" IsVisible="{Binding IsCommandDissabled, Converter={StaticResource InverseBooleanConverter}}"/> </BaseControls:MenuItemsDisablingViewCell.Behaviors> </BaseControls:MenuItemsDisablingViewCell>
小提示
因为你用了RecycleElement缓存策略,一定要确保属性变化时能及时更新MenuItem的状态——方案1的自定义控件已经处理了这个问题,方案2和3也通过监听PropertyChanged事件保证了缓存复用后的正确性。
内容的提问来源于stack exchange,提问作者user3132499
相关产品推荐
相关产品推荐

