【问题标题】:WPF: using Commands bound in a UserControlWPF:使用在用户控件中绑定的命令
【发布时间】:2011-08-18 14:01:27
【问题描述】:

我正在使用 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}"

感谢任何帮助。谢谢。

【问题讨论】:

    标签: wpf binding user-controls


    【解决方案1】:

    Save 按钮不会导致其他控件的命令绑定执行的原因是因为 Save 按钮在用户控件之外,因此命令系统不会在该控件中查找命令绑定。 Command 执行策略有点像冒泡事件,会从焦点项(Button)开始向上走可视化树,直到找到 CommandBindings。

    您可以在父控件中实现命令绑定,也可以将保存按钮的CommandTarget 属性设置为用户控件。

    另一种方法是在按钮或按钮的容器上设置FocusManager.IsFocusScope=True。如果您这样做,我建议您阅读IsFocusScope 所做的事情,但简而言之,当您按下按钮时,它会将输入焦点留在任何具有焦点的控件上,而不是使按钮成为新的输入焦点。这通常用于工具栏或类似菜单的结构。

    【讨论】:

    • 你能举个例子吗?我尝试为按钮设置Command="local:Commands.Save"CommandTarget="{Binding ElementName=ArticleUserControl},但它不起作用。该按钮一直处于禁用状态。
    • 您也可以在按钮上设置 FocusManager.IsFocusScope=True。
    【解决方案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>
    

    【讨论】:

      【解决方案3】:

      我认为您只需将您的 CommandBinding 移动到资源字典中,这样它就可以在您的 UserControl 之外使用!

      【讨论】:

      • 试过但没有用,正如我在下面的答案中解释的那样(无法将代码发布到评论中,这也受到大小限制)。
      【解决方案4】:

      这就是我所做的工作,尽管我对解决方案并不特别满意。如果有人知道更好的方法,请告诉我。

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

         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);
        }
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-02-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多