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

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}" />

Related

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.

DataGrid Column Header binding inside Expander: Binding source not found when expander is collapsed

I have DataGrid which has a column that is to be shown/hidden depending on a property. I have had no luck in showing/hiding the column, so we ended up taking the column out and put it in a separate DataGrid and place the second DataGrid inside an Expander.
The header of the second DataGrid is bound the the DataContext of the Expander (or DataGrid - same effect, have also tried binding it to the containing usercontrol).
<Expander ExpandDirection="Right" Style="{DynamicResource ExpanderDirectionRight}" IsExpanded="{Binding ShowLaterDate}">
<DataGrid AutoGenerateColumns="False" AlternationCount="2" SelectionMode="Single" ItemsSource="{Binding Rows, UpdateSourceTrigger=PropertyChanged}" CanUserDeleteRows="True" CanUserAddRows="False" CanUserResizeColumns="False" SnapsToDevicePixels="True" Visibility="{Binding Rows.HasContent, Converter={StaticResource BooleanToVisibilityConverter}}" HorizontalAlignment="Left" SelectedIndex="{Binding SelectedIndex, ElementName=BudgetDataGrid}">
<DataGrid.Columns>
<DataGridTemplateColumn CellTemplate="{StaticResource LaterDataTemplate}" Width="{StaticResource WidthOfValueColumns}">
<DataGridTemplateColumn.Header>
<Grid Margin="{StaticResource MarginOfTextBox}">
<userControls:DateOrAgeEditor Date="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}, Path=DataContext.LaterDate, UpdateSourceTrigger=LostFocus}" DateOfBirth="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}, Path=DataContext.DateOfBirth}" TabIndex="11"/>
</Grid>
</DataGridTemplateColumn.Header>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Expander>
This is all good an well.
Problem:
The bound value is shown when the usercontrol is displayed. BUT ONLY if the Expander is expanded!
If the Expander is collapsed when the usercontrol is displayed this is printed to output:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.Expander', AncestorLevel='1''. BindingExpression:Path=DataContext.DateOfBirth; DataItem=null; target element is 'DateOrAgeEditor' (Name='UserControl'); target property is 'DateOfBirth' (type 'DateTime')
System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.Expander', AncestorLevel='1''. BindingExpression:Path=DataContext.LaterDate; DataItem=null; target element is 'DateOrAgeEditor' (Name='UserControl'); target property is 'Date' (type 'DateTime')
When the Expander is then expanded the binding is not updated and continues to be 'broken'.
Question:
How do I get the binding to bind correctly even when the Expander is not initially expanded?
Just found a way of binding the Visibility of my column (http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/) this has however reintroduced the same problem as the expander. But easy to fix, just use the same kind of binding for the header-content as for the visibility.

Binding Context menu commands to ViewModel which is not in context Visuals tree

I have a treeView whose itemsource is a collection of my Model class.
I have added a context menu on the treeView. Since the commands of the contextMenu should be in the visual tree, so I had to place them in my Model class. Which is wrong (Binding directory to the Model).
How can I Bind my context menu's Command to my ViewModel rather than Model?
Thanks
You need not to place commands in model. Here you can access your commands in ViewModel like below:
Here Tag will contain the Binding to ViewModel and can be used to access the command.
<TreeView Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="MyCommand"
CommandParameter="{Binding }"
Command="{Binding Path=PlacementTarget.Tag.DataContext.MyCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
</ContextMenu>
</TreeView.ContextMenu>
</TreeView>

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.

Bind to ItemsControl's DataContext from inside an ItemTemplate

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'".

Resources