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

在C#与WinUI3中创建可复用Flyout并在多代码后置中实例化

解决跨类复用CommandBarFlyout并绑定动态状态的方案

1. 正确声明全局可复用的CommandBarFlyout

资源字典不能直接以CommandBarFlyout为根元素,必须包裹在<ResourceDictionary>中,你可以选择两种实现方式:

方式一:添加到App.xaml的全局资源

打开App.xaml,在<Application.Resources>节点内添加以下代码:

<Application.Resources>
    <BooleanToVisibilityConverter x:Key="BoolToVis" />
    <CommandBarFlyout x:Key="ImageContextMenu" 
                      AlwaysExpanded="True"
                      x:Shared="False"> <!-- 关键:确保每次获取都是新实例,避免状态串用 -->
        <CommandBarFlyout.SecondaryCommands>
            <AppBarButton Label="A" 
                          Visibility="{Binding IsButtonAVisible, Converter={StaticResource BoolToVis}}"/>
            <AppBarButton Label="B" 
                          Visibility="{Binding IsButtonBVisible, Converter={StaticResource BoolToVis}}"/>
            <AppBarButton Label="C" 
                          Visibility="{Binding IsButtonCVisible, Converter={StaticResource BoolToVis}}"/>
            <AppBarButton Label="D" 
                          Visibility="{Binding IsButtonDVisible, Converter={StaticResource BoolToVis}}"/>
        </CommandBarFlyout.SecondaryCommands>
    </CommandBarFlyout>
</Application.Resources>

方式二:使用独立资源字典文件

  1. 新建资源字典文件(如FlyoutResources.xaml),内容如下:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <BooleanToVisibilityConverter x:Key="BoolToVis" />
    <CommandBarFlyout x:Key="ImageContextMenu" 
                      AlwaysExpanded="True"
                      x:Shared="False">
        <CommandBarFlyout.SecondaryCommands>
            <AppBarButton Label="A" 
                          Visibility="{Binding IsButtonAVisible, Converter={StaticResource BoolToVis}}"/>
            <AppBarButton Label="B" 
                          Visibility="{Binding IsButtonBVisible, Converter={StaticResource BoolToVis}}"/>
            <AppBarButton Label="C" 
                          Visibility="{Binding IsButtonCVisible, Converter={StaticResource BoolToVis}}"/>
            <AppBarButton Label="D" 
                          Visibility="{Binding IsButtonDVisible, Converter={StaticResource BoolToVis}}"/>
        </CommandBarFlyout.SecondaryCommands>
    </CommandBarFlyout>
</ResourceDictionary>
  1. 在App.xaml中合并该资源字典:
<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="FlyoutResources.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

2. 定义图片的状态类

创建实现INotifyPropertyChanged的状态类,用于控制按钮可见性和承载交互逻辑:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;

public class ImageFlyoutState : INotifyPropertyChanged
{
    private bool _isButtonAVisible;
    public bool IsButtonAVisible
    {
        get => _isButtonAVisible;
        set => SetField(ref _isButtonAVisible, value);
    }

    private bool _isButtonBVisible;
    public bool IsButtonBVisible
    {
        get => _isButtonBVisible;
        set => SetField(ref _isButtonBVisible, value);
    }

    private bool _isButtonCVisible;
    public bool IsButtonCVisible
    {
        get => _isButtonCVisible;
        set => SetField(ref _isButtonCVisible, value);
    }

    private bool _isButtonDVisible;
    public bool IsButtonDVisible
    {
        get => _isButtonDVisible;
        set => SetField(ref _isButtonDVisible, value);
    }

    // 可选:用ICommand绑定按钮点击,替代代码后置的回调
    public ICommand ButtonACommand { get; set; }
    public ICommand ButtonBCommand { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}

3. 代码后置中绑定Flyout到图片并设置状态/回调

方式一:使用ICommand绑定点击推荐

// 为每张图片创建独立状态实例
var imageState = new ImageFlyoutState
{
    IsButtonAVisible = true, // 根据图片需求设置可见性
    IsButtonBVisible = false,
    ButtonACommand = new RelayCommand(() => 
    {
        // 按钮A的点击逻辑
    }),
    ButtonBCommand = new RelayCommand(() => 
    {
        // 按钮B的点击逻辑
    })
};

// 获取全局资源中的Flyout实例(因为x:Shared="False",每次都是新的)
if (Application.Current.Resources.TryGetValue("ImageContextMenu", out var flyoutObj) && 
    flyoutObj is CommandBarFlyout flyout)
{
    // 将状态类绑定到Flyout的DataContext,让可见性绑定生效
    flyout.DataContext = imageState;
    // 将Flyout关联到目标图片
    FlyoutBase.SetAttachedFlyout(image, flyout);
}

注:RelayCommand是常见的ICommand实现,你可以自行实现或使用MvvmLight等社区库的版本

方式二:代码后置直接绑定点击事件

如果坚持用代码后置的回调,可参考以下写法:

var imageState = new ImageFlyoutState
{
    IsButtonAVisible = true,
    IsButtonCVisible = true
};

if (Application.Current.Resources.TryGetValue("ImageContextMenu", out var flyoutObj) && 
    flyoutObj is CommandBarFlyout flyout)
{
    flyout.DataContext = imageState;
    
    // 遍历SecondaryCommands为按钮绑定点击事件
    foreach (var cmd in flyout.SecondaryCommands)
    {
        if (cmd is AppBarButton btn)
        {
            switch (btn.Label)
            {
                case "A":
                    btn.Click += ButtonA_Click;
                    break;
                case "B":
                    btn.Click += ButtonB_Click;
                    break;
                // 其他按钮同理
            }
        }
    }

    FlyoutBase.SetAttachedFlyout(image, flyout);
}

// 点击事件处理函数
private void ButtonA_Click(object sender, RoutedEventArgs e)
{
    // 按钮A的逻辑
}

关键注意事项

  • x:Shared="False":必须添加该属性,否则所有图片会共用同一个Flyout实例,导致状态串用。
  • DataContext绑定:每个Flyout实例的DataContext必须设置为对应图片的独立状态对象,确保可见性绑定正确对应。
  • BooleanToVisibilityConverter:用于将布尔值转换为UI的Visibility枚举值,是绑定可见性的标准方式。

内容的提问来源于stack exchange,提问作者John Glen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:25:13