Bind to ItemsControl's DataContext from inside an ItemTemplate - wpf

I have an ItemsControl whose for the ItemTemplate DataTemplate contains a Button. I want the Command on the button to bind to a Command on the DataContext of the ItemsControl, not the ItemTemplate. I think the solution has to do with using RelativeSource, but my attempts so far have failed:
<ItemsControl ItemsSource="{Binding Games}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Path=GameSelectedCommand, Source={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding}"
Style="{StaticResource MenuButtonStyle}"
Content="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
How can I get the Button to bind to the GameSelectedCommand of the ItemsControl's DataContext object?

You're setting the source of the binding to the ItemsControl itself. Therefore, you'll need to dereference the DataContext of the ItemsControl:
Command="{Binding DataContext.GameSelectedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
How would you have known this? Take a look at your debug output window when running the app. You'll see a message along the lines of "Cannot resolve property 'GameSelectedCommand' on type 'ItemsControl'".

Related

WPF CommandParameter value is changing to null on execution [duplicate]

My WPF uses the MVVM approach. I'm trying to bind 2 controls within my list control
<ListBox ItemsSource="{Binding ParentDuplicate}" SelectedItem="{Binding SelectedParent, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<ContentControl Content="{Binding}" />
<Button Content="Delete me now"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType=Window}, Path=DeleteCommand}"
CommandParameter="{Binding FilePath}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The problem I'm having, is the DeleteCommand is not binding (the Ouput window informs me as well)
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Data.Binding', AncestorLevel='1''. BindingExpression:Path=DeleteCommand; DataItem=null; target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')
If I move this button to outside the ListBox, then the binding works and the event fires so I know the issue must be with the ListBox (I'm guessing the problem is the ItemsSource prevents the binding to anything but the ItemsSource bound property (in this case, ParentDuplicate))
So, within the Button control, there are 2 properties being bound, DeleteCommand and FilePath
Both of these properties live within my single ViewModel. FilePath is a child of ParentDuplicate and this binds as desired. The issue is only with the DeleteCommand. What am I doing wrong?
Edit
When I use the Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType=Window}, Path=DeleteCommand} it is looking in my MainWindow code behind, not at the ViewModel. How do I make it use the ViewModel?
I tried
Command="{Binding RelativeSource={RelativeSource AncestorType=xmlnsViewModel:MainWindowViewModel}, Path=DeleteCommand}"
but the above also results in the same binding errors
The ancestor search finds the control, not the DataContext, so you'll need to tell your binding where to find the DeleteCommand property. If your ViewModel is the DataContext of the MainWindow then you can just use:
<Button Content="Delete me now"
Command="{Binding RelativeSource={RelativeSource
Mode=FindAncestor, AncestorLevel=1, AncestorType=Window},
Path=DataContext.DeleteCommand}"
CommandParameter="{Binding FilePath}" />

Getting CurrentItem within a nested ItemsControl

I have an itemscontrol, with a item template. Inside this item template another itemscontrol exists. The latter ItemsControl has a button in its template, the command bound to this template needs to get the "parent" itemscontrol current item.
The structure looks something like this:
<ItemsControl x:Name="outerItemsControl" ItemsSource={Binding MyCollection}>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource={Binding MySecondCollection}>
<ItemTemplate>
<DataTemplate>
<Button Command="{Binding MyFantasticCommand}"
CommandParameter="{Binding ????}"/>
</DataTemplate>
</ItemTemplate>
</ItemsControl>
</DataTemplate>
<ItemControl.ItemTemplate>
</ItemsControl>
What should I replace the {Binding ????} with to get hold of the current item in MyCollection?
I've tried with both:
Binding ., ElementName=outerItemsControl
and
Binding Path="." RelativeSource="{RelativeSource AncestorType={x:Type ItemsControl}, AncestorLevel=2}
EDIT
Usually when we need to access the "current item" in an items control we do the following:
<ItemsControl x:Name="outerItemsControl" ItemsSource={Binding MyCollection}>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding MyCommand}" CommandParameter="{Binding .}"/>
</DataTemplate>
<ItemControl.ItemTemplate>
</ItemsControl>
I want to do the same as this example, but access the parent's "current" item from the child itemscontrol.
It seems you want to access the object with the MySecondCollection property from the inner DataTemplate.
This should work:
CommandParameter="{Binding DataContext,
RelativeSource={RelativeSource AncestorType=ContentPresenter, AncestorLevel=2}}"

Unable to Bind to ViewModel from within a RadGridView GridViewDataColumn CellTemplate

