Target not getting set on a custom control with convention

Feb 2, 2011 at 3:06 PM

Hello,

I am trying to use Caliburn.Micro with the Timeline control from the Timeline.codeplex.com. It displays a timeline of events. I want to trigger a viewmodel method call when the user clicks on the event.

I first created an event called "OnMouseLeftButtonDownOnEvent" defined as 

public event Action<TimelineEvent>        OnMouseLeftButtonDownOnEvent;

I invoke it in this fashion

if (OnMouseLeftButtonDownOnEvent != null)
                OnMouseLeftButtonDownOnEvent(timelineEvent);

I then added this convention in the structuremap bootstrapper

ConventionManager.AddElementConvention<TimelineLibrary.TimelineBand>(
                TimelineLibrary.TimelineBand.DataContextProperty, "DataContext""OnMouseLeftButtonDownOnEvent");

I then added the Message.Attach on the element.

                        <timeline:TimelineBand
                            x:Name="timelineMainBand"
                            Grid.Row="1"
                            IsMainBand="True"
                            ItemSourceType="minutes10"
                            HorizontalAlignment="Stretch"     
                            TimelineWindowSize="8"
 
		           cal:Message.Attach="[Event OnMouseLeftButtonDownOnEvent]=Action[EventDoubleClicked('$eventArgs')]"
                            MaxEventHeight="50">
                        </timeline:TimelineBand>

 

Since this did not work I tried the following

                        <timeline:TimelineBand
                            x:Name="timelineMainBand"
                            Grid.Row="1"
                            IsMainBand="True"
                            ItemSourceType="minutes10"
                            HorizontalAlignment="Stretch"     
                            TimelineWindowSize="8"
                            MaxEventHeight="50">
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="OnMouseLeftButtonDownOnEvent">
                                    <cal:ActionMessage MethodName="EventDoubleClicked">
                                        <cal:Parameter Value="{Binding $eventArgs}" />
                                    </cal:ActionMessage>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                        </timeline:TimelineBand>

My viewmodel method looks like

public void EventDoubleClicked(TimelineLibrary.TimelineEvent eventClickedOn) { }

Are there any obvious mistake I have commited?

Coordinator
Feb 2, 2011 at 3:36 PM

The mistake I see is in putting the special parameter value in single quotes. Your action should look like this:

                            cal:Message.Attach="[Event OnMouseLeftButtonDownOnEvent] = Action[EventDoubleClicked($eventArgs)]"

The expanded version would look like this:

                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="OnMouseLeftButtonDownOnEvent">
                                    <cal:ActionMessage MethodName="EventDoubleClicked">
                                        <cal:Parameter Value="$eventArgs" />
                                    </cal:ActionMessage>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>

 

One of those should do it, Please let me know if that fixes your problem.

Feb 2, 2011 at 4:11 PM

Rob,

I have been using single quotes around '$datacontext' and $eventArgs' and it seems to have worked in earlier cases.

I removed the single quotes and it had no effect. I changed the markup to what you suggested.

                        <timeline:TimelineBand
                            x:Name="timelineMainBand"
                            Grid.Row="1"
                            IsMainBand="True"
                            ItemSourceType="minutes10"
                            HorizontalAlignment="Stretch"     
                            TimelineWindowSize="8"
                            OnMouseLeftButtonDownOnEvent="timelineMainBand_OnMouseLeftButtonDownOnEvent"
                            MaxEventHeight="50">
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="OnMouseLeftButtonDownOnEvent">
                                    <cal:ActionMessage MethodName="EventDoubleClicked">
                                        <cal:Parameter Value="$eventArgs" />
                                    </cal:ActionMessage>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                        </timeline:TimelineBand>

Since there was no effect, I created an event handler in the code behind. That event handler does get invoked. I also tried to expose another event via the code behind in that eventhandler.

        public event EventHandler<EventClickedArgs> EventClicked;
        private void timelineMainBand_OnMouseLeftButtonDownOnEvent(TimelineLibrary.TimelineEvent obj)
        {
            if (EventClicked != null)
                EventClicked(thisnew EventClickedArgs(obj));
        }

In the markup I declared it like below

<Window ..

 cal:Message.Attach="[Event EncounterMoved] = Action[PatientMoved('$eventArgs')];
                       [Event EventClicked] = Action[EventDoubleClicked($eventArgs)]">

What is funny is that "EncounterMoved" event does get fired alright but in case of the EventClicked, in the eventhandler shown above, it is null.

Not sure why the event is 'null'?

Feb 2, 2011 at 5:05 PM

I was debugging into the Micro code,

In ActionMessage.SetMethodBinding I do see the context.Method being set to the method in the view model, target is the viewmodel and view is the window.

I can also see that in the ElementLoaded handler in ActionMessage, the binding is being set for the method from the viewmodel.

 BindingOperations.SetBinding(this, HandlerProperty, binding);

Not sure if the above helps.

 

Coordinator
Feb 2, 2011 at 5:05 PM

Is it possible for you to email me a simple reproduction of the issue so that I can take a look at it? Please send it to robertheisenberg at hotmail dot com

Coordinator
Feb 2, 2011 at 7:11 PM

I had a look at your code and I think that this is a bug in Microsoft's implementation of EventTrigger. I suspect that it can only handle events of type EventHandler or EventHandler<T>. I didn't have the source for your timeline control so I couldn't change it to be sure, but I did create an inherited control using the traditional event pattern (wired to your original event) and everything seamed to work fine. I tried to email you the code, but gmail doesn't like me today. I think that if you can modify your control to use EventHandler<T> with a custom EventArgs that would be the recommended course of action.

Feb 2, 2011 at 8:13 PM

Rob,

Thanks for the investigative work. Once I changed the event from Action<T> to Eventhandler<T> it worked.

How did you figure out the issue as in what part (class) of the Micro was not reacting as expected?

Thanks again.

Coordinator
Feb 3, 2011 at 12:39 AM

The invoke method of ActionMessage was never being called. That led me to believe that there was a problem with the Trigger itself. So I did the little experiment I mentioned above to confirm my suspicions after I had a look at how your events were defined.