WPF Combobox的SelectedIndexChanged事件不触发(MVVM+Expression.Blend)
WPF中ComboBox绑定SelectedIndexChanged事件命令未执行问题
问题描述
刚接触WPF,已掌握MVVM与Expression.Blend,尝试通过InvokeCommandAction将ComboBox的SelectedIndexChanged事件绑定到ViewModel中的SetBuildingComponentConstruction命令,但命令代码始终未执行(myvalue值从未更新),求问题原因及解决办法。
相关代码
XAML代码
<UserControl x:Class="GDX.UI.Views.AnalyseView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:i1="http://schemas.microsoft.com/xaml/behaviors" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:local="clr-namespace:GDX.UI.Views" mc:Ignorable="d" Height="720" Width="1210"> <Control.Resources> <ResourceDictionary Source="pack://application:,,,/GDX.UI;component/ResourceDictionary/ResourceDictionary.xaml"/> </Control.Resources> <materialDesign:Card Margin="15,15,15,15" Grid.Column ="0" Grid.Row="0" Grid.RowSpan="6" Grid.ColumnSpan="2"> <ScrollViewer Grid.Row="0" Grid.RowSpan="5" Grid.Column="0" Grid.ColumnSpan="2" VerticalScrollBarVisibility="Hidden"> <ItemsControl ItemsSource="{Binding buildingComponents}"> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation="Vertical" Margin="10"> <TextBlock Margin="10" Text="{Binding Path=UIName}" FontWeight="Bold"/> <ComboBox ItemsSource="{Binding Path=ConstructionOptions}" SelectedItem="{Binding Path=Construction}" HorizontalAlignment="Center" Style="{StaticResource MaterialDesignOutlinedComboBox}" Width="256" Height="50" BorderBrush="Red" materialDesign:HintAssist.Hint="Baukonstruktion"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectedIndexChanged"> <i:InvokeCommandAction Command="{Binding SetBuildingComponentConstruction}"/> </i:EventTrigger> </i:Interaction.Triggers> </ComboBox> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </ScrollViewer> </materialDesign:Card> </UserControl>
ViewModel代码
using GDX.IO.Schemas; using GDX.IO.Statics; using GDX.MVVM; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; namespace GDX.ViewModel.ViewModels { public class AnalyseViewModel : BaseViewModel { public ObservableCollection<BuildingComponent> buildingComponents { get; set; } public double myvalue { get; set; } public ICommand SetBuildingComponentConstruction { get; set; } public AnalyseViewModel() { this.buildingComponents = new ObservableCollection<BuildingComponent>(); if (Building.BuildingComponents.Count != 0) //if contains elements { foreach (BuildingComponent bcomp in Building.BuildingComponents) { this.buildingComponents.Add(bcomp); } } this.SetBuildingComponentConstruction = new RelayCommand(this.setBuildingComponentConstruction); this.myvalue = 0; } public void setBuildingComponentConstruction() { myvalue += 1; } } }
问题原因
- 数据上下文不匹配:ComboBox位于
ItemsControl的DataTemplate内部,此时它的DataContext是集合中的单个BuildingComponent对象,而SetBuildingComponentConstruction命令定义在AnalyseViewModel中,直接绑定会找不到命令,导致无法执行。 myvalue未实现属性通知:即使命令执行,myvalue属性没有实现INotifyPropertyChanged,UI也无法感知到值的变化。
解决办法
方法一:修正命令绑定的数据源
修改InvokeCommandAction的Command绑定,通过RelativeSource指定父级ItemsControl的DataContext(即AnalyseViewModel):
<i:InvokeCommandAction Command="{Binding DataContext.SetBuildingComponentConstruction, RelativeSource={RelativeSource AncestorType=ItemsControl}}"/>
方法二:利用SelectedItem绑定(更符合MVVM规范)
由于ComboBox已经绑定了SelectedItem="{Binding Construction}",可以直接在BuildingComponent类的Construction属性的setter中处理逻辑,或者在ViewModel中监听属性变化:
方案1:修改BuildingComponent类(需继承INotifyPropertyChanged)
private Construction _construction; public Construction Construction { get => _construction; set { if (_construction != value) { _construction = value; OnPropertyChanged(nameof(Construction)); // 在这里添加选中变化后的逻辑,或者通过事件通知ViewModel } } }
方案2:在AnalyseViewModel中监听属性变化
public AnalyseViewModel() { this.buildingComponents = new ObservableCollection<BuildingComponent>(); if (Building.BuildingComponents.Count != 0) { foreach (BuildingComponent bcomp in Building.BuildingComponents) { this.buildingComponents.Add(bcomp); // 绑定PropertyChanged事件 bcomp.PropertyChanged += BuildingComponent_PropertyChanged; } } this.SetBuildingComponentConstruction = new RelayCommand(this.setBuildingComponentConstruction); this.myvalue = 0; } private void BuildingComponent_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == nameof(BuildingComponent.Construction)) { myvalue += 1; // 通知UI更新myvalue OnPropertyChanged(nameof(myvalue)); } }
补充:修复myvalue的属性通知
无论哪种方法,都需要给myvalue添加INotifyPropertyChanged实现,确保UI能更新:
private double _myvalue; public double myvalue { get => _myvalue; set { _myvalue = value; OnPropertyChanged(nameof(myvalue)); } }
内容的提问来源于stack exchange,提问作者aai
相关产品推荐
相关产品推荐

