【问题标题】:Caliburn Micro 'Enter' Key EventCaliburn Micro 'Enter' 关键事件
【发布时间】:2013-05-19 03:23:57
【问题描述】:

我正在尝试将事件与 Caliburn Micro 绑定,但在将正确消息传递给该方法时遇到了一些问题。我想添加在更改文本框中的值后按“Enter”键的功能,它执行与旁边的按钮绑定的相同方法。但是,无论按下哪个键,都会出现以下异常:

“System.InvalidCastException”类型的第一次机会异常 发生在 MyApp.exe 中

类型的第一次机会异常 mscorlib.dll 中发生“System.Reflection.TargetInvocationException”

类型的第一次机会异常 'System.Reflection.TargetInvocationException' 发生在 WindowsBase.dll

在另一个类似问题Binding KeyDown Event Silverlight 的建议下,我尝试使用 ActionExecutionContext,但无济于事。

这里是 xaml:

<TextBox Name="Threshold"                     
              Margin="5"
              Grid.Column="1"
              >
     <i:Interaction.Triggers>
         <i:EventTrigger EventName="KeyDown">
             <cal:ActionMessage MethodName="ExecuteFilterView">
                 <cal:Parameter Value="$executionContext"/>
             </cal:ActionMessage>
         </i:EventTrigger>
     </i:Interaction.Triggers>
</TextBox>

以及方法:

 public void ExecuteFilterView(ActionExecutionContext context)
    {
        //Do stuff...
    }

我知道我可能会省去一些麻烦,只需在后面的代码中做一个标准的事件处理程序,但这个应用程序是 MVVM 的一个练习,并学习使用 Caliburn.Micro,所以我想坚持做这个特殊的方法工作。

我只是想从活动中发送错误的信息吗?我的 xaml 是否没有正确编码以获得我想要的东西?还是我完全错过了其他东西?

【问题讨论】:

    标签: wpf mvvm caliburn.micro


    【解决方案1】:

    只是一起进行了测试,这两个都对我有用:

    使用完整的语法:

        <TextBox Name="Threshold"                     
              Margin="5"
              Grid.Column="1">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="KeyDown">
                    <cal:ActionMessage MethodName="ExecuteFilterView">
                        <cal:Parameter Value="$executionContext"/>
                    </cal:ActionMessage>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </TextBox>
    

    使用 CM 语法(更喜欢这个,因为它更易读)

        <TextBox Name="Threshold"                     
              Margin="5"
              Grid.Column="1"
              cal:Message.Attach="[Event KeyDown] = [Action ExecuteFilterView($executionContext)]" />            
    

    这是测试虚拟机:

    public class MainWindowViewModel
    {
        public void ExecuteFilterView(ActionExecutionContext context)
        { 
            // This method is hit and the context is present and correct
        }
    }
    

    您能否发布您的完整代码 - 您确定您的框架设置正确吗? (您是否按照入门示例进行操作?

    http://caliburnmicro.codeplex.com/wikipage?title=Basic%20Configuration%2c%20Actions%20and%20Conventions&referringTitle=Documentation

    编辑:

    好的,在你澄清之后,我可以给你一些例子来说明如何做到这一点 - 我会告诉你我的个人喜好和原因,但是选择最适合你的那个

    1. 使用ActionExecutionContext 并强制转换事件参数:
        cal:Message.Attach="[Event KeyDown] = [Action ExecuteFilterView($executionContext)]"
    
        public void ExecuteFilterView(ActionExecutionContext context)
        {
            var keyArgs = context.EventArgs as KeyEventArgs;
    
            if (keyArgs != null && keyArgs.Key == Key.Enter)
            {
                // Do Stuff
            }
        }
    
    1. 直接使用EventArgs
        cal:Message.Attach="[Event KeyDown] = [Action ExecuteFilterView($eventArgs)]"
    
        public void ExecuteFilterView(KeyEventArgs keyArgs)
        {
            if (keyArgs.Key == Key.Enter)
            {
                // Do Stuff
            }
        }
    
    1. 我个人的最爱,创建您自己的SpecialValues 字典条目:

    在您的 Bootstrapper.Configure 方法中...

    MessageBinder.SpecialValues.Add("$pressedkey", (context) =>
    {
        // NOTE: IMPORTANT - you MUST add the dictionary key as lowercase as CM
        // does a ToLower on the param string you add in the action message, in fact ideally
        // all your param messages should be lowercase just in case. I don't really like this
        // behaviour but that's how it is!
        var keyArgs = context.EventArgs as KeyEventArgs;
    
        if (keyArgs != null)
            return keyArgs.Key;
    
        return null;
    });
    

    你的行动:

    cal:Message.Attach="[Event KeyDown] = [Action ExecuteFilterView($pressedKey)]"
    

    还有代码:

    public void ExecuteFilterView(Key key)
    {
        if (key == Key.Enter)
        {
            // Do Stuff
        }
    }
    

    为什么这是我最喜欢的?这意味着您的 VM 只接收您想要的值(大多数时候您不关心很多其他参数)并且您不需要知道如何或费心地转换 eventargs - 您只需操作关于价值。显然使用最适合你的东西

    同样值得注意的是,如果您有其他类型的控件,子类KeyEventArgs 这将适用于它们。如果他们不继承 KeyEventArgs 但他们仍然返回类型为 Key 的值,这仍然可以工作,因为如果第一个失败,您可以向委托添加另一个演员:

    例如

    MessageBinder.SpecialValues.Add("$pressedkey", (context) =>
    {
        var keyArgs = context.EventArgs as KeyEventArgs;
    
        if (keyArgs != null)
            return keyArgs.Key;
    
        // Ok so it wasn't KeyEventArgs... check for some other type - maybe a 3rd party implementation
        var thirdPartyKeyArgs = context.EventArgs as ThirdPartyKeyArgs;
    
        if (thirdPartyKeyArgs != null)
            return thirdPartyKeyArgs.KeyProperty;
    
        return null;
    });
    

    【讨论】:

    • 所以我想我应该澄清一下,我对被击中的方法没有任何问题。问题是,一旦方法触发,我无法对信息做任何事情而不会得到异常。基本上,我试图弄清楚如何使用 ActionExecutionContext 来确定按下的键是否是“Enter”键,如果是,则对其进行处理。
    • 我可能会为此使用MessageBinder.SpecialValues,因为它抽象出实际的事件参数转换并为您提供了一种很好的可重用方式来执行此操作。我会用所有选项更新我的帖子
    • 漂亮!它可重用的事实甚至更好,因为我将为 4 个不同的视图做同样的事情。在实施您的建议时,我发现了另一个我错过的错误。我将 System.Windows.Forms 用于 KeyEventArgs,这不允许我尝试的演员表。当我切换到 System.Windows.Input 时,一切就绪。感谢您深思熟虑的答案。
    • SpecialValues 方法对我有用,但其他方法传入 null 而不是键。
    • 其他方法不会传递密钥,而是传递某些类型的事件参数(例如KeyEventArgs),您需要在 VM 中进行强制转换或检查。这就是使用特殊值来抽象掉间接层的原因
    【解决方案2】:

    如果使用 keyeventargs 作为参数类型,它给你返回 null ,那么看看这个: 您可以适当地使用System.Windows.Forms.KeyEventArgsKeyEventArgs,因为参数类型指的是这个,请尝试使用System.Windows.Input.KeyEventArgs(它适用于我)。

    public void ExecuteFilterView(System.Windows.Input.KeyEventArgs context)
    {
         if(context.Key==System.Windows.Input.Key.Return)
         {
             //action here
         }      
    }
    

    【讨论】:

      【解决方案3】:

      看看Caliburn.Micro samples, Scenario.Keybinding.

      你可以这样做:

      <TextBox Name="Threshold"                     
                Margin="5"
                Grid.Column="1"
                cal:Message.Attach="[Key Enter] = [ExecuteFilterView($this.Text)]"/>
      

      这会将文本框的文本发送到 ExecuteFilterView 方法。

      要使其正常工作,您需要将以下代码 (which I copied from the sample) 添加到您的引导程序。

      protected override void Configure()
      {
          var defaultCreateTrigger = Parser.CreateTrigger;
      
          Parser.CreateTrigger = (target, triggerText) => 
          {
              if (triggerText == null)
              {
                  return defaultCreateTrigger(target, null);
              }
      
              var triggerDetail = triggerText
                  .Replace("[", string.Empty)
                  .Replace("]", string.Empty);
      
              var splits = triggerDetail.Split((char[])null, StringSplitOptions.RemoveEmptyEntries);
      
              switch (splits[0])
              {
                  case "Key":
                      var key = (Key)Enum.Parse(typeof(Key), splits[1], true);
                      return new KeyTrigger { Key = key };
      
                  case "Gesture":
                      var mkg = (MultiKeyGesture)(new MultiKeyGestureConverter()).ConvertFrom(splits[1]);
                      return new KeyTrigger { Modifiers = mkg.KeySequences[0].Modifiers, Key = mkg.KeySequences[0].Keys[0] };
              }
      
              return defaultCreateTrigger(target, triggerText);
          };
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-01-04
        • 1970-01-01
        • 2014-09-02
        • 2018-07-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-22
        相关资源
        最近更新 更多