using attached events with caliburn micro Message.

2019-01-14 08:16发布

问题:

I'm trying to use caliburn micro message to trigger an attached event that I created:

public static class DataChanging
{

    public delegate void DataChangingEventHandler(object sender, DataChangingEventArgs e);
    public static readonly RoutedEvent ChangingEvent =
        EventManager.RegisterRoutedEvent("Changing",
                                         RoutingStrategy.Bubble,
                                         typeof(DataChangingEventHandler),
                                         typeof(DataChanging));

    public static void AddChangingHandler(DependencyObject o, DataChangingEventHandler handler)
    {
        ((UIElement)o).AddHandler(DataChanging.ChangingEvent, handler);
    }
    public static void RemoveChangingHandler(DependencyObject o, DataChangingEventHandler handler)
    {
        ((UIElement)o).RemoveHandler(DataChanging.ChangingEvent, handler);
    }

    public static bool GetActivationMode(DependencyObject obj)
    {
        return (bool)obj.GetValue(ActivationModeProperty);
    }
    public static void SetActivationMode(DependencyObject obj, bool value)
    {
        obj.SetValue(ActivationModeProperty, value);
    }
    public static readonly DependencyProperty ActivationModeProperty =
        DependencyProperty.RegisterAttached("ActivationMode",
                                            typeof(bool),
                                            typeof(DataChanging),
                                            new FrameworkPropertyMetadata(false,
                                                                          HandleActivationModeChanged));

    private static void HandleActivationModeChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        var dataGrid = target as XamDataGrid;
        if (dataGrid == null) // if trying to attach to something else than a datagrid, just ignore
            return;
        if ((bool)e.NewValue)
        {
            dataGrid.RecordDeactivating += selector_RecordDeactivating;
        }
        else
        {
            dataGrid.RecordDeactivating -= selector_RecordDeactivating;
        }
    }

    static void selector_RecordDeactivating(object sender, RecordDeactivatingEventArgs e)
    {

        var args = new DataChangingEventArgs(DataChanging.ChangingEvent,sender)
                       {
                           Data = ((DataRecord) e.Record).DataItem, 
                           ShouldCancelChange = false
                       };
        (sender as UIElement).RaiseEvent(args);
        e.Cancel = args.ShouldCancelChange;
    }
}

In the XAML itself I added the following line:

cal:Message.Attach="[Helpers:DataChanging.Changing] = [Action SelectedDataChanged($eventArgs)]"

Helpers refer to the right namespace. I also tried other versions that failed (full namespace):

cal:Message.Attach="[clr-namespace:RTF.Client.UI.Helpers.DataChanging.Changing] = [Action SelectedDataChanged($eventArgs)]"

tried to set the interaction event by myself:

When I tried adding a normal event trigger everything worked well, so it's not my attached event declaration :

 <EventTrigger RoutedEvent="Helpers:DataChanging.Changing">
                    <EventTrigger.Actions>
                        <BeginStoryboard x:Name="sb">
                            <Storyboard x:Name="dsf">
                                <Storyboard x:Name="myStoryboard">
                                    <BooleanAnimationUsingKeyFrames Storyboard.TargetName="SSS" Storyboard.TargetProperty="IsChecked">
                                        <DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="False" />
                                    </BooleanAnimationUsingKeyFrames>
                                </Storyboard>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>

What am I doing wrong here? There is no way to attach an attached event and invoke it using caliburn micro?

回答1:

finally I understand the problem and the solution. The problem is that system.windows.interactiviy.EventTrigger doesn't support attached events. Caliburn micro uses it for actions thus my attached event didn't work. The solution was to write a customized event trigger and use caliburn micro action in a explicit manner. the customized event trigger was taken from that post:http://joyfulwpf.blogspot.com/2009/05/mvvm-invoking-command-on-attached-event.html?showComment=1323674885597#c8041424175408473805

 public class RoutedEventTrigger : EventTriggerBase<DependencyObject>
{
    RoutedEvent _routedEvent; 
    public RoutedEvent RoutedEvent
    {
        get { return _routedEvent; } 
        set { _routedEvent = value; }
    } 

    public RoutedEventTrigger() { } 
    protected override void OnAttached()
    {
        Behavior behavior = base.AssociatedObject as Behavior; 
        FrameworkElement associatedElement = base.AssociatedObject as FrameworkElement; 
        if (behavior != null)
        {
            associatedElement = ((IAttachedObject)behavior).AssociatedObject as FrameworkElement;
        } 
        if (associatedElement == null)
        {
            throw new ArgumentException("Routed Event trigger can only be associated to framework elements");
        } 
        if (RoutedEvent != null) 
        { associatedElement.AddHandler(RoutedEvent, new RoutedEventHandler(this.OnRoutedEvent)); }
    } 
    void OnRoutedEvent(object sender, RoutedEventArgs args)
    {
        base.OnEvent(args);
    } 
    protected override string GetEventName() { return RoutedEvent.Name; }
}

and then when you want to use caliburn action:

<i:Interaction.Triggers>
                <!--in the routed event property you need to put the full name space and event name-->
                <Helpers:RoutedEventTrigger RoutedEvent="Helpers:DataChanging.Changing">
                    <cal:ActionMessage MethodName="SelectedDataChanged">
                        <cal:Parameter Value="$eventargs" />
                    </cal:ActionMessage>
                </Helpers:RoutedEventTrigger>
 </i:Interaction.Triggers>


回答2:

I dont think that the parser for the shortened Message.Attach syntax supports attached events. But why don't you just add the ActionMessage directly to the Actions of the EventTrigger?

<EventTrigger RoutedEvent="Helpers:DataChanging.Changing">
    <EventTrigger.Actions>
        <!-- new part -->
        <cal:ActionMessage MethodName="SelectedDataChanged">  
            <cal:Parameter Value="$eventargs" />  
        </cal:ActionMessage>  
        <!-- /new part -->
        <BeginStoryboard x:Name="sb">
            <Storyboard x:Name="dsf">
                <Storyboard x:Name="myStoryboard">
                    <BooleanAnimationUsingKeyFrames Storyboard.TargetName="SSS" Storyboard.TargetProperty="IsChecked">
                        <DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="False" />
                    </BooleanAnimationUsingKeyFrames>
                </Storyboard>
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger.Actions>
</EventTrigger>


回答3:

Have you tried this?

cal:Message.Attach="[Event Changing] = [Action SelectedDataChanged($eventArgs)]"

I needed to send an event from a child control to the parents ViewModel, and it worked fine for me. I'll post some example code maybe it'll help someone out!

Child Control codebehind:

public partial class MyControl : UserControl
{
    public MyControl()
    {
        InitializeComponent();
    }

    #region Routed Events

    public static readonly RoutedEvent ControlClosedEvent = EventManager.RegisterRoutedEvent(
        "ControlClosed",
        RoutingStrategy.Bubble,
        typeof(RoutedEventHandler),
        typeof(MyControl));

    public event RoutedEventHandler ControlClosed
    {
        add { AddHandler(ControlClosedEvent, value); }
        remove { RemoveHandler(ControlClosedEvent, value); }
    }

    #endregion Routed Events

    private void Close(object sender, RoutedEventArgs e)
    {
        var rea = new RoutedEventArgs(ControlClosedEvent);
        RaiseEvent(rea);
    }
}

Child control XAML:

<Button Grid.Row="1" Grid.Column="0"
        Content="Close Me!" Height="50"
        Click="Close" />

Parent view:

<userControls:MyControl cal:Message.Attach="[Event ControlClosed] = [Action ClosePopup]" />