WPF:在UserControl中使用绑定命令

3

我正在使用MVVM进行示例,并遇到了命令问题。我有一个Article类(带有ID,Name,Price等属性),一个表示视图模型的ArticleViewModel,以及一个用户控件(ArticleControl),允许输入文章数据,并将其绑定到ArticleViewModel的属性。该用户控件具有保存命令的绑定。

   <UserControl.CommandBindings>
      <CommandBinding x:Name="saveCmd" 
                      Command="local:Commands.Save" 
                      CanExecute="CommandBinding_CanExecute"
                      Executed="CommandBinding_Executed"/>
   </UserControl.CommandBindings>

这是命令的定义方式:

   public class Commands
   {
      private static RoutedUICommand _save;
      public static RoutedUICommand Save
      {
         get { return _save; }
      }

      static Commands()
      {
         InputGestureCollection saveInputs = new InputGestureCollection();
         saveInputs.Add(new KeyGesture(Key.S, ModifierKeys.Control, "Ctrl+S"));

         _save = new RoutedUICommand(
            "Save",
            "Save",
            typeof(Commands),
            saveInputs);
      }
   }

还有命令绑定处理程序:

  private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
  {
     double baseprice = 0;
     double.TryParse(ArticleBasePrice.Text, out baseprice);

     e.CanExecute =
        !string.IsNullOrEmpty(ArticleID.Text) &&
        !string.IsNullOrEmpty(ArticleName.Text) &&
        !string.IsNullOrEmpty(ArticleDescription.Text) &&
        baseprice > 0;
  }

  private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
  {
     ArticleViewModel avm = (ArticleViewModel)DataContext;
     if (avm != null && avm.Save())
     {
        ArticleID.Text = String.Empty;
        ArticleName.Text = String.Empty;
        ArticleDescription.Text = String.Empty;
        ArticleBasePrice.Text = String.Empty;
     }
  }

现在,我将此用户控件放在一个窗口上。当我按下 Ctrl+S 时,命令将被执行。但是,我还在该窗口中的此用户控件旁边放了一个保存按钮。当我单击它时,我希望执行相同的命令(并且我不想在托管用户控件的窗口中执行其他命令绑定)。
   <StackPanel>
      <local:ArticleControl x:Name="articleControl" />
      <Button Name="btnSave" 
              Content="Save" Width="100" 
              HorizontalAlignment="Left" 
              Command="{???}"/> <!-- what should I put here? -->
   </StackPanel>

但我不知道如何引用用户控件中定义的saveCmd。我尝试了不同的方法,有些完全错误(在运行应用程序时会抛出异常),有些则没有任何效果。

Command="{StaticResource saveCmd}"
Command="{StaticResource local:ArticleControl.saveCmd}"
Command="{x:Static local:Commands.Save}"

非常感谢您提供的任何帮助。谢谢。

4个回答

4
Save按钮不会导致其他控件的CommandBindings执行的原因是因为Save按钮在用户控件之外,因此命令系统不会在该控件中查找CommandBinding。命令执行策略有点像冒泡事件,它将从焦点项(按钮)开始,并沿着可视树向上寻找CommandBindings。
您可以在父控件中实现命令绑定,或将Save按钮的CommandTarget属性设置为用户控件。
另一种方法是在按钮或按钮容器上设置FocusManager.IsFocusScope=True。如果您这样做,我建议您了解一下IsFocusScope的作用,但简而言之,它将保持输入焦点在按下按钮时拥有焦点的任何控件上,而不是使按钮成为新的输入焦点。这通常用于工具栏或类似菜单的结构中。

你能给我一个例子吗?我尝试了一些设置,例如将 Command="local:Commands.Save"CommandTarget="{Binding ElementName=ArticleUserControl} 应用于按钮,但它并不起作用。按钮一直处于禁用状态。 - Marius Bancila
你也可以在按钮上设置FocusManager.IsFocusScope=True。 - Patrick Klug

2

根据Patrick的建议,这是我所做的:

  1. 将命令绑定放在用户控件中,并在代码后端实现处理程序,如原始消息所示。

  2. 在按钮上使用CommandCommandTargetFocusManager属性,指向来自用户控件的绑定(ArticleUserControl是用户控件的x:Name)。

这是窗口XAML的方式:

<Window x:Class="MVVMModel.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MVVMModel"
        Title="MainWindow" Height="350" Width="525">
   <StackPanel>
      <local:ArticleControl x:Name="articleControl" />
      <Button Name="btnSave" Content="Save" Width="100" HorizontalAlignment="Left" 
              Command="local:Commands.Save"
              CommandTarget="{Binding ElementName=ArticleUserControl}"
              FocusManager.IsFocusScope="True" />
   </StackPanel>
</Window>

1
我觉得你只需要将你的CommandBinding移到一个资源字典中,这样它就可以在你的UserControl之外使用了!

尝试过了,但没有成功,正如我在下面的回答中所解释的那样(无法在评论中发布代码,评论也有大小限制)。 - Marius Bancila

0

这是我为了让它工作所做的事情,虽然我对解决方案并不特别满意。如果有人知道更好的方法,请告诉我。

我将命令处理程序的逻辑移动到一个单独的静态类中:

   static class CommandsCore
   {
      public static bool Save_CanExecute(ArticleControl ac)
      {
         double baseprice = 0;
         double.TryParse(ac.ArticleBasePrice.Text, out baseprice);

         return
            !string.IsNullOrEmpty(ac.ArticleID.Text) &&
            !string.IsNullOrEmpty(ac.ArticleName.Text) &&
            !string.IsNullOrEmpty(ac.ArticleDescription.Text) &&
            baseprice > 0;
      }

      public static void Save_Executed(ArticleControl ac)
      {
         ArticleViewModel avm = (ArticleViewModel)ac.DataContext;
         if (avm != null && avm.Save())
         {
            ac.ArticleID.Text = String.Empty;
            ac.ArticleName.Text = String.Empty;
            ac.ArticleDescription.Text = String.Empty;
            ac.ArticleBasePrice.Text = String.Empty;
         }
      }
   }

我将绑定命令的部分保留在用户控件中,就像原来一样。

   <UserControl.CommandBindings>
      <CommandBinding x:Name="saveCmd" 
                      Command="local:Commands.Save" 
                      CanExecute="CommandBinding_CanExecute"
                      Executed="CommandBinding_Executed"/>
   </UserControl.CommandBindings>

但是在处理程序中,我调用了上面定义的两个方法。

  public void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
  {
     e.CanExecute = CommandsCore.Save_CanExecute(this);
  }

  public void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
  {
     CommandsCore.Save_Executed(this);
  }

然后我从使用控件的窗口做了同样的事情。

<Window x:Class="MVVMModel.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MVVMModel"
        Title="MainWindow" Height="350" Width="525">
   <Window.CommandBindings>
      <CommandBinding x:Name="saveCmd" 
                      Command="local:Commands.Save" 
                      CanExecute="CommandBinding_CanExecute"
                      Executed="CommandBinding_Executed"/>
   </Window.CommandBindings>

   <StackPanel>
      <local:ArticleControl x:Name="articleControl" />
      <Button Name="btnSave" Content="Save" Width="100" HorizontalAlignment="Left" 
              Command="local:Commands.Save"/>
   </StackPanel>
</Window>

以及处理程序

  public void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
  {
     e.CanExecute = CommandsCore.Save_CanExecute(articleControl);
  }

  public void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
  {
     CommandsCore.Save_Executed(articleControl);
  }

这个是有效的,只有在字段填写正确并且单击按钮时命令被正确执行时,保存按钮才会启用。


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