WPF - MVVM:SelectionChanged后ComboBox的值

9
我刚接触C#和MVVM,一整天都在尝试获取ComboBox在SelectionChanged时的值并将其传递给ViewModel。我已经成功使用以下资源中的CallMethodAction或InvokeCommandAction解决了问题:
  • System.Windows.Interactivity.dll
  • Microsoft.Expression.Interactions.dll
但是我的问题是,这两种方法都只能获取ComboBox在变化之前的值。请问有没有办法获取变化后的值呢?我在Stack Overflow和Google上搜索了很久,也许其他人也有同样的困扰。非常感谢您的建议!
以下是我的代码:

MainWindow.xaml

<Window x:Class="SelectionChange.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:si="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
        xmlns:vm="clr-namespace:SelectionChange"
        Title="MainWindow" Width="300" Height="300">
    <Window.DataContext>
        <vm:ViewModel />
    </Window.DataContext>
    <Grid>
        <ComboBox Name="SelectBox" VerticalAlignment="Top" SelectedIndex="0">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="SelectionChanged">
                    <si:CallMethodAction MethodName="SelectionChanged" TargetObject="{Binding}" />
                    <!--<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}" CommandParameter="{Binding ElementName=SelectBox, Path=Text}" />-->
                </i:EventTrigger>
            </i:Interaction.Triggers>
            <ComboBoxItem Content="Item 1" />
            <ComboBoxItem Content="Item 2" />
            <ComboBoxItem Content="Item 3" />
        </ComboBox>
    </Grid>
</Window>

ViewModel.cs

namespace SelectionChange
{
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;

    public class ViewModel
    {
        public ViewModel()
        {
            SelectionChangedCommand = new SelectionChangedCommand();
        }

        public ICommand SelectionChangedCommand
        {
            get;
            set;
        }

        public void SelectionChanged(object sender, EventArgs e)
        {
            ComboBox SelectBox = (ComboBox)sender;
            MessageBox.Show("Called SelectionChanged: " + SelectBox.Text);
        }
    }
}

SelectionChangedCommand.cs

namespace SelectionChange
{
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;

    public class SelectionChangedCommand : ICommand
    {
        public SelectionChangedCommand()
        {
        }

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            MessageBox.Show("Executed SelectionChangedCommand: " + parameter);
        }
    }
}




编辑:我的解决方案

事实证明,我对Binding的理解不够充分,而是试图以一种相当复杂的方式实现简单的东西!现在,我已经使用常规绑定实现了所需的内容,而不是使用依赖项。例如,我将一个TextBox绑定到ComboBoxSelectedIndex上,这个绑定使用INotifyPropertyChanged进行更新。

MainWindow.xaml截图

MainWindow.xaml

<Window x:Class="SelectionChange.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:SelectionChange"
        Title="MainWindow" Width="300" Height="300">
    <Window.DataContext>
        <vm:ViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <ComboBox SelectedItem="{Binding SelectedItem}" SelectedIndex="0" Grid.Column="0" VerticalAlignment="Top">
            <ComboBoxItem Content="Item 1" />
            <ComboBoxItem Content="Item 2" />
            <ComboBoxItem Content="Item 3" />
        </ComboBox>

        <!-- TextBox to display the ComboBox's SelectedIndex -->
        <TextBox Text="{Binding SelectedIndex}" Grid.Column="1" VerticalAlignment="Top" />
    </Grid>
</Window>

ViewModel.cs

namespace SelectionChange
{
    using System;
    using System.ComponentModel;
    using System.Windows.Controls;

    public class ViewModel : INotifyPropertyChanged
    {
        public ViewModel()
        {   
        }

        // Property to store / retrieve ComboBox's SelectedIndex
        private int _SelectedIndex;
        public int SelectedIndex { get; set; }

        // Property to bind to ComboBox's SelectedItem
        private ComboBoxItem _SelectedItem;
        public ComboBoxItem SelectedItem
        {
            get { return _SelectedItem; }
            set
            {
                _SelectedItem = value;

                // SelectedItem's Content
                string Content = (string)value.Content;

                // SelectedItem's parent (i.e. the ComboBox)
                ComboBox SelectBox = (ComboBox)value.Parent;

                // ComboBox's SelectedIndex
                int Index = SelectBox.SelectedIndex;

                // Store the SelectedIndex in the property
                SelectedIndex = Index;

                // Raise PropertyChanged with the name of the stored property
                RaisePropertyChanged("SelectedIndex");
            }
        }

        // INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(string PropertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }
    }
}

如果我理解你想做什么,我认为这会有所帮助 https://dev59.com/ZVPTa4cB1Zd3GeqPk54v - kenny
我只是给你提个建议。你可以将 SelectedItems 属性绑定到 ViewModel 中的属性,使用双向绑定模式。不需要监听 SelectionChanged 事件或创建命令。每当 SelectedItems 值发生变化时,你会在 ViewModel 中的属性 setter 内部得到通知。试一试吧。 - dev hedgehog
尝试类似于以下内容的代码:<ComboBox ItemsSource="{Binding Path=PropertyWhichHasList, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" SelectedItem="{Binding PropertyWhichHasSelectedItem}"></ComboBox>。 - srsyogesh
感谢所有三个建议!它们帮助我找到了解决方案。 - Alistair Tweed
2个回答

19

为什么不用更简单的方式呢?

<ComboBox MaxHeight="25" 
          ItemsSource="{Binding Source}" 
          SelectedItem="{Binding TheSelectedItem, Mode=TwoWay}" />
在你的ViewModel中声明组合框项目,并使用一个名为 "Source" 的属性将其返回到视图。
List<string> _source = new List<string>{"Item 1", "Item 2", "Item 3"};
public List<string> Source 
{ 
    get { return _source; } 
}

然后定义一个属性来保存所选项目

string _theSelectedItem = null;
public string TheSelectedItem 
{ 
    get { return _theSelectedItem; } 
    set { _theSelectedItem = value; } // NotifyPropertyChanged
}

同时在设置_source时不要忘记实现INotifyPropertyChanged接口。


这是我最终得出的最接近的结果,我猜,根据你想要用ComboBox做什么,这可能是解决方案(对于其他人)。@aks81,感谢您的回答! - Alistair Tweed

1
如果您只想在当前项更改时收到通知,为什么不使用已经包含在WPF中的工具,而不是所有这些依赖关系。首先获取到集合的基础视图并在其上使用CurrentChanged事件。
ComboBoxList = new ObservableCollection<string>();
var view = CollectionViewSource.GetDefaultView(ComboBoxList);
view.MoveCurrentTo(ComboBoxList[0]);
view.CurrentChanged += new EventHandler(ComboBoxCurrentChanged);

在您的XAML中,只需绑定集合并将IsSynchronizedWithCurrentItem设置为true即可。
<ComboBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding ComboBoxList}"/>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接