You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的属性值:

  1. 定义附加属性
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;
        }
    }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 16:57:29