I have a View that is of type telerikDocking:RadDocumentPane. The first visual element on this view, and the container for all other visual elements, is a Grid with x:Name="MainGrid".
If I bind a property of the Grid to a public property on the ViewModel which is its DataContext, it works and sees it.
Inside this grid is a telerik:RadGridView and one of its columns is a Delete button whose visibility property is bound to the IsSelected property of the GridviewRow. That works fine as well.
I need to be able to override that Visibility of the button based on other criteria that is handled in a public property on the ViewModel. To accomplish this, I wrapped the button in a StackPanel and am trying to bind its Visibility to the ViewModel's public property and no matter what RelativeSource syntax I use, I cannot get to that property on the VieModel.
Here is the XAML for delete button:
<telerik:GridViewDataColumn>
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<StackPanel Visibility="{Binding ???}">
<telerik:RadButton Width="70"
Command="telerik:RadGridViewCommands.Delete"
CommandParameter="{Binding}"
Content="Delete"
IsEnabled="{Binding UserHasCofundingRole}"
Visibility="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType={x:Type telerik:GridViewRow}},
Converter={StaticResource booleanToVisibilityConverter}}" />
</StackPanel>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
Here are all the binding syntax attempts that I have tried for the StackPanel's Visibility but none ever hit the ViewModel:
{Binding DataContext.DeleteButtonVisible, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerikDocking:RadDocumentPane}}, Converter={StaticResource booleanToVisibilityConverter}}
{Binding ViewModel.DeleteButtonVisible, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerikDocking:RadDocumentPane}}, Converter={StaticResource booleanToVisibilityConverter}}
{Binding ElementName=MainGrid, Path=DataContext.DeleteButtonVisible, Converter={StaticResource booleanToVisibilityConverter}}
{Binding ElementName=LayoutRoot, Path=ViewModel.DeleteButtonVisible, Converter={StaticResource booleanToVisibilityConverter}}
{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DataContext.DeleteButtonVisible, Converter={StaticResource booleanToVisibilityConverter}}
Try to bind like this :
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path= DataContext.YourPublicProp}
if you are in a Window or Page, or smt else, replace "UserControl" with the right type.

How do I have alternative content in a DataGrid.RowHeaderTemplate?

In a DataGrid, I'm using the RowDetails area to load a UserControl with its own ViewModel - works very well.
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<vw:IndicesView DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
Path=DataContext.DrillDownIndexVM}" />
</DataTemplate>
</DataGrid.RowDetailsTemplate>
I now want to use alternative UserControls, depending on a property in the main ViewModel.
How can this be done? Is the answer?
Check out WPF datatemplate selector
http://www.switchonthecode.com/tutorials/wpf-tutorial-how-to-use-a-datatemplateselector

WPF + Binding Command and Header of a Context Menu Item using MVVM

I created a WPF application and am following MVVM Pattern. I have a context menu in my xaml and I need to bind the command and Header text. Using the following code I can bind the Header of the context menu with the "MenuItemName" which is a property in BOList which is an observable collection. My issue is that command is not getting fired? I changes the Item source of the Context Menu to datacontext
(DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}")
Command is working fine but my header is getting blank. Is there a way to bind my header and command of a menu item? Here the command MenuClick is a Icommand property in the VM and MenuItemName is a property inside BOList which is an observable collection binded to my ListBox.
<Grid>
<StackPanel Orientation="Vertical">
<Button x:Name="btnClickMe" Command="{Binding ButtonCommand}" Content="Click Me" />
<ListBox ItemsSource="{Binding BOList}" x:Name="lstDemo" SelectedItem="{Binding BussinessObj,Mode=OneWayToSource}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="stkStyleRender" Orientation="Horizontal" Background="Cyan" Width="525" Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" >
<TextBlock x:Name="txtId" Text="{Binding FirstName}"></TextBlock>
<TextBlock x:Name="txtName" Text="{Binding LastName}"></TextBlock>
<StackPanel.ContextMenu>
<ContextMenu x:Name="cntMnuTest" ItemsSource ="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}" >
<MenuItem Header="{Binding MenuItemName}" Command="{Binding MenuClick}" CommandParameter="Icon"></MenuItem>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
Hi Identified the issue.
If we set Item Source of the context menu to BOList (observable collection ) command wont get fired because the ICommand definition is in Window data context (vm).
We need to handle the code like wise.
Since debugging is not possible for binding , I was beating around the bush :-)
This link helped me a lot WPF Tutorial - Debug Databinding Issues in WPF
In context menu use DataContext instead of using Items source
then bind your menu item
try this:
<MenuItem Header="{Binding Path=PlacementTarget.MenuItemName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}" Command="{Binding Path=PlacementTarget.MenuClick, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}" />

Resources