【问题标题】:Binding Button click to a method绑定按钮单击到方法
【发布时间】:2011-04-01 16:47:25
【问题描述】:

我有一个绑定到可观察对象集合的数据网格。我想要做的是有一个按钮,它将执行对象的方法,该方法表示被单击的按钮的行。所以我现在拥有的是这样的:

            <DataGridTemplateColumn Header="Command">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Button Name="cmdCommand" Click="{Binding Command}" 
                                Content="Command"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

哪个不行,报如下错误:

Click="{绑定命令}" 无效。 “{Binding Command}”不是有效的事件处理程序方法名称。只有生成类或代码隐藏类的实例方法才有效。

我查看了命令绑定,但看起来它最终会转到单个外部命令而不是绑定到行的对象。我让它在后面的代码上使用事件处理程序工作,然后将其路由到绑定到所选行的项目(因为单击按钮时该行被选中)但这似乎是一种糟糕的处理方式,我假设我'我只是在这里遗漏了一些东西。

【问题讨论】:

  • 后面代码中的“命令”是什么?该命令应该实现什么?选择一行?
  • 在这种情况下,“命令”不在后面的代码中。我试图把它降低到解释所需的最低限度,所以在这种情况下,它只是我绑定到数据网格的类的一个方法(所以它会是 myObject.Command();)。
  • 至于它应该实现什么,现在只是为了测试它只是更新对象中触发 propertychanged 事件的另一个属性。
  • 我创建了一个库,允许您使用以下语法 {BindTo SaveObject}。你可以在这里找到它:simplygoodcode.com/2012/08/simpler-wpf-binding.html
  • 令我惊讶的是,这是响应 MVVM 中的按钮单击所必需的。他们有没有说明它与其他绑定不同的原因?

标签: wpf


【解决方案1】:

我一直这样做。下面是一个示例以及如何实现它。

更改您的 XAML 以使用按钮的 Command 属性而不是 Click 事件。我使用的是 SaveCommand 这个名字,因为它比 Command 更容易理解。

<Button Command="{Binding Path=SaveCommand}" />

Button 绑定到的 CustomClass 现在需要具有类型为 ICommand 的名为 SaveCommand 的属性。它需要指向执行命令时要运行的 CustomClass 上的方法。

public MyCustomClass
{
    private ICommand _saveCommand;

    public ICommand SaveCommand
    {
        get
        {
            if (_saveCommand == null)
            {
                _saveCommand = new RelayCommand(
                    param => this.SaveObject(), 
                    param => this.CanSave()
                );
            }
            return _saveCommand;
        }
    }

    private bool CanSave()
    {
        // Verify command can be executed here
    }

    private void SaveObject()
    {
        // Save command execution logic
    }
}

上面的代码使用了一个 RelayCommand,它接受两个参数:要执行的方法,以及命令是否可以执行的真/假值。 RelayCommand 类是一个单独的 .cs 文件,其代码如下所示。我是从 Josh Smith 那里得到的 :)

/// <summary>
/// A command whose sole purpose is to 
/// relay its functionality to other
/// objects by invoking delegates. The
/// default return value for the CanExecute
/// method is 'true'.
/// </summary>
public class RelayCommand : ICommand
{
    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;        

    #endregion // Fields

    #region Constructors

    /// <summary>
    /// Creates a new command that can always execute.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    /// <summary>
    /// Creates a new command.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    /// <param name="canExecute">The execution status logic.</param>
    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;           
    }

    #endregion // Constructors

    #region ICommand Members

    [DebuggerStepThrough]
    public bool CanExecute(object parameters)
    {
        return _canExecute == null ? true : _canExecute(parameters);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameters)
    {
        _execute(parameters);
    }

    #endregion // ICommand Members
}

【讨论】:

  • 这比我想象的要复杂得多。我如何将它连接到对象中的命令?该类是否需要被绑定对象公开?>
  • +1 但是,这可能需要更多关于如何使用的解释。
  • HCL:同意,如何将其连接到视图中?这在我绑定的对象中究竟是如何使用的?似乎这可能是要走的路,但我不知道如何为我当前的需求实现它。谢谢!
  • @Sinaesthetic 这是一个事件定义。事件有 addremove,就像属性有 getset。它只是告诉它在您向事件添加或删除方法时要做什么。
  • 请注意:以下是有关此代码缺点的 cmets。但我不能说对典型使用的影响有多大。 stackoverflow.com/questions/2281566/…
【解决方案2】:

你有各种各样的可能性。最简单最丑的就是:

XAML

<Button Name="cmdCommand" Click="Button_Clicked" Content="Command"/> 

代码背后

private void Button_Clicked(object sender, RoutedEventArgs e) { 
    FrameworkElement fe=sender as FrameworkElement;
    ((YourClass)fe.DataContext).DoYourCommand();     
} 

另一种解决方案(更好的)是在您的YourClass 上提供一个 ICommand 属性。该命令已经引用了您的YourClass-object,因此可以对此类执行操作。

XAML

<Button Name="cmdCommand" Command="{Binding YourICommandReturningProperty}" Content="Command"/>

