【问题标题】:Untangle EventToCommand on two controls with MVVM Light使用 MVVM Light 解开两个控件上的 EventToCommand
【发布时间】:2013-10-29 15:12:02
【问题描述】:

您好,我是 WPF 的新手,我已经开始使用基于 mvvm light 框架的 mvvm 模式实现应用程序。我发现它很棒,但是在应该交互的控件上使用两个 EventToCommand 时遇到了问题。我猜我做错了什么......你能帮我找出究竟是什么吗?

我有一个带有两个控件的窗口:一个允许选择 Name 的组合框和一个显示 Caption 的 TextBox。名称具有默认标题(在 ViewModel 的构造函数中为示例硬编码,见下文)。因此,当用户选择名称时,文本框应显示默认标题。但是,标题是可编辑的,这意味着用户可以更改标题(只要他不再更改名称)。

在下面的示例中,我使用 MVVM 模式和 MVVM Light 框架实现了这一点。 Ok 按钮仅绑定到在 OutPut 窗口中记录值的命令(以查看 ViewModel 中的属性值)。

正如您将在源代码注释中看到的那样,问题出在 NameSelectionChanged 命令使用“过期值”触发 CaptionTextChanged 命令这一事实。目前,我通过设置一个布尔值来实现一个 hacky 解决方法(不在下面的代码中),该值在 NameSelectionChanged 中执行 RaisePropertyChanged 时忽略 CaptionTextChanged 中的代码,但这并不令人满意。

XAML 中的视图

<Window x:Class="TwoControls.MainWindow"
         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:vm="clr-namespace:TwoControls"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras"
        mc:Ignorable="d" 
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <vm:DummyViewModelLocator x:Key="Locator" d:IsDataSource="True" />
    </Window.Resources>
    <Window.DataContext>
        <Binding Path="GetViewModel" Source="{StaticResource Locator}" />
    </Window.DataContext>
    <Grid>
        <ComboBox ItemsSource="{Binding ColumnNames}"  x:Name="NamesComboBox" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="SelectionChanged">
                    <cmd:EventToCommand Command="{Binding NameSelectionChanged}" CommandParameter="{Binding SelectedValue, ElementName=NamesComboBox}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </ComboBox>
        <TextBox Text="{Binding Caption, Mode=TwoWay}" Name="CaptionTextBox"  HorizontalAlignment="Left" Height="23" Margin="0,45,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="TextChanged">
                    <cmd:EventToCommand Command="{Binding CaptionTextChanged}" CommandParameter="{Binding Text, ElementName=CaptionTextBox}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </TextBox>
        <Button Content="Ok" Command="{Binding ClickOk}" HorizontalAlignment="Left" Margin="120,170,0,0" VerticalAlignment="Top" Width="75"/>
    </Grid>
</Window>

C# 中的视图模型

public class MainViewModel : ViewModelBase
        {
            private readonly List<string> _names;
            private readonly string[] _captions;

            public MainViewModel()
            {
                _names = new List<string>(new[]{"TOTO","TATA","TUTU"});
                _captions = new[] {"toto", "tata", "tutu"};
            }

            public string Name { get; set; }

            public string Caption { get; set; }

            public ICommand NameSelectionChanged
            {
                get
                {
                    return new RelayCommand<string>((input) =>
                        {
                            Name = input;
                            int index = _names.IndexOf(input);
                            Caption = _captions[index];

                            //Trigger the execution of CaptionTextChanged with the old value of the TextBox
                            //even if Caption and TextBox.Text are bound TwoWay....
                            base.RaisePropertyChanged(()=>this.Caption);
                        });
                }
            }

            public ICommand CaptionTextChanged
            {
                get
                {
                    return new RelayCommand<string>((input) =>
                        {
                            Caption = input;
                        });
                }
            }


            public ICommand ClickOk
            {
                get
                {
                    return new RelayCommand(() =>
                    {
                        Console.WriteLine("Name=" + Name +";" +"Caption=" + Caption);
                    });
                }
            }

            public List<string> ColumnNames
            {
                get { return _names; }
            } 
        }

Ps:目标.NET是3.5,MVVMLight的版本是4.1.27.1

【问题讨论】:

    标签: c# wpf mvvm mvvm-light


    【解决方案1】:

    您不需要在 WPF 中使用任何事件来执行此操作。所有这一切都可以通过使用属性Bindings 来实现如果你按照惯例在视图模型上实现INotifyPropertyChanged 接口...怎么样:

    private ObservableCollection<string> columnNames = new 
        ObservableCollection<string>();
    public ObservableCollection<string> ColumnNames
    {
        get { return columnNames; }
        set { columnNames = value; NotifyPropertyChanged("ColumnNames"); }
    }
    
    private string selectedColumnName;
    public string SelectedColumnName
    {
        get { return selectedColumnName; }
        set 
        { 
            selectedColumnName = value;
            NotifyPropertyChanged("SelectedColumnName");
            int index = _names.IndexOf(value); // <<< Changes are reflected here
            Caption = _captions[index];
        }
    }
    
    private string caption = string.Empty;
    public string Caption 
    {
        get { return caption; }
        set { caption = value; NotifyPropertyChanged("Caption"); }
    }
    

    在 XAML 中:

    <Grid>
        <ComboBox ItemsSource="{Binding ColumnNames}" SelectedItem="{Binding 
            SelectedColumnName}" x:Name="NamesComboBox" HorizontalAlignment="Left" 
            VerticalAlignment="Top" Width="120" />
        <TextBox Text="{Binding Caption, Mode=TwoWay}" Name="CaptionTextBox" 
            HorizontalAlignment="Left" Height="23" Margin="0,45,0,0" TextWrapping="Wrap" 
            VerticalAlignment="Top" Width="120" />
        <Button Content="Ok" Command="{Binding ClickOk}" HorizontalAlignment="Left" 
            Margin="120,170,0,0" VerticalAlignment="Top" Width="75"/>
    </Grid>
    

    告诉我进展如何。

    【讨论】:

    • 太完美了。实际上,我使用 EventToCommand 来获取 TextChanged 事件,以便根据标题的值启用/禁用 Ok 按钮(在上面的示例中未表示)。默认情况下,source 的属性只有在失去焦点后才会更新,错误来自我缺少 UpdateSourceTrigger 属性stackoverflow.com/questions/8912456/… 谢谢
    猜你喜欢
    • 2017-12-18
    • 1970-01-01
    • 2012-07-08
    • 1970-01-01
    • 2013-02-25
    • 2012-02-07
    • 1970-01-01
    • 2011-03-09
    • 1970-01-01
    相关资源
    最近更新 更多