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

WinUI 3:RadioMenuFlyoutItem的IsChecked属性二次重选后绑定失效

RadioMenuFlyoutItem绑定ViewModel切换选中项失效问题

我创建了一个测试项目,用于验证从ViewModel设置RadioMenuFlyoutItem选中状态的功能,已提交相关Bug,可下载示例项目复现问题。

XAML代码

<Window
x:Class="Test_menu_radioitem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Test_menu_radioitem"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<StackPanel x:Name="RootPanel" Orientation="Vertical">
    <StackPanel.Resources>
        <local:EnumToBoolConverter x:Key="EnumToBoolConverter" />
    </StackPanel.Resources>
    <DropDownButton HorizontalAlignment="Left"
                    Margin="0 1">
        <TextBlock Text="测试按钮"/>
        <DropDownButton.Flyout>
            <MenuFlyout x:Name="AvailabilityFlyout"
                        Placement="Bottom">
                <RadioMenuFlyoutItem GroupName="1"
                                     Text="活跃"
                                     Command="{Binding SetUserAvailabilityCommand}"
                                     IsChecked="{Binding Presence, Converter={StaticResource EnumToBoolConverter}, ConverterParameter='Active', Mode=OneWay}">
                    <RadioMenuFlyoutItem.CommandParameter>
                        <local:ContactPresence>Active</local:ContactPresence>
                    </RadioMenuFlyoutItem.CommandParameter>
                </RadioMenuFlyoutItem>
                <RadioMenuFlyoutItem GroupName="1"
                                     Text="请勿打扰"
                                     Command="{Binding SetUserAvailabilityCommand}"
                                     IsChecked="{Binding Presence, Converter={StaticResource EnumToBoolConverter}, ConverterParameter='DoNotDisturb', Mode=OneWay}">
                    <RadioMenuFlyoutItem.CommandParameter>
                        <local:ContactPresence>DoNotDisturb</local:ContactPresence>
                    </RadioMenuFlyoutItem.CommandParameter>
                </RadioMenuFlyoutItem>
                <RadioMenuFlyoutItem GroupName="1"
                                     Text="离开"
                                     Command="{Binding SetUserAvailabilityCommand}"
                                     IsChecked="{Binding Presence, Converter={StaticResource EnumToBoolConverter}, ConverterParameter='Away', Mode=OneWay}">
                    <RadioMenuFlyoutItem.CommandParameter>
                        <local:ContactPresence>Away</local:ContactPresence>
                    </RadioMenuFlyoutItem.CommandParameter>
                </RadioMenuFlyoutItem>
                <RadioMenuFlyoutItem GroupName="1"
                                     Text="离线"
                                     Command="{Binding SetUserAvailabilityCommand}"
                                     IsChecked="{Binding Presence, Converter={StaticResource EnumToBoolConverter}, ConverterParameter='Offline', Mode=OneWay}">
                    <RadioMenuFlyoutItem.CommandParameter>
                        <local:ContactPresence>Offline</local:ContactPresence>
                    </RadioMenuFlyoutItem.CommandParameter>
                </RadioMenuFlyoutItem>
            </MenuFlyout>
        </DropDownButton.Flyout>
    </DropDownButton>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button Click="myButton_Click_Active">活跃</Button>
        <Button Click="myButton_Click_DoNotDisturb">请勿打扰</Button>
        <Button Click="myButton_Click_Away">离开</Button>
        <Button Click="myButton_Click_Offline">离线</Button>
    </StackPanel>
</StackPanel>

后台代码(简化版)

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;


namespace Test_menu_radioitem
{
    /// <summary>
    /// 可独立使用或在Frame内导航的空白窗口。
    /// </summary>
    public sealed partial class MainWindow
    {
        public MainWindow()
        {
            this.InitializeComponent();
            RootPanel.DataContext = new ViewModel();
        }
    private void myButton_Click_Active(object sender, RoutedEventArgs e)
    {
        ((ViewModel)RootPanel.DataContext).Presence = ContactPresence.Active;
    }

    private void myButton_Click_DoNotDisturb(object sender, RoutedEventArgs e)
    {
        ((ViewModel)RootPanel.DataContext).Presence = ContactPresence.DoNotDisturb;
    }

    private void myButton_Click_Away(object sender, RoutedEventArgs e)
    {
        ((ViewModel)RootPanel.DataContext).Presence = ContactPresence.Away;
    }

    private void myButton_Click_Offline(object sender, RoutedEventArgs e)
    {
        ((ViewModel)RootPanel.DataContext).Presence = ContactPresence.Offline;
    }
}

public enum ContactPresence
{
    Active,
    Away,
    DoNotDisturb,
    Offline
}

public class EnumToBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value != null && value.GetType().IsEnum && parameter is string strParam)
        {
            if (Enum.TryParse(value.GetType(), strParam, out var enumParam))
            {
                return Equals(enumParam, value);
            }
        }

        return DependencyProperty.UnsetValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language) =>
        throw new NotImplementedException();
}

public class ViewModel : INotifyPropertyChanged
{
    private ContactPresence _presence;

    public ContactPresence Presence
    {
        get => _presence;
        set
        {
            _presence = value;
            OnPropertyChanged();
        }
    }

    public ICommand SetUserAvailabilityCommand { get; }

    public ViewModel()
    {
        Presence = ContactPresence.Away;
        SetUserAvailabilityCommand = new RelayCommand(Execute);
    }

    private void Execute(object param)
    {
        var value = (ContactPresence)param;
        Presence = value;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual 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;
    }
}

public class RelayCommand : ICommand
{
    private readonly Action<Object> _execute;
    private readonly Func<bool> _canExecute;

    public event EventHandler CanExecuteChanged;

    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    public RelayCommand(Action<object> execute, Func<bool> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute();
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        var handler = CanExecuteChanged;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

问题现象

首次设置选中项时功能正常,但再次切换选中项(例如从“活跃”切换到“离开”后),数据绑定会失效,无法正确更新RadioMenuFlyoutItem的选中状态。

复现步骤

  • 启动测试项目
  • 查看默认选中的菜单项
  • 点击任意其他状态按钮(比如“活跃”)
  • 确认菜单中的选中状态显示正确
  • 点击任意其他状态按钮(比如“离线”)
  • 确认菜单中的选中状态显示正确
  • 点击任意其他状态按钮(比如“活跃”)
  • 实际结果:“活跃”菜单项未被选中
  • 预期结果:“活跃”菜单项被选中

预期结果

切换ViewModel中的Presence属性时,对应的RadioMenuFlyoutItem应始终正确显示选中状态,比如点击“活跃”按钮后,“活跃”菜单项应被选中。

内容的提问来源于stack exchange,提问作者Valerii Nozdrenkov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:20:21