Unable to Bind to ViewModel from within a RadGridView GridViewDataColumn CellTemplate - wpf

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.

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

WPF: Listbox or menu will not close on click (inside a Telerik Splitbutton)

I have the split button defined as below (in a control template). When the user selects a menu item, I expect the menu to close. I dont know if it's in the SplitButton or Menu that is not closing. It have tried with a ListBox as well, instead of a menu and got the same result.
IsSplitButtonOpen and all the other bound properties are defined in the viewmodel. IsSplitButtonOpen is set to false in the event handler of the command.
<telerik:RadSplitButton Content="New Search"
Height="22"
Visibility="{TemplateBinding AddButtonVisibility}"
AutoOpenDelay="0:0:0.5"
IsOpen="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:RadTabbedWindow}}, Path=DataContext.IsSplitButtonOpen}"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:RadTabbedWindow}}, Path=DataContext.ShowSearchCommand}"
CommandParameter="SearchAccountsView">
<telerik:RadSplitButton.DropDownContent>
<telerik:RadMenu Orientation="Vertical"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:RadTabbedWindow}}, Path=DataContext.NewTabListItems}" >
<telerik:RadMenu.ItemTemplate>
<DataTemplate>
<telerik:RadMenuItem Header="{Binding Text}"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:RadTabbedWindow}}, Path=DataContext.ShowSearchCommand}"
CommandParameter="{Binding ViewName}"
/>
</DataTemplate>
</telerik:RadMenu.ItemTemplate>
</telerik:RadMenu>
</telerik:RadSplitButton.DropDownContent>
</telerik:RadSplitButton>
Please, try to use CloseOnPopupMouseLeftButtonUp property and set it to True. Both RadSplitButton and RadDropDownButton have that property. You can even refuse IsOpen property if it has only one purpose.

TreeView ContextMenu MVVM Binding

I currently have a UserControl that uses the MVVM model.
In that control there is a TreeView, which displays some items. I have added a HierarchicalDataTemplate for this TreeView and in that template is a ContextMenu for the Items.
In the ViewModel, which is DataContext of the control (named RestoresTreeViewControl) is a command I want to bind one of the menu items to. However what I have done doesn't seem to be working. I am getting the usual can't find source for binding reference.
Here is the bit of code for the datatemplate that tried to bind the EditDatabaseCommand to one of the menu items.
<HierarchicalDataTemplate DataType="{x:Type model:Database}" >
<StackPanel>
<TextBlock Text="{Binding Name}" >
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit" Command="{Binding ElementName=RestoresTreeViewControl, Path=DataContext.EditDatabaseCommand}" />
<MenuItem Header="Delete"/>
<Separator/>
<MenuItem Header="Test Connection"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
Here is a section of the ViewModel where the command is.
public ICommand EditDatabaseCommand { get; private set; }
Unfortunately the ContextMenu is not in the VisualTree, so it's not going to see your DataContext. What you can do is something like this (copied from here: MVVM binding command to contextmenu item)
<Button Height="40" Margin="0,2,0,0" CommandParameter="{Binding Name}"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type UserControl}}}" Command = "{Binding
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}},
Path=DataContext.ConnectCommand}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove"
CommandParameter="{Binding Name}"
Command="{Binding Path=PlacementTarget.Tag.DataContext.RemoveCommand,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
</ContextMenu>
</Button.ContextMenu>
So simply use PlacementTarget.Tag to find your ViewModel.
You can try tracing the binding:
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
...
{binding ... diag:PresentationTraceSources.TraceLevel="High"}
However requiring the users (even if it is just yourself) of your control to name each instance of "RestoresTreeViewControl" rather burdensome.
Try:
{Binding Path=... RelativeSource={ FindAncestor, AncestorType={x:TheRestoresTreeViewControlType}} }
That probably has to do with the inheritance context.
See: Binding WPF ContextMenu MenuItem to UserControl Property vs ViewModel Property

WPF Binding to ElementName inside ItemsControl

I have a checkbox, and an ItemsControl populating several DataGrids the following way:
<Checkbox Content="Birthday Column Visible" x:Name="UI_BirthdayVisibleCB" />
<ItemsControl ItemsSource="{Binding Path=ParentsCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Children}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Birthday" Width="120" Visibility="{Binding IsChecked, ElementName=UI_BirthdayVisibleCB, Converter={StaticResource BoolToVis}}" >
...
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Rest of closing tags>
This creates binding output errors as it tries to find IsChecked on the DataGridTemplateColumn. If I try to search for a Relative Ancestor I receive the exception:
Binding.RelativeSource cannot be set while using Binding.ElementName.
I have a ViewModel, and stick to MVVM mostly, but in this case I'd really like to keep the column visibilities on the View layer. Note that BoolToVis just converts Boolean to Visibility.
Edit
Here is an example of what I'm trying to do:
<DataGridTemplateColumn Header="Birthday" Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyView} }, Path=IsChecked, ElementName=UI_BirthdayVisibleCB, Converter={StaticResource BoolToVis}}" />
It compiles but doesn't run however, it throws the exception above.
You are using RelativeSource, which can't be mixed with ElementName, but you once you have the correct RelativeSource, you can drill down deeper using path.
e.g.
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyView} }, Path=UI_BirthdayVisibleCB.IsChecked, Converter={StaticResource BoolToVis}}"
presumably you have some xaml like this:
<UserControl class="MyView" ... >...<CheckBox Name="UI_BirthdayVisibileCB"/> ...
The above binding should find this UserControl by type based on RelativeSource, then it will try to find a property named UI_BirthdayVisibleCB, which it won't find because WPF XAML implements this named element as a field.
The easy work around is to go into your codebehind and expose a property for it.
public object BirthdayVisibileCB_4_binding {
get { return UI_BirthdayVisibileDB; }
}
and bind to it instead:
Visibility="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:MyView} },
Path=BirthdayVisibileCB_4_binding.IsChecked, Converter={StaticResource BoolToVis}}"
Yes, it kind of a pain to do this, but MVVM only matches WPF so far... its not a great fit, its only the best fit we have around.
If you want to try RelativeSource, you have to remove ElementName from the declaration:
However, only one of the three
properties, ElementName, Source, and
RelativeSource, should be set for each
binding, or a conflict might occur.
This property throws an exception if
there is a binding source conflict.
http://msdn.microsoft.com/en-us/library/system.windows.data.binding.elementname.aspx
Your usage of ElementName seems correct, so I'll continue to look at the problem if you prefer that over RelativeSource.

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