因为在写这个答案的过程中,发布了很多其他的答案,我不再写了。如果您对我展示的其中一种方式感兴趣,或者您认为我犯了错误,请发表评论。

【讨论】:

  • 对于 Icommand 的想法,你会用下面 Rachel 的想法来实现它吗?
  • Rachel 向您展示了解决此类问题的基础设施。还有其他方法可以做到这一点,但我发现值得花时间尝试按照她向您展示的方式理解和实施它。
  • 如果您使用基于 ICommand 的属性,它不是“Command”,而不是“Click”吗?
  • 我尝试了“丑陋”的方式来看看我是否可以使用我父母之外的另一个类,因为我有一个 UserControl 并且很好奇我是否可以做类似((UserControl1)fe.DataContext).myStackPanel.Visibility = Visibility.Hidden; 的事情。它编译,但它没有工作。当我单击该按钮时,该行显示Object reference not set to an instance of an object 失败。所以它实际上一定没有得到参考。
【解决方案3】:

这是上面 Rachel 答案的 VB.Net 版本。

显然 XAML 绑定是一样的...

<Button Command="{Binding Path=SaveCommand}" />

您的自定义类看起来像这样...

''' <summary>
''' Retrieves an new or existing RelayCommand.
''' </summary>
''' <returns>[RelayCommand]</returns>
Public ReadOnly Property SaveCommand() As ICommand
    Get
        If _saveCommand Is Nothing Then
            _saveCommand = New RelayCommand(Function(param) SaveObject(), Function(param) CanSave())
        End If
        Return _saveCommand
    End Get
End Property
Private _saveCommand As ICommand

''' <summary>
''' Returns Boolean flag indicating if command can be executed.
''' </summary>
''' <returns>[Boolean]</returns>
Private Function CanSave() As Boolean
    ' Verify command can be executed here.
    Return True
End Function

''' <summary>
''' Code to be run when the command is executed.
''' </summary>
''' <remarks>Converted to a Function in VB.net to avoid the "Expression does not produce a value" error.</remarks>
''' <returns>[Nothing]</returns>
Private Function SaveObject()
    ' Save command execution logic.
   Return Nothing
End Function

最后RelayCommand类如下...

Public Class RelayCommand : Implements ICommand

ReadOnly _execute As Action(Of Object)
ReadOnly _canExecute As Predicate(Of Object)
Private Event ICommand_CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged

''' <summary>
''' Creates a new command that can always execute.
''' </summary>
''' <param name="execute">The execution logic.</param>
Public Sub New(execute As Action(Of Object))
    Me.New(execute, Nothing)
End Sub

''' <summary>
''' Creates a new command.
''' </summary>
''' <param name="execute">The execution logic.</param>
''' <param name="canExecute">The execution status logic.</param>
Public Sub New(execute As Action(Of Object), canExecute As Predicate(Of Object))
    If execute Is Nothing Then
        Throw New ArgumentNullException("execute")
    End If
    _execute = execute
    _canExecute = canExecute
End Sub

<DebuggerStepThrough>
Public Function CanExecute(parameters As Object) As Boolean Implements ICommand.CanExecute
    Return If(_canExecute Is Nothing, True, _canExecute(parameters))
End Function

Public Custom Event CanExecuteChanged As EventHandler
    AddHandler(ByVal value As EventHandler)
        AddHandler CommandManager.RequerySuggested, value
    End AddHandler
    RemoveHandler(ByVal value As EventHandler)
        RemoveHandler CommandManager.RequerySuggested, value
    End RemoveHandler
    RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
        If (_canExecute IsNot Nothing) Then
            _canExecute.Invoke(sender)
        End If
    End RaiseEvent
End Event

Public Sub Execute(parameters As Object) Implements ICommand.Execute
    _execute(parameters)
End Sub

End Class

希望对任何 VB.Net 开发人员有所帮助!

【讨论】:

    【解决方案4】:

    点击是一个事件。在您的代码中,您需要有一个与 XAML 中的任何内容对应的事件处理程序。在这种情况下,您需要具备以下条件:

    private void Command(object sender, RoutedEventArgs e)
    {
    
    }
    

    命令不同。如果你需要连接一个命令,你可以使用按钮的 Commmand 属性,你可以使用一些预先构建的命令或通过 CommandManager 类连接你自己的命令(我认为)。

    【讨论】:

    • 谢谢,这就是我帖子最后一点的意思,我通过后面代码的普通事件处理程序让它工作,但我想要一个更好的解决方案。
    【解决方案5】:

    在 Xamarin Forms 上,最丑最直接的版本:

    Xaml:

    <Button Margin="0,10,0,0" 
                        Text="Access galery"
                        Clicked="OpenGalery"
                        BackgroundColor="{StaticResource Primary}"
                        TextColor="White" />
    

    然后:在.cs

    private async void OpenGalery(object sender, EventArgs e) 
    {
    //do your bidding
    }
    

    【讨论】:

      【解决方案6】:

      对 Rachel 已经给出的解决方案的更多解释:

      “具有模型-视图-视图模型设计模式的 WPF 应用程序”

      乔什·史密斯

      http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多