使用 Caliburn Micro 从 DataGridComboBoxColumn 捕获 TextChanged 事件

Capturing TextChanged event from DataGridComboBoxColumn using Caliburn Micro

我在将 ComboBoxTextBox 部分的 TextChanged 事件传递给关联的视图模型时遇到问题。

符合预期:

  1. TextChanged 上调用后面代码中的 OnTextChanged() 方法。
  2. KeyUp 上,调用了视图模型中的 OnKeyUp() 方法。

但是:

  1. TextChanged 上,未调用视图模型中的 OnTextChanged() 方法。

为什么没有调用它,我该如何解决?

<UserControl x:Class="AutoComplete.Views.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:cal="http://www.caliburnproject.org"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             >
    <UserControl.Resources>
        <FrameworkElement x:Key="ProxyElement" DataContext="{Binding}"/>
    </UserControl.Resources>
    <Grid>
        <ContentControl Visibility="Collapsed" Content="{StaticResource ProxyElement}"/>
        <DataGrid ItemsSource="{Binding Rows}" AutoGenerateColumns="False" CanUserAddRows="True" CanUserDeleteRows="True">
            <DataGrid.Columns>

                <DataGridComboBoxColumn Header="Code">
                    <DataGridComboBoxColumn.EditingElementStyle>
                        <Style TargetType="ComboBox">
                            <EventSetter Event="TextBoxBase.TextChanged" Handler="OnTextChanged"/>
                            <Setter Property="cal:Action.TargetWithoutContext" Value="{Binding DataContext, Source={StaticResource ProxyElement}}"/>
                            <Setter Property="cal:Message.Attach" Value="[Event TextBoxBase.TextChanged] = [Action OnTextChanged($source, $dataContext)]; [Event KeyUp] = [Action OnKeyUp()]"/>
                            <Setter Property="IsEditable" Value="True"/>
                            <Setter Property="ItemsSource" Value="{Binding DataContext.Suggestions, Source={StaticResource ProxyElement}}"/>
                        </Style>
                    </DataGridComboBoxColumn.EditingElementStyle>
                </DataGridComboBoxColumn>

            </DataGrid.Columns>
        </DataGrid>

    </Grid>
</UserControl>

这不起作用,因为 Caliburn Micro 默认使用 EventTrigger class 作为 Message.Attach。根据 this post EventTrigger 使用反射使用 EventName 属性 查找事件失败,因为 ComboBox 没有公开名为 TextBoxBase.TextChanged 的事件.它也没有公开 TextChanged 事件,它是 TextChanged 从应该被捕获的 ComboBoxTextBox 组件冒出来的。

以上post也提供了一种适应性强的解决方案。首先创建一个新的 RoutedEventTrigger class:

public class RoutedEventTrigger : EventTriggerBase<DependencyObject>
{
    public RoutedEvent RoutedEvent { get; set; }

    protected override void OnAttached()
    {
        var element = (AssociatedObject as Behavior as IAttachedObject)?.AssociatedObject as UIElement
                  ?? AssociatedObject as UIElement;

        element?.AddHandler(RoutedEvent, new RoutedEventHandler(OnRoutedEvent));
    }

    void OnRoutedEvent(object sender, RoutedEventArgs args)
    {
        OnEvent(args);
    }

    protected override string GetEventName()
    {
        return RoutedEvent.Name;
    }
}

然后将 Caliburn Micro 配置为在可能的情况下使用 RoutedEventTrigger 而不是 EventTrigger:

public class Bootstrapper : BootstrapperBase
{
    public Bootstrapper()
    {
        Initialize();
    }

    private RoutedEventTrigger CreateRoutedEventTrigger(DependencyObject target, string routedEvent)
    {
        var routedEvents = EventManager.GetRoutedEvents().ToDictionary(r => $"{r.OwnerType.Name}.{r.Name}");
        if (routedEvents.ContainsKey(routedEvent))
        {
            var trigger = new RoutedEventTrigger
            {
                RoutedEvent = routedEvents[routedEvent]
            };
            trigger.Attach(target);
            return trigger;
        }
        return null;
    }

    protected override void OnStartup(object sender, StartupEventArgs args)
    {
        var baseCreateTrigger = Parser.CreateTrigger;
        Parser.CreateTrigger = (target, triggerText) =>
        {
            var baseTrigger = baseCreateTrigger(target, triggerText);
            var baseEventTrigger = baseTrigger as EventTrigger;
            return CreateRoutedEventTrigger(target, baseEventTrigger?.EventName ?? "") ?? baseTrigger;
        };
       ...
    }
    ...
}

此设置后可以使用冒泡 RoutedEvents。