【问题标题】:Raising an event on a control in XAML在 XAML 中的控件上引发事件
【发布时间】:2017-09-14 06:29:17
【问题描述】:

我正在尝试为 WPF 中的钢琴键盘编写 XAML 控件,该控件响应来自外部 MIDI 键盘的 NoteOn 和 NoteOff MIDI 事件。我正在使用 Tom Lokovic 的 midi-dot-net 来引发由硬件触发的 NoteOn 和 NoteOff 事件,但我需要一种方法来让这些事件引发我的 XAML Key 类(派生自 WPF 按钮)的 NoteOn 和 NoteOff 事件。键的颜色应该在它打开时改变,并且事件应该是可订阅的,以便钢琴键盘控件的用户可以播放按键被按下的声音。

我可以通过将每个 Midi.InputDevice 传递给键盘上的每个键来做到这一点,这样每个人都可以订阅每个 InputDevice 的 NoteOn 和 NoteOff 事件,然后依次引发他们自己的 NoteOn 和 NoteOff 事件,但问题在于这是因为 PianoKeyboard 控件(一个 ItemsControl 包含 Keys)及其嵌套的 Key 控件都与 midi-dot-net 的实现紧密耦合。如果我必须这样做,我会这样做,但似乎在 WPF 中应该有更好的方法来执行此操作,将 midi-dot-net 的依赖移到调用堆栈中更高的位置。

我有太多代码无法在此处完整粘贴,但仍然可以阅读,因此这里是我用作 PianoKeyboard 的 ItemTemplate 的其中一个 DataTemplate 的示例。

<DataTemplate x:Key="naturalKeyTemplate"> <!--NOTE: Background and Foreground Color assignment and changing is accounted for in the style.--> <local:Key Grid.Column="{Binding Converter={StaticResource keyToColumnNumberConverter}}" Grid.ColumnSpan="{Binding Converter={StaticResource keyToColumnSpanConverter}}" Grid.RowSpan="2" Style="{StaticResource naturalKeyStyle}" Note="{Binding Note}" IsHighlighted="{Binding IsHighlighted}"> <!--TODO: Find a way of raising the Key's NoteOn and NoteOff events from here.--> </local:Key> </DataTemplate>

基本上我要问的是:给定输入设备不支持通过按钮的内置行为(例如鼠标单击)触发 WPF 按钮,如何让它触发按钮而不将其耦合到按钮的派生类?

【问题讨论】:

    标签: c# wpf xaml events command


    【解决方案1】:

    您可以通过编程方式引发Click 事件:

    button1.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent));
    

    【讨论】:

    • 我试图避免将逻辑放在代码隐藏中,因此我不必将 PianoKeyboard 控件的 DataContext 设置为自身(尽管目前它是出于其他原因,我也试图消除)。如果我依赖于绑定到后面的代码,那么只需控件的用户将其数据上下文设置为其他内容,如果他们不知道他们的 DataContext 上应该有什么来支持它,它就会中断。
    • 这段代码不在视图的代码隐藏中,而是在自定义控件类中。
    • PianoKeyboard 控件类?
    • 我猜。每当您获得 MIDI 库事件时,您都会以编程方式引发 WPF 事件。
    • 嗯,如果它在 PianoKeyboard 类中,那么它必须查看 PianoKeyboard 的项目以找到引发 NoteOn 事件的键(如果有的话)。我可以看到它是如何工作的,但这是在 WPF 中执行此操作的正确 (MVVM) 方式吗?
    【解决方案2】:

    我认为这可以帮助您根据调用的事件调用特定的方法。

    <local:Key>  
             <i:Interaction.Triggers>
                <i:EventTrigger EventName="Mouse.PreviewMouseDown">
                    <ei:CallMethodAction MethodName="NoteOn"
                                         TargetObject="{Binding}" />
                </i:EventTrigger>
               <i:EventTrigger EventName="Mouse.PreviewMouseUp">
                    <ei:CallMethodAction MethodName="NoteOff"
                                         TargetObject="{Binding}" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
         </local:Key>
    

    您需要添加 xmlns 扩展:

     xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
     xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
    

    【讨论】:

    • 我认为这可能是我所追求的;那么我认为 NoteOn 将是我的 Binding 对象上的一个方法并且在调用时会在我的 Key 控件上触发 Mouse.PreviewMouseDown 是否正确?
    • 其实它的对面,Mouse.PreviewMouseDown 会调用方法 NoteOn。
    • 哦,等等,正好相反!我需要我正在使用的 MIDI 库的 NoteOn 和 NoteOff 事件触发 Key 上的 NoteOn 和 NoteOff 事件(或 MouseUp 和 MouseDown,如果可能的话)。
    • 那么也许正如@mm8 所说,以编程方式在代码后面引发点击。
    【解决方案3】:

    我会做一个 MIDI 侦听器服务单例来侦听所有 MIDI 事件,它会更新键盘状态存储库,它会公开绑定到您的键盘控件的视图模型...但我并没有真正看到您的程序结构,所以它是不好说。

    将 xaml 中的 midi 事件绑定到包装器 midi.net 似乎不是 LOB 方式,因为您将 gui 与 midi.net 链接...

    对了,开/关还不够,你需要“按下”作为按键状态??

    【讨论】:

    • "On" 将是按下状态,但我注意到 ButtonBase 上的 IsPressed 属性是只读的,所以很遗憾我不能用它来触发按钮。
    • 关于 MIDI NoteOn 和 NoteOff 事件,我的问题是微软如何处理来自硬件的鼠标事件?他们会使用单例吗?我认为单例是不好的做法,因为它们引入了全局状态。
    • 如果你的键盘是 mvvm 绑定的,那么 isPressed 就可以工作了……我没听明白吗?你想触发一个事件还是从 midi.net 获取一个?
    • 我正在使用来自 MIDI.NET 的事件在我的 Key 类上触发另一个同名事件。 Key 类的哪个实例需要其 NoteOn 或 NoteOff 事件触发取决于按下的音符。我认为 mm8 的解决方案可以解决问题,但我很想知道是否有办法在 XAML 中做到这一点。
    • 你是 MVVM 吗?为什么要从键控触发业务事件?你打算用它做什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多