Help binding command parameter to relative source - wpf

I have a ListBox that I have added a ContextMenu to. I want one of the items in the ContextMenu to be bound to a command and I want the parameter passed to that command to be the currently selected item of the ListBox control. Here's my xaml:
<ListBox x:Name="selectedVascularBeds"
ItemsSource="{Binding Path=UserSelectedVascularBeds}"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DropHandler="{Binding}"
DisplayMemberPath="VascularBedName">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Command="{Binding Path=RemoveSelectedVascularBedCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBox}},
Path=SelectedItem}"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
This ListBox is part of a user control that is bound to a view model object. My command method on the underlying object gets called but the parameter passed in is always null.
I have tested changing the binding of the CommandParameter to simply {Binding} which results in the user control's data context being passed into my method - so I know that the command is working and passes parameters correctly. I just can't seem to get the correct binding to access the ListBox's SelectedItem property.
Help?

the context menu is not a descendant of the list box. try an element name binding instead
<MenuItem Header="Remove" Command="{Binding Path=RemoveSelectedVascularBedCommand}" CommandParameter="{Binding ElementName=selectedVascularBeds, Path=SelectedItem}"/>

The ElementName binding also didn't work, the parameter was still null and I found an error in the console output:
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'ElementName=selectedVascularBeds'.
BindingExpression:Path=DataContext; DataItem=null; target element is
'MenuItem' (Name=''); target property is 'CommandParameter' (type
'Object')
Searching for that error lead me to this link though and it looks like the Context menu is different and I can't achieve what I want the way I'm going about it.
ElementName Binding from MenuItem in ContextMenu

Related

WPF Resources.ContextMenu.MenuItem binding to ContextMenu.PlacementTarget.(AttachedProperty)

if the title didn't scare you away, here goes...
<UserControl.Resources>
<!-- so the attached CustomObject can bind to the context -->
<my:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
<!-- for chaining IsNull to Visibility.Collapsed -->
<my:ConverterGroup x:Key="IsNullToVisibility">
<my:IsNullConverter />
<my:VisibilityValuesEqual />
</my:ConverterGroup>
<ContextMenu x:Key="ctxmnu">
<MenuItem Header="Copy" Click="ctxmnu_itmCopy_Click" />
<MenuItem Header="Add" Click="ctxmnu_itmAdd_Click"
IsEnabled="{Binding Source={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu},
Path=PlacementTarget,
Converter={StaticResource IsNotNullConverter}}"
Visibility="{Binding Source={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu},
Path=PlacementTarget.(myAP:APClass.Property),
Converter={StaticResource IsNullToVisibility}}"
/>
</ContextMenu>
</UserControl.Resources>
<Label Content="{Binding Path=Description}" ContextMenu="{StaticResource ctxmnu}">
<myAP:APClass.Property>
<myAP:CustomObject ID="{Binding Source={StaticResource DataContextProxy}, Path=ID}" />
</myAP:APClass.Property>
</Label>
</UserControl>
Basically, I've got a context menu, with two menu items... the first (copy) is always available... the second (Add) is only available if the context menu is applied from a UIElement with the attached property.
Most of this works... attached property is correctly binding via the proxy resource, menuitem click event is able to get the attached property value.
The only thing that DOESN'T work is the MenuItem.IsEnabled/Visibility binding (I was initially going to bind visibility, but lately I'm thinking IsEnabled is a better idea for UX).
The error is in the binding. Standard error 40 in the output window.
System.Windows.Data
Error: 40 :
BindingExpression path error:
'PlacementTarget' property not found on 'object' ''RelativeSource' (HashCode=22838427)'.
BindingExpression:Path=PlacementTarget.(0); DataItem='RelativeSource' (HashCode=22838427);
target element is 'MenuItem' (Name='');
target property is 'Visibility' (type 'Visibility')
Thing is, every article or example I've been able to find (regarding MenuItem binding to PlacementTarget) has the context menu directly on the UIElement, and since the binding occurs when the window/control is instantiated (before the context menu is shown), I'm thinking that the PlacementTarget is NULL and thus error.
Thx in advance!
The problem is you're using the Source property of the binding to assign a RelativeSource. It should be:
IsEnabled="{Binding RelativeSource={RelativeSource
^^^^^^^^^^^^^^
If you look at the error it actually explains it:
'PlacementTarget' property not found on 'object' ''RelativeSource'
The object should've been the context menu.

Caliburn Micro with Treeview Context Menu

I have my hierarchical treeview binding wonderfully to my ViewModel using Caliburn Micro. (The ViewModel has an Items property that returns an ObservableCollection - the treeview is named to this Items property - nothing wrong with the binding).
However the issue comes up with the context menu. The menu fires a method on an instance of the object that the treenode represents. What I rather want to achieve, is to have the menu fire a method on my root ViewModel, passing to it as a parameter the instance of the object represented by the clicked treenode.
Here is my XAML:
<HierarchicalDataTemplate DataType="{x:Type m:TaskGrouping}"
ItemsSource="{Binding Children}">
<Label Content="{Binding Name}"
FontWeight="Bold">
<Label.ContextMenu>
<ContextMenu>
<MenuItem Header="Add New SubFolder"
cal:Message.Attach="AddNewSubfolder" />
<MenuItem Header="Remove this folder"
cal:Message.Attach="RemoveFolder" />
</ContextMenu>
</Label.ContextMenu>
</Label>
</HierarchicalDataTemplate>
What changes do I need to make to my XAML in order to achieve what I want?
ContextMenus are located in a separate visual tree from everything else - it can be a pain to get the bindings right (I often have 10-15 minutes of fighting the bindings on them to get them right!)
You've got your Message.Attach attached property set, all you need to do is ensure that the action target is pointing to the VM rather than the data item. You can use Action.TargetWithoutContext to specify the target for actions (CM will otherwise use DataContext)
You will also need to get a binding path which points to the other visual tree - try using RelativeSource bindings - the ContextMenu also has a property called PlacementTarget which should point to the element that the ContextMenu is attached to
So possibly:
cal:Action.TargetWithoutContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Label}}"
or
cal:Action.TargetWithoutContext="{Binding PlacementTarget.DataContext}"
You might have to experiment as I often get this almost right first time!
EDIT by OP(Shawn):
This is what worked for me eventually:
<Label Content="{Binding Name}"
Tag="{Binding DataContext, ElementName=LayoutRoot}">
<Label.ContextMenu>
<ContextMenu
cal:Action.TargetWithoutContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Run Task Now" cal:Message.Attach="SomeRootViewModelMethod($dataContext)" />

Access my Window's DataContext inside a DataTemplate

I'm working on a WPF application and I'm using the MVVM pattern. I use MVVMLight to help me handle some Events. I need to forward the "Click" event so that I can pass the arguments as well so that I can know for sure which item that sent the event. If I use the "Command" I cant know for sure that it was the selected item that sent the event - as the item doesnt need to be selected to right click on it.
This is my code for displaying a list of "order lines". There are two types of order lines, and for one of the data types; "AccessoryOrderLine" - I want to add a context menu.
My problem is that I cannot access my Window's DataContext. I've named the root node in the Window "root", and I'm trying to access the root's DataContext, but this failes with the following error:
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'ElementName=root'.
BindingExpression:Path=DataContext.PackAccessory; DataItem=null;
target element is 'EventToCommand' (HashCode=5903270); target property
is 'Command' (type 'ICommand')
<ListBox HorizontalContentAlignment="Stretch" Margin="10,0,10,10" DockPanel.Dock="Bottom" Grid.Row="1" ItemsSource="{Binding OrderLines, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type m:UnitOrderLine}">
<v:OrderLine />
</DataTemplate>
<DataTemplate DataType="{x:Type m:AccessoryOrderLine}">
<v:OrderLine>
<v:OrderLine.ContextMenu>
<ContextMenu>
<MenuItem Header="Pack 1" IsCheckable="False">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand Command="{Binding ElementName=root, Path=DataContext.PackAccessory }" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</MenuItem>
</ContextMenu>
</v:OrderLine.ContextMenu>
</v:OrderLine>
</DataTemplate>
</ListBox.Resources>
</ListBox>
I've also tried to use "TemplatedParent" and then I get access to my "OrderLine" DataContext, but I cant get one step further back to my "MainWindowModel".
http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
Found a solution to my problem :)
Found a solution. Updated my original post with the link to my solution.
Its not issue with the DataTemplate. Binding with ElemetName works in all cases except in the case of ContextMenu since it does not lies in the same visual tree as of your window. However, there is one hack where you can use the PlacementTarget property of your context menu.
For details refer to this link - http://social.msdn.microsoft.com/Forums/nl/wpf/thread/526ab350-8788-4bc6-a98a-1e4dee6ad33a
It contains exactly what you are trying to achieve here.
Seems like here are answers for your question:
ElementName Binding from MenuItem in
ContextMenu
WPF MenuItem.Command binding to
ElementName..

Cannot set CommandParameter using ElementName

I have a TreeView with ContextMenu, and inside that menu I want to bind to a command on VIewModel
<TreeView x:Name="treeView"
ItemTemplate="{StaticResource ItemTemplate}"
ItemsSource="{Binding View}">
<TreeView.ContextMenu>
<ContextMenu>
<telerik:RadMenuItem Header="Remove" Command="{Binding RemoveCommand}" CommandParameter="{Binding ElementName=treeView, Path=SelectedItem, Mode=OneWay}" />
</ContextMenu>
</TreeView.ContextMenu>
</TreeView>
I receive an exception in Output window, like
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=treeView'. BindingExpression:Path=SelectedItem; DataItem=null; target element is 'RadMenuItem' (Name=''); target property is 'CommandParameter' (type 'Object')
I am actually using RadTreeView, but the same applies for TreeView. Why I cannot bind to TreeView's SelectedItem property? I have tried with RelativeSource AncestorType, the same problem.
The problem is that, like the Popup control, it's a different visual tree. The error is telling you that it is trying to find a property called 'CommandParameter' on a 'RadMenuItem', because this is the DataContext it has within the ContextMenu's visual tree.
This will help you: Placement Target
I ended up setting the CommandTarget property of the MenuItem to the ContextMenu's PlacementTarget property, but it doesn't look like that's the approach you're taking. Even still, the PlacementTarget is what you're looking for.

WPF Treeview contextmenu IsChecked binding MVVM

I've got a TreeView to which I associate a ContextMenu. That contextmenu has an item whose IsChecked property I want to bind to my ViewModel. Since I am using a tree each treeitem is bound to a subproperty of my ViewModel.
In the VS2010 output window I am seeing this databinding error:
BindingExpression path error: 'IsAutoStart' property not found on 'object' ''HostMgmtViewModel' (HashCode=12565727)'. BindingExpression:Path=IsAutoStart; DataItem='HostMgmtViewModel'
This clearly shows it is trying to bind to my ViewModel and not to the treeitem's associated data. How do I bind to the correct object? Remember my contextmenu is associated with the whole TreeView not to the specific treeitem.
---------- Edit
As xandy pointed out below the resolution to my problem was to bind the IsChecked like this:
{Binding Path=PlacementTarget.SelectedItem.IsDisabledStart, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}
<TreeView Name="tview" Grid.Row="0" Tag="{Binding RelativeSource={RelativeSource Self}, Path=SelectedItem}">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Name="miC" Header="{Binding Path=Tag.Key}" DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"></MenuItem>
</ContextMenu>
</TreeView.ContextMenu>
</TreeView>
This is the working code snippet I have. Courtesy of [this].1 All you need is to change the binding path in the tag. I am currently binding the Treeview to a dictionary, so it is the Key property of it. It should not have any problem in binding to any object collections. One interesting finding is context menu is not in part of element tree and this cause the problem. I could bind the text box with no problem:
<TextBlock Grid.Row="1" DataContext="{Binding ElementName=tview, Path=SelectedItem}">
<TextBlock.Text>
<Binding Path="Key" />
</TextBlock.Text>
</TextBlock>
But it is not functioning if for menuitem if I put the same thing.

Resources