Action.Target not set for sub MenuItems in DataTemplate problem

Mar 16, 2011 at 1:55 PM

Hi,

I am having a problem trying to call a View Model method from within a data template, more specifically within a nested MenuItem.

Scenario;

I have a listbox with templated items. The Item template needs to contain a menu and that menu needs to have a menu item which takes the user to the item details.

I have the following Xaml. 

Snippet

<DataTemplate.........
<
Menu>   <MenuItem Header="options" x:Name="MnuOptions">      <MenuItem Micro:Message.Attach="Details($dataContext)" Header="Details"/>   </MenuItem>  </Menu>.....
Snippet
<Button Content="Details" Micro:Message.Attach="Details($dataContext)"/>
......</DataTemplate>

When I click the "Details" menu item I get the exception "No target found from the method Details".  However, I added a button with the same Message.Attach value and this calls the view model passing the Item when clicked!?! If a button works and a sub menuItem does not then surely this a bug

 

A simple repro sample can be found on my skydrive;

http://cid-044ad885bcfe52a6.office.live.com/self.aspx/.Public/CM%5E_SubMenuItemInDataTemplate.rar

 

Any assistance would be appreciated.

Regards

David

 

 

 

 

 

Snippet
<Menu>
            <MenuItem Header="options">
                <MenuItem Micro:Message.Attach="Details($dataContext)" Header="Details"/>
            </MenuItem>
        </Menu>
Mar 16, 2011 at 2:09 PM

Is the MenuItem defined inside a ContextMenu? If so, note that the ContextMenu is on a separate visual tree separated from the main one and the Target is not resolved automatically.

If this is your scenario, binding the Action.Target of the MenuItem to the ListBoxItem.DataContext should fix it.

Mar 16, 2011 at 2:31 PM
BladeWise wrote:

Is the MenuItem defined inside a ContextMenu? If so, note that the ContextMenu is on a separate visual tree separated from the main one and the Target is not resolved automatically.

If this is your scenario, binding the Action.Target of the MenuItem to the ListBoxItem.DataContext should fix it.

Hi BladeWise,

The MenuItem is defined within a menu, not a context menu. Also Setting Action.Target to the ListBoxItem.DataContext would not be valid in this case as the Method im calling lives in the VM, and the VM is the DataContext of the ListBox.  Setting the Action.Target to ListBox.DataContext works in the sense that the method is called, however the $dataContext parameter passed to the method is now the VM rather than the Item in the listbox.  Any other Ideas??

Regards

David

Mar 16, 2011 at 2:47 PM

My bad, indeed setting Action.Target will set the DataContext too. You should use Action.TargetWithoutContext to avoid the side effect you described.

The reasoning from above still applies to your scenario, since the MenuItem is displayed on a Popup. I should have written Popup, instead of ContextMenu! ><'

Mar 16, 2011 at 3:55 PM
BladeWise wrote:

My bad, indeed setting Action.Target will set the DataContext too. You should use Action.TargetWithoutContext to avoid the side effect you described.

The reasoning from above still applies to your scenario, since the MenuItem is displayed on a Popup. I should have written Popup, instead of ContextMenu! ><'

This works if you set Action.Target on each menu Item. However going back to my inital post, the problem with menuItems/Popups only occurs when inside of the DataTemplate,this is why I thought there may be a bug in Caliburn?? 

In the sample i attached I also had a menu outside of the listbox which worked fine without the need to set Action.Target on each MenuItem. So having to write something simliar to below for each menuItem seems like a lot of unnecessary XAML when it just works outside of the Data Template;

Snippet

Micro:Message.Attach="[Event Click] = [Action Details($dataContext)]" Micro:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}"
Mar 16, 2011 at 4:15 PM
Edited Mar 16, 2011 at 4:17 PM

The reason it works outside the ListBox, is that the DataContext of the MenuItem is indeed the view-model containing the Details method. If such method was defined on a parent view-model, you would have hit the same wall.

The 'unusual' behaviour is caused by two factors:

  • The action method is not available on the databound view-model
  • There is no way to determine a Popup 'visual parent' reliably

The first factor causes CM to navigate the visual tree to determine which view-model exposes the required Action. Unfortunally, since the Popup spawns its own visual tree, once reached the root there is no way to jump to the 'visual parent' visual tree reliably. The fact is that a Popup can be placed using a UIElement reference (PlacementTarget) or a screen position (plus some variants), so even if you could jump to the PlacementTarget by default, it could still fail under certain scenarios (causing the same exception you were experiencing).

Using the 'longer' version is indeed the proper way to deal with the issue, since the target view-model is defined explicitly and it is not necessary to navigate the visual tree.

That's why I don't consider it a bug: there is literally no other way to address this problem reliably (and that's why TargetWithoutContext was created, I suppose).

Coordinator
Mar 16, 2011 at 5:54 PM

Mr. BladeWise you speak the truth! :)

Mar 18, 2011 at 4:29 PM

If thats how CM works fair enough.

Many Thanks for the info.