20

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

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

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

WindowsBase.dll 中出现了“System.Reflection.TargetInvocationException”类型的第一次机会异常

在另一个类似问题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 是否没有正确编码以获得我想要的东西?还是我完全错过了其他东西?

4

3 回答 3

55

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

使用完整的语法:

    <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和转换 eventargs:
    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
    }
}

这是我最喜欢的原因?这意味着您的虚拟机只接收您想要的值(大多数时候您不关心很多其他参数)并且您不需要知道如何或费心地转换 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;
});
于 2013-05-24T09:35:04.513 回答
3

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

public void ExecuteFilterView(System.Windows.Input.KeyEventArgs context)
{
     if(context.Key==System.Windows.Input.Key.Return)
     {
         //action here
     }      
}
于 2015-05-05T10:51:16.080 回答
0

看看Caliburn.Micro 示例,Scenario.Keybinding。

你可以这样做:

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

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

为了使它工作,您需要将下面的代码(我从示例中复制)添加到您的引导程序。

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);
    };
}
于 2021-11-03T12:23:42.070 回答