Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Wpf 如何创建自定义控件的事件以支持转换为命令?_Wpf_Event Handling_Command_Custom Controls - Fatal编程技术网

Wpf 如何创建自定义控件的事件以支持转换为命令?

Wpf 如何创建自定义控件的事件以支持转换为命令?,wpf,event-handling,command,custom-controls,Wpf,Event Handling,Command,Custom Controls,我创建的自定义控件有一个事件,如下所示 public class Editor : Control { ... public event EventHandler<AlarmCollection> AlarmFired = null; ... } <DataTemplate DataType="{x:Type documentViewModels:EditorTypeViewModel}"> <editor:Editor FontS

我创建的自定义控件有一个事件,如下所示

public class Editor : Control
{
    ...
    public event EventHandler<AlarmCollection> AlarmFired = null;
    ...
}
<DataTemplate DataType="{x:Type documentViewModels:EditorTypeViewModel}">
    <editor:Editor FontSize="15" FontFamily="Arial"
                                 KeywordForeground="LightBlue">

        <i:Interaction.Triggers>
            <i:EventTrigger EventName="AlarmFired">
                <mvvm:EventToCommand Command="{Binding AlarmFiredCommand}"
                                     PassEventArgsToCommand="True"
                                     EventArgsConverter="{localConverters:RemoveObjectConverter}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>

    </editor:Editor>

</DataTemplate>
现在,我尝试将上述事件绑定到ViewModel外部,如下所示

public class Editor : Control
{
    ...
    public event EventHandler<AlarmCollection> AlarmFired = null;
    ...
}
<DataTemplate DataType="{x:Type documentViewModels:EditorTypeViewModel}">
    <editor:Editor FontSize="15" FontFamily="Arial"
                                 KeywordForeground="LightBlue">

        <i:Interaction.Triggers>
            <i:EventTrigger EventName="AlarmFired">
                <mvvm:EventToCommand Command="{Binding AlarmFiredCommand}"
                                     PassEventArgsToCommand="True"
                                     EventArgsConverter="{localConverters:RemoveObjectConverter}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>

    </editor:Editor>

</DataTemplate>
EditorTypeViewModel的定义如下所示

public class EditorTypeViewModel : DocumentViewModel
{
    public event EventHandler<AlarmCollection> AlarmFired = null;

    public EditorTypeViewModel(string title) : base(title)
    {
    }

    private RelayCommand<AlarmCollection> alarmFiredCommand = null;
    public RelayCommand<AlarmCollection> AlarmFiredCommand
    {
        get
        {
            if (this.alarmFiredCommand == null)
                this.alarmFiredCommand = new RelayCommand<AlarmCollection>(this.OnAlarmFired);

            return this.alarmFiredCommand;
        }
    }

    private void OnAlarmFired(AlarmCollection alarmInfos)
    {


        this.AlarmFired?.Invoke(this, alarmInfos);
    }
}
当我执行上述程序时,没有调用与RelayCommand连接的OnAlarmFireed方法。 我试图找出原因,发现了一个可疑点

当调用编辑器的TextChanged方法时,编辑器的AlarmFireed事件的值为null是一个可疑点。它显示在下面

我认为AlarmFired不是空的,因为它将与命令连接,但它不是空的

我试图做的是将CustomControl的事件绑定到ViewModel的命令并使用它

例如,ListView的MouseDoubleClick事件可以绑定到MouseDoubleClick命令,如下所示。 MouseDoubleClick命令将在触发MouseDoubleClick事件时获得控制权

private async void TextArea_TextChanged(object sender, TextChangedEventArgs e)
{
    ...
    this.AlarmFired?.Invoke(this, this.alarmList);
    ...
}
<ListView>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseDoubleClick">
            <mvvm:EventToCommand Command="{Binding MouseDoubleClickCommand}"
                                 PassEventArgsToCommand="True"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

</ListView>
我想创建一个事件来支持转换为命令,比如ListView的MouseDoubleClick 我不想在CustomControl中创建命令,因为事件的数量很多

我应该做些什么来实现这个目标


感谢阅读。

如果要将事件参数传递给ViewModel,应创建如下新行为:

public class EventToCommandBehavior : Behavior<FrameworkElement>
{
    private Delegate _handler;
    private EventInfo _oldEvent;

    // Event
    public string Event
    {
        get => (string)GetValue(EventProperty);
        set => SetValue(EventProperty, value);
    }
    public static readonly DependencyProperty EventProperty = DependencyProperty.Register("Event", typeof(string), typeof(EventToCommandBehavior), new PropertyMetadata(null, OnEventChanged));

    // Command
    public ICommand Command
    {
        get => (ICommand)GetValue(CommandProperty);
        set => SetValue(CommandProperty, value);
    }
    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommandBehavior), new PropertyMetadata(null));

    // PassArguments (default: false)
    public bool PassArguments
    {
        get => (bool)GetValue(PassArgumentsProperty);
        set => SetValue(PassArgumentsProperty, value);
    }
    public static readonly DependencyProperty PassArgumentsProperty = DependencyProperty.Register("PassArguments", typeof(bool), typeof(EventToCommandBehavior), new PropertyMetadata(false));


    private static void OnEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var beh = (EventToCommandBehavior)d;

        if (beh.AssociatedObject != null) // is not yet attached at initial load
            beh.AttachHandler((string)e.NewValue);
    }

    protected override void OnAttached()
    {
        AttachHandler(Event); // initial set
    }

    /// <summary>
    /// Attaches the handler to the event
    /// </summary>
    private void AttachHandler(string eventName)
    {
        // detach old event
        if (_oldEvent != null)
            _oldEvent.RemoveEventHandler(AssociatedObject, _handler);

        // attach new event
        if (!string.IsNullOrEmpty(eventName))
        {
            var ei = AssociatedObject.GetType().GetEvent(eventName);
            if (ei != null)
            {
                var mi = GetType().GetMethod("ExecuteCommand", BindingFlags.Instance | BindingFlags.NonPublic);
                if (mi != null) _handler = Delegate.CreateDelegate(ei.EventHandlerType, this, mi);
                ei.AddEventHandler(AssociatedObject, _handler);
                _oldEvent = ei; // store to detach in case the Event property changes
            }
            else
                throw new ArgumentException($"The event '{eventName}' was not found on type '{AssociatedObject.GetType().Name}'");
        }
    }

    /// <summary>
    /// Executes the Command
    /// </summary>
    // ReSharper disable once UnusedParameter.Local
    private void ExecuteCommand(object sender, EventArgs e)
    {
        object parameter = PassArguments ? e : null;
        if (Command == null) return;
        if (Command.CanExecute(parameter))
            Command.Execute(parameter);
    }
}
<ListView>
      <i:Interaction.Behaviors>
            <behaviors:EventToCommandBehavior Command="{Binding AlarmFiredCommand}" Event="AlarmFired" PassArguments="True" />
      </i:Interaction.Behaviors>
</ListView>
然后像这样使用:

public class EventToCommandBehavior : Behavior<FrameworkElement>
{
    private Delegate _handler;
    private EventInfo _oldEvent;

    // Event
    public string Event
    {
        get => (string)GetValue(EventProperty);
        set => SetValue(EventProperty, value);
    }
    public static readonly DependencyProperty EventProperty = DependencyProperty.Register("Event", typeof(string), typeof(EventToCommandBehavior), new PropertyMetadata(null, OnEventChanged));

    // Command
    public ICommand Command
    {
        get => (ICommand)GetValue(CommandProperty);
        set => SetValue(CommandProperty, value);
    }
    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommandBehavior), new PropertyMetadata(null));

    // PassArguments (default: false)
    public bool PassArguments
    {
        get => (bool)GetValue(PassArgumentsProperty);
        set => SetValue(PassArgumentsProperty, value);
    }
    public static readonly DependencyProperty PassArgumentsProperty = DependencyProperty.Register("PassArguments", typeof(bool), typeof(EventToCommandBehavior), new PropertyMetadata(false));


    private static void OnEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var beh = (EventToCommandBehavior)d;

        if (beh.AssociatedObject != null) // is not yet attached at initial load
            beh.AttachHandler((string)e.NewValue);
    }

    protected override void OnAttached()
    {
        AttachHandler(Event); // initial set
    }

    /// <summary>
    /// Attaches the handler to the event
    /// </summary>
    private void AttachHandler(string eventName)
    {
        // detach old event
        if (_oldEvent != null)
            _oldEvent.RemoveEventHandler(AssociatedObject, _handler);

        // attach new event
        if (!string.IsNullOrEmpty(eventName))
        {
            var ei = AssociatedObject.GetType().GetEvent(eventName);
            if (ei != null)
            {
                var mi = GetType().GetMethod("ExecuteCommand", BindingFlags.Instance | BindingFlags.NonPublic);
                if (mi != null) _handler = Delegate.CreateDelegate(ei.EventHandlerType, this, mi);
                ei.AddEventHandler(AssociatedObject, _handler);
                _oldEvent = ei; // store to detach in case the Event property changes
            }
            else
                throw new ArgumentException($"The event '{eventName}' was not found on type '{AssociatedObject.GetType().Name}'");
        }
    }

    /// <summary>
    /// Executes the Command
    /// </summary>
    // ReSharper disable once UnusedParameter.Local
    private void ExecuteCommand(object sender, EventArgs e)
    {
        object parameter = PassArguments ? e : null;
        if (Command == null) return;
        if (Command.CanExecute(parameter))
            Command.Execute(parameter);
    }
}
<ListView>
      <i:Interaction.Behaviors>
            <behaviors:EventToCommandBehavior Command="{Binding AlarmFiredCommand}" Event="AlarmFired" PassArguments="True" />
      </i:Interaction.Behaviors>
</ListView>

注意:如果要传递参数,则应将PassArguments属性的参数设置为True

这是一个好主意,但我真正想要的是使用类似Visual Studio基本控件的CustomControl,而不创建其他模块。例如:ListView示例有没有一种方法可以实现这个目标?它不容易被支持。这里有一篇文章:哦,谢谢你提供的信息。如果明天早上之前没有更好的答案,我会采纳这篇文章。