如何使用DelegateCommand将信息从View传递到ViewModel?

13

在我的视图中,我有一个按钮。

当用户点击此按钮时,我希望ViewModel将TextBlock的上下文保存到数据库中。

<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top">
    <TextBlock Text="{Binding FirstName}"/>
    <TextBox Text="Save this text to the database."/>
    <Button Content="Save" Command="{Binding SaveCommand}"/>
</StackPanel>

然而,在我的ViewModel中的DelegateCommand中,“Save()”方法不会传递任何参数,那么在这一点上我如何从视图获取数据?

#region DelegateCommand: Save
private DelegateCommand saveCommand;

public ICommand SaveCommand
{
    get
    {
        if (saveCommand == null)
        {
            saveCommand = new DelegateCommand(Save, CanSave);
        }
        return saveCommand;
    }
}

private void Save()
{
    TextBox textBox = ......how do I get the value of the view's textbox from here?....
}

private bool CanSave()
{
    return true;
}
#endregion
6个回答

20

查看Josh Smith的这篇MSDN文章。 在文章中,他展示了一种称为RelayCommand的DelegateCommand变体,其中RelayCommand上的Execute和CanExecute委托接受一个类型为object的单个参数。

使用RelayCommand,您可以通过CommandParameter向委托传递信息:

<Button Command="{Binding SaveCommand}" 
        CommandParameter="{Binding SelectedItem,Element=listBox1}" />

更新

根据这篇文章,似乎有一个通用版本的DelegateCommand可以以类似的方式接受参数。您可能想尝试将SaveCommand更改为DelegateCommand<MyObject>,并更改Save和CanSave方法以使其接受MyObject参数。


2
我通过将TextBox绑定到一个ViewModel Property (实现INotifyPropertyChanged接口)来解决了问题,Save()命令当然可以访问该值。但是您提出的建议非常有趣,我会去尝试一下。 - Edward Tanguay
有一个特定的(并且相当常见)用例,Matt的解决方案绝对胜过访问单独的绑定值:在ItemsControl内执行的命令。在这里,例如,在ListView中的按钮可以使用其DataContext作为参数,并将被点击的项目随命令一起传递。否则很难确定哪个项目的按钮被点击了。 - Bob Sammers
1
请注意,通用的 DelegateCommand<T> 有一个重要的限制(直到运行时才会发现 - 编译器不会抱怨):您不能使用值类型作为参数。官方建议是使用可空的值类型(允许使用),并在使用之前检查其是否包含值(https://msdn.microsoft.com/zh-cn/library/gg431410%28v=pandp.50%29.aspx - 见“备注”部分)。 - Bob Sammers

13

这是一种优雅的方式。

给你的文本框命名,然后将按钮中的CommandParameter绑定到它的Text属性:

<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top">
    <TextBlock Text="{Binding FirstName}"/>
    <TextBox x:Name="ParameterText" Text="Save this text to the database."/>
    <Button Content="Save" Command="{Binding SaveCommand}"
            CommandParameter="{Binding Text, ElementName=ParameterText}"/>
</StackPanel>

12

在您的虚拟机中:

private DelegateCommand<string> _saveCmd = new DelegateCommand<string>(Save);

public ICommand SaveCmd{ get{ return _saveCmd } }

public void Save(string s) {...}
在您的 View 中,使用 CommandParameter,就像 Matt 的示例一样。

这不是一个完整的答案,但它指引我正确地使用CommandParameter并为DelegateCommand指定类型。谢谢。 - vdidxho

5
你在询问如何通过按钮命令传递数据。 实际上,我认为你想要的是将你的文本框文本与ViewModel中的公共属性进行绑定。
<!-- View: TextBox's text is bound to the FirstName property in your ViewModel -->
<TextBox Text="{Binding Path=FirstName}" />
<Button Command="{Binding SaveCommand}"/>

<!-- ViewModel: Expose a property for the TextBox to bind to -->
public string FirstName{ get; set; }
...
private void Save()
{
    //textBox's text is bound to --> this.FirstName;
}

这是我通常的做法,因为TextBox的值已经绑定到viewmodel中的属性。没有必要将它作为参数传递给命令。 - sourcenouveau
如果您只是为了一个目的使用文本,将其作为参数传递会更有意义(代码可读性)。例如,将激活密钥传递给您的虚拟机。 - Bassem Akl

1

我想我还不能发表评论。我是在回应Carlos的建议,因为我试过了。虽然这是个好主意,但DelegateCommand需要进行一些修改,否则你会得到以下错误: 字段初始化器无法引用非静态字段、方法或属性'MyViewModel.Save(string)'。


0

在视图中绑定的网格内的文本块:

                            <telerik:GridViewDataColumn Header="Anunciante">
                            <telerik:GridViewDataColumn.CellTemplate>
                                <DataTemplate>
                                    <Grid Background="{Binding AnuncianteColor}" Margin="0,7,0,7" VerticalAlignment="Center"  >
                                        <TextBlock Text="{Binding Anunciante}" 
                                                   Padding="5,10,5,10" 
                                                   HorizontalAlignment="Stretch" 
                                                   TextAlignment="Center"
                                                   Tag="{Binding Index}"
                                                   MouseDown="AnuncianteMouseDown" />
                                    </Grid>
                                </DataTemplate>
                            </telerik:GridViewDataColumn.CellTemplate>
                        </telerik:GridViewDataColumn>

和一个隐藏的按钮绑定命令

<Button Visibility="Collapsed" x:Name="AnunciantedoubleClick" Command="{Binding AnuncianteClickCommand}"></Button>

代码后台

        private void AnuncianteMouseDown(object sender, MouseButtonEventArgs e) 
    {
        //if (e.ClickCount == 2)
        //{
            this.AnunciantedoubleClick.Command.Execute(((TextBlock)sender).Tag);
        //}
    }

最后在 ModelView 中编写代码:

public ICommand AnuncianteClickCommand { get; }

在构造函数中:

this.AnuncianteClickCommand = new DelegateCommand<object>(this.AnuncianteClickDelegate);

并且委托将显示带有行索引的消息(自定义消息框服务,抱歉)

        private void AnuncianteClickDelegate(object v) 
    {
        MessageBoxService.Show(v.ToString());
    }

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