MAUI中ControlTemplate绑定HideableToolbarItem IsVisible报错:Binding实例不可重用
MAUI ControlTemplate中HideableToolbarItem绑定问题
背景
我用MAUI开发应用,想让部分工具栏项不用在所有页面显示,不想自定义工具栏,打算用内置的ContentPage.ToolbarItems/ToolbarItem。
问题经过
发现ToolbarItem没有IsVisible属性,之后找到一个解决方案,用自定义的HideableToolbarItem,在单个XAML页面里这么写能正常工作:
<ContentPage.ToolbarItems> <controls:HideableToolbarItem Text="Filter" IconImageSource="filter.png" IsVisible="{Binding FilterVisible}" /> </ContentPage.ToolbarItems>
但我已经用了ControlTemplate,不想重复写代码,就试着把这段加到ControlTemplate里:
<ContentPage.ToolbarItems> <controls:HideableToolbarItem Text="Filter" IconImageSource="filter.png" IsVisible="{TemplateBinding BindingContext.FilterVisible}" /> </ContentPage.ToolbarItems>
运行时直接报错:
System.InvalidOperationException: Binding instances cannot be reused
试过的绑定写法
我试了好几种绑定方式:
IsVisible="{TemplateBinding BindingContext.FilterVisible}" IsVisible="{TemplateBinding FilterVisible}" IsVisible="{Binding BindingContext.FilterVisible}" IsVisible="{Binding FilterVisible}"
其中后三种没效果,第一种会抛出上面的错误。
想要的效果
希望ControlTemplate里的绑定能获取到页面ViewModel的BindableProperty值,实现工具栏项的显示/隐藏控制。
解决方案
方法1:通过附加属性实现绑定
因为ToolbarItem不在VisualTree中,TemplateBinding无法直接生效,可通过附加属性传递ViewModel的属性值:
- 定义附加属性
public static class ToolbarItemHelper { public static readonly BindableProperty VisibilityProperty = BindableProperty.CreateAttached("Visibility", typeof(bool), typeof(ToolbarItemHelper), true, propertyChanged: OnVisibilityChanged); public static bool GetVisibility(BindableObject view) { return (bool)view.GetValue(VisibilityProperty); } public static void SetVisibility(BindableObject view, bool value) { view.SetValue(VisibilityProperty, value); } static void OnVisibilityChanged(BindableObject bindable, object oldValue, object newValue) { if (bindable is HideableToolbarItem item) { item.IsVisible = (bool)newValue; } } }
- 在ControlTemplate中使用该附加属性,结合RelativeSource绑定到页面的BindingContext
<ContentPage.ToolbarItems> <controls:HideableToolbarItem Text="Filter" IconImageSource="filter.png" local:ToolbarItemHelper.Visibility="{Binding FilterVisible, Source={RelativeSource AncestorType={x:Type ContentPage}}}" /> </ContentPage.ToolbarItems>
方法2:修改HideableToolbarItem绑定逻辑
让自定义ToolbarItem在加载时自动关联页面的ViewModel:
public class HideableToolbarItem : ToolbarItem { public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(HideableToolbarItem), true, propertyChanged: OnIsVisibleChanged); public bool IsVisible { get => (bool)GetValue(IsVisibleProperty); set => SetValue(IsVisibleProperty, value); } static void OnIsVisibleChanged(BindableObject bindable, object oldValue, object newValue) { var item = (HideableToolbarItem)bindable; item.IsVisible = (bool)newValue; } protected override void OnParentSet() { base.OnParentSet(); if (Parent is ContentPage page) { var existingBinding = this.GetBinding(IsVisibleProperty); if (existingBinding != null) { this.SetBinding(IsVisibleProperty, new Binding(existingBinding.Path, source: page.BindingContext)); } } } }
之后在ControlTemplate中直接使用普通绑定即可:
<ContentPage.ToolbarItems> <controls:HideableToolbarItem Text="Filter" IconImageSource="filter.png" IsVisible="{Binding FilterVisible}" /> </ContentPage.ToolbarItems>
方法3:用Behavior监听属性变化
给HideableToolbarItem添加Behavior,监听页面ViewModel的属性变更:
public class ToolbarItemVisibilityBehavior : Behavior<HideableToolbarItem> { public static readonly BindableProperty VisibilityPathProperty = BindableProperty.Create(nameof(VisibilityPath), typeof(string), typeof(ToolbarItemVisibilityBehavior), null); public string VisibilityPath { get => (string)GetValue(VisibilityPathProperty); set => SetValue(VisibilityPathProperty, value); } protected override void OnAttachedTo(HideableToolbarItem bindable) { base.OnAttachedTo(bindable); UpdateVisibility(bindable); if (bindable.Parent is ContentPage page) { page.BindingContextChanged += Page_BindingContextChanged; } } protected override void OnDetachingFrom(HideableToolbarItem bindable) { base.OnDetachingFrom(bindable); if (bindable.Parent is ContentPage page) { page.BindingContextChanged -= Page_BindingContextChanged; } } void Page_BindingContextChanged(object sender, EventArgs e) { if (sender is ContentPage page && AssociatedObject != null) { UpdateVisibility(AssociatedObject); } } void UpdateVisibility(HideableToolbarItem item) { if (item.Parent is ContentPage page && page.BindingContext != null && !string.IsNullOrEmpty(VisibilityPath)) { var property = page.BindingContext.GetType().GetProperty(VisibilityPath); if (property != null) { item.IsVisible = (bool)property.GetValue(page.BindingContext); if (page.BindingContext is INotifyPropertyChanged vm) { vm.PropertyChanged += (s, args) => { if (args.PropertyName == VisibilityPath) { item.IsVisible = (bool)property.GetValue(s); } }; } } } } }
在ControlTemplate中使用Behavior:
<ContentPage.ToolbarItems> <controls:HideableToolbarItem Text="Filter" IconImageSource="filter.png"> <controls:HideableToolbarItem.Behaviors> <local:ToolbarItemVisibilityBehavior VisibilityPath="FilterVisible" /> </controls:HideableToolbarItem.Behaviors> </controls:HideableToolbarItem> </ContentPage.ToolbarItems>
内容的提问来源于stack exchange,提问作者Burnmw
相关产品推荐
相关产品推荐

