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

ItemsControl中MultiBinding Converter未触发问题求助

问题:SelectedItem变更时无法触发SelectedItemConverter

SelectedItem值发生变更时,无法触发SelectedItemConverter。尝试过使用SetCurrentValue和InvalidateProperty方法,也调整了NotifyOnSourceUpdated、UpdateSourceTrigger以及Mode等属性,但均未解决问题。是否需要在父级XAML中添加相关配置?

父级XAML代码

<local:Menu
        SelectedItem="{Binding SelectedMenuItem}"
        OnSelect="{Binding OnSelectCommand}" Items="{Binding MenuItems}"/>

自定义Menu控件代码

控件XAML

<UserControl>
 <UserControl.Resources>
        <vm:MockMenuViewModel x:Key="DesignViewModel"/>
        <cv:SelectedItemConverter x:Key="SelectedItemConverter"/>
    </UserControl.Resources>
    <ItemsControl
        d:DataContext="{Binding Source={StaticResource DesignViewModel}}"
        ItemsSource="{Binding Path=MenuItems, Mode=OneWay}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel></StackPanel>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="{x:Type MenuItem}">
                <Button Content="{Binding Name}"
                        Command="{Binding OnSelect, RelativeSource={RelativeSource AncestorType=UserControl}}"
                        CommandParameter="{Binding}">
                    <Button.IsEnabled>
                        <MultiBinding Converter="{StaticResource SelectedItemConverter}"
                                      NotifyOnSourceUpdated="True"
                                      NotifyOnTargetUpdated="True" Mode="OneWay">
                            <Binding Path="SelectedItem" UpdateSourceTrigger="PropertyChanged" Mode="OneWay"/>
                            <Binding Path="." Mode="OneWay"/>
                        </MultiBinding>
                    </Button.IsEnabled>
                </Button>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</UserControl>

控件C#代码

public partial class Menu : UserControl, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    public Menu()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
        "SelectedItem",
        typeof(MenuItem),
        typeof(Menu),
        new PropertyMetadata(null, OnSelectedItemChanged));

    public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(
        "Items",
        typeof(IEnumerable<MenuItem>),
        typeof(Menu),
        new FrameworkPropertyMetadata(null));

    public static readonly DependencyProperty OnSelectProperty = DependencyProperty.Register(
        "OnSelect",
        typeof(ICommand),
        typeof(Menu),
        new PropertyMetadata(null, OnSelectChanged));


    public MenuItem SelectedItem
    {
        get => (MenuItem)GetValue(SelectedItemProperty);
        set
        {
            SetValue(SelectedItemProperty, value);
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Items)));
        }
    }

    public IEnumerable<MenuItem> Items
    {
        get => (IEnumerable<MenuItem>)GetValue(ItemsProperty);
        set => SetValue(ItemsProperty, value);
    }

    public ICommand OnSelect
    {
        get => (ICommand)GetValue(OnSelectProperty);
        set => SetValue(OnSelectProperty, value);
    }

    public static void OnSelectedItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
    {
        var menu = dependencyObject as Menu;

        if (menu == null)
        {
            return;
        }

        menu.InvalidateProperty(SelectedItemProperty);
        menu.InvalidateProperty(ItemsProperty);
    }

    public static void OnSelectChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
    {
        var menu = dependencyObject as Menu;

        if (menu == null)
        {
            return;
        }

        menu.SetCurrentValue(OnSelectProperty, eventArgs.NewValue);
    }
}

解决方案

1. 修正MultiBinding中SelectedItem的绑定

DataTemplate的DataContext是单个MenuItem实例,直接绑定SelectedItem会找不到该属性,必须通过RelativeSource指向父级Menu控件:

<MultiBinding Converter="{StaticResource SelectedItemConverter}"
              NotifyOnSourceUpdated="True"
              NotifyOnTargetUpdated="True" Mode="OneWay">
    <!-- 添加RelativeSource指向Menu控件 -->
    <Binding Path="SelectedItem" RelativeSource="{RelativeSource AncestorType=local:Menu}" Mode="OneWay"/>
    <Binding Path="." Mode="OneWay"/>
</MultiBinding>

2. 修正ItemsControl的ItemsSource绑定

自定义Menu控件的数据源依赖属性是Items,而非MenuItems,需要绑定到控件自身的Items属性:

<ItemsControl
    d:DataContext="{Binding Source={StaticResource DesignViewModel}}"
    <!-- 修改为绑定自身的Items属性 -->
    ItemsSource="{Binding Path=Items, RelativeSource={RelativeSource AncestorType=UserControl}, Mode=OneWay}">

3. 移除无效的属性通知代码

  • 删除SelectedItem属性setter中错误的PropertyChanged触发代码:
    public MenuItem SelectedItem
    {
        get => (MenuItem)GetValue(SelectedItemProperty);
        set
        {
            SetValue(SelectedItemProperty, value);
            // 移除这行错误的通知
            // PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Items)));
        }
    }
    
  • 删除OnSelectedItemChanged中无效的InvalidateProperty调用(会导致不必要的循环):
    public static void OnSelectedItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
    {
        var menu = dependencyObject as Menu;
        if (menu == null) return;
        // 移除以下两行无效代码
        // menu.InvalidateProperty(SelectedItemProperty);
        // menu.InvalidateProperty(ItemsProperty);
    }
    

4. 可选:显式指定父级绑定的Mode

父级XAML中可显式指定SelectedItem的绑定Mode为TwoWay(确保双向同步):

<local:Menu
    SelectedItem="{Binding SelectedMenuItem, Mode=TwoWay}"
    OnSelect="{Binding OnSelectCommand}" 
    Items="{Binding MenuItems}"/>

内容的提问来源于stack exchange,提问作者Józef Podlecki

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 13:50:26