Message.Attach on context menu item in Silverlight

Oct 12, 2010 at 1:26 PM
Edited Oct 12, 2010 at 1:28 PM
I need to bind the click on a menuitem inside a contextmenu to a method on the viewmodel of the contextmenu's container.
I've gone over all the context menu stuff I could find and come up with a solution I think should work, but it does not:
<my:HyperlinkMenuButton x:Name="CreateNew" Style="{StaticResource CommandWithDropdown}" Content="Create" MenuItemsSource="{Binding Templates}">
 <my:HyperlinkMenuButton.MenuItemTemplate>
  <DataTemplate>
   <TextBlock Text="{Binding Name}"/>
  </DataTemplate>
 </my:HyperlinkMenuButton.MenuItemTemplate>
 <my:HyperlinkMenuButton.MenuItemContainerStyle>
  <Style TargetType="toolkit:MenuItem">
   <Setter Property="cal:Message.Attach" Value="[Event Click] = [Action CreateNew($datacontext)]"/>
  </Style>
 </my:HyperlinkMenuButton.MenuItemContainerStyle>
</my:HyperlinkMenuButton>
The Context menu definition (inside a control template):
<toolkit:ContextMenu
 x:Name="MenuButtonContextMenu"
 ItemsSource="{Binding MenuItemsSource, RelativeSource={RelativeSource TemplatedParent}}"
 Foreground="{TemplateBinding Foreground}"
 FlowDirection="{TemplateBinding FlowDirection}"
 ItemTemplate="{Binding MenuItemTemplate, RelativeSource={RelativeSource TemplatedParent}}"
 ItemContainerStyle="{Binding MenuItemContainerStyle, RelativeSource={RelativeSource TemplatedParent}}"
 cal:Action.TargetWithoutContext="{Binding}"/>


This results in a NullReferenceException in ActioMessage.cs at line 169. It seems as though the context field is not set.

I've adapted this solution from a post in the (full) caliburn forums, where it ran successfully on wpf. Can anyone tell me what step I'm missing, or suggest an alternate way to get this right?

Oct 12, 2010 at 3:19 PM

It's likely that toolkit:MenuItem doesn't receive the the correct Action.Target (which should be the root VM for each item, I guess). Not sure of the cause, though: is a multi-level menu with nested popups? This may break Action.Target inheritance context... I would try to apply cal:Action.TargetWithoutContext to each toolkit:MenuItem, binding to the external Target.

As an alternative solution, you might create an explicit VM for each node with a Create action on it, so that the message doesn't have to climb up to find common handler.

Does it help? Would it be possible for you to create a simple repro if the previous advices can't solve the problem?

Oct 27, 2010 at 1:37 PM
Edited Oct 27, 2010 at 1:40 PM

I finally got around to following up and looking at the caliburn source a bit harder.

I think it's something like this:
In ActionMessage's OnAttached, Caliburn Micro attaches a handler on the Loaded event of the MenuItem to initialize the context. However the Loaded event on the MenuItem had either already fired or didn't fire at all (second seems more likely, since I couldn't even catch it manually). This in turn prevents the initialization logic in ActionMessage to execute, resulting in the error I had above.

What I did as a quick & dirty fix is to add a typecheck for MenuItem to the if-clause in ActionMessage.cs at line 105 and invoke ElementLoaded directly.

Michael