Error Using RelativeSource FindAncestor in WPF - c#

I do have an Window (DataContext VM) and a Listview (ItemSource Observable List in VM)
Data comes from an sqlite DB, the Observable List is Type of Model, representing Residents.
Each Resident should have an "CheckIn" or "CheckOut" Button, for them, i do have ICommands in my VM.
<ListView ItemsSource="{Binding Oc_BewohnerList}">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Name:" Width="50"/>
<TextBlock Text="{Binding Nachname, StringFormat={}{0}\, }" FontWeight="Bold"/>
<Button Content="Check In" Command="{Binding Source={RelativeSource AncestorType={x:Type viewModels:BewohnerVM}}, Path=CheckInCommand}" CommandParameter="{Binding Id}" />
Why do i get an Error "Die Eigenschaft "CheckInCommand" wurde im Objekt vom Typ "RelativeSource" nicht gefunden." That the CheckInCommand is not found?
I did it first with Xamarin.Forms and there it worked in a similiar way ... i cant find a solution myself, tried tons of combinations for realtivesource and ancestorlevel ...
Thx in advance
Nala

A relativesource binding is up the visual tree.
As in controls not viewmodels.
You're not going to be looking for a viewmodel.
If the command is a property on VM and that's the datacontext of the window then the ListView inherits that datacontext.
You've not explained everything but I think you need more like:
<Button Content="Check In"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListView}},
Path=DataContext.CheckInCommand}"
CommandParameter="{Binding Id}" />

Likely just a small syntax error and you want (note the double usage of RelativeSource , once as a property of Binding once as MarkupExtension...)
EDIT and also the AncestorType should be an UIElement, whose DataContext exposes that CheckInCommand.
<Button Content="Check In"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListView}}, Path=DataContext.(viewModels:BewohnerVM.CheckInCommand)}"
CommandParameter="{Binding Id}" />

Related

How to bind to another function, inside a container with ItemsSource [duplicate]

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

A command from a context menu that does not work

Well, I have a WPF project and I'm using Visual Studio 2010. I'm using C# and XAML, and I'm using the MVVM pattern.
The problem I have must be simple but I just can't see why it's not working.
Alright, so I have a project with a ListBox. In that ListBox are many ChatNodes; each represented visually. The visual element for a ChatNode is here:
<ControlTemplate x:Key="NodeVisualTemplate">
<Grid>
<Border BorderThickness="2" Margin="2" CornerRadius="5,5,5,5" BorderBrush="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.SelectionMode, Converter={StaticResource SelectionModeToColourConverter}}" ContextMenu="{StaticResource ChatNodeMenu}">
<StackPanel Opacity="{Binding IsInvisibleNode, Converter={StaticResource ResourceKey=VisibleToOpacityConverter}}">
<TextBlock Text="Test" Background="AntiqueWhite"/>
<TextBlock Text="{Binding Path=NodeText}" Background="Aqua"/>
<StackPanel Orientation="Horizontal">
<TextBox Text="Type here" MinWidth="50"/>
<Image Source="{StaticResource ImgFolder}" Margin="0,0,5,0" Width="32" Height="32"/>
</StackPanel>
</StackPanel>
</Border>
</Grid>
</ControlTemplate>
I draw your attention to the BorderBrush for the Border. I will show that again here:
BorderBrush="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.SelectionMode, Converter={StaticResource SelectionModeToColourConverter}}"
This code sits inside a ListBoxItem and is able to find the parent ListBox and then access a property of it. This works fine. The property is in a view model called ChatNodeListViewModel and looks like this:
private int _selectionMode = 0;
public int SelectionMode
{
get { return _selectionMode; }
set
{
if (_selectionMode != value)
{
_selectionMode = value;
RaisePropertyChanged("SelectionMode");
}
}
}
I mention it specifically because another thing which is almost identical is not working, even though this BorderBrush code is working.
So, on to the not working part.
In that same ControlTemplate above, we see a ContextMenu, called 'ChatNodeMenu'. This is as follows:
<ContextMenu x:Key="ChatNodeMenu" >
<MenuItem Header="Remove" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.RemoveChatNodeCommand}" />
</ContextMenu>
It has the same binding, only this time it is for a Command called 'RemoveChatNodeCommand'. The menu does appear on a right click on a ChatNode, but the command does not run. I have actually used almost identical code in other parts of my project so I assume it's correct... but clearly there is an error somewhere.
So where is this command? It is in the view model called 'ChatNodeListViewModel' and I will present it here:
void RemoveChatNodeExecute()
{
MessageBox.Show("Remove chat node");
return;
}
bool CanRemoveChatNode()
{
return true;
}
public ICommand RemoveChatNodeCommand { get { return new RelayCommand(RemoveChatNodeExecute, CanRemoveChatNode); } }
I have also used this in many places throughout my code and it's worked every time but this one.
So, either there's a fault in the code or perhaps a simple mistake. I've checked the name of the command and re-copied it several times. I've checked the other parts of my code where I've used the same code, but I can't see anything wrong. I've cleaned and rebuilt my project just in case.
If anyone can venture a guess, I would be very happy with that.
The ListBox is not a visual ancestor of a ContextMenu but you could set the Tag property of the Border to the ListBox and then bind to the PlacementTarget of the ContextMenu:
<Border ... Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}" ContextMenu="{StaticResource ChatNodeMenu}">
<MenuItem Header="Remove" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.Tag.DataContext.RemoveChatNodeCommand}" />
I have same issue related to conextmenu not able to work on command. could you help me to find solution.
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="test2">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="rr2" Command="{Binding DataContext.NextCommand, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}" CommandParameter="{Binding}" ></MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>

Cannot bind command within a ListBox

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

how to bind a command from DataContext of Page to a list item

I have a Page and a viewmodel is set as its datacontext. in that page I have a list. which is populating through a property in the viewmodel. List has a user control. and that user control has a button. I want that button to be bind with a command that is in viewmodel. Is there anyway to do that?
<Page DataContext=PageViewModel>
...
<ScrollViewer Grid.Row="3" Margin="20,0" Visibility="{Binding ByVenueSelected, Converter={StaticResource BooleanToVisibilityConverter}}">
<StackPanel>
<ItemsControl ItemsSource="{Binding EventsListByVenue}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<myControls:EventDetails /> <!--in this control i want to bind a command available in PageViewModel-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
...
</Page>
with help of #FunksMaName, I solved this. I am sure there are more elegant and better approach, but yet this is a quick & easy solution for me:
<Button Style="{StaticResource NoHighlightButtonStyle}" Tag="{Binding link}" CommandParameter="{Binding Path=Tag,RelativeSource={RelativeSource Mode=Self}}" Visibility="{Binding link,Converter={StaticResource DataAvailabilityToVisibilityConverter}}" Command="{Binding Path=Event.LinkCommand,Source={StaticResource Locator}}" >
<TextBlock Margin="0,5" Foreground="SkyBlue" Text="{Binding link}" TextWrapping="Wrap" FontSize="16"/>
</Button>
things to note:
i think xaml searched the command parameter in context of command only, so it was giving me null parameter, while the same binding was working fine for textblock inside Button. So i tricked it to store the value in tag, and used it from there.
Tag="{Binding link}" CommandParameter="{Binding Path=Tag,RelativeSource={RelativeSource Mode=Self}}"

Command binding inside a DataGridTemplateColumn

I am working on a Silverlight application which makes an extensive use of Prism, the MVVM pattern and MEF. For several reasons, I have chosen to follow a View-first approach.
In one of the Views there is a DataGrid, and one of the columns of this grid is a DataGridTemplateColumn, which has just a Button.
I'd like to define both a Command and a CommandParameter on the Button. The Command should be a DelegateCommand of the ViewModel. CommandParameter should be the SelectedItems list coming straight out of the dataGrid.
I've tried several approaches to do this, but either Command or CommandParameter are null.
It follows the code that I originally wrote:
<sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="15" Height="15" Content=">"
Command="{Binding UpdateSearchParametersCommand}"
CommandParameter="{Binding SelectedItems, ElementName=dataGrid}">
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
Could someone advice me on what the best way to go about it is?
Thanks in advance,
Gianluca.
Your current binding is pointing to DataGridRowItem.UpdateSearchParametersCommand. You need to change it to point to DataGrid.DataContext.UpdateSearchParametersCommand
<sdk:DataGrid x:Name=dataGrid>
<sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="15" Height="15" Content=">"
Command="{Binding DataContext.UpdateSearchParametersCommand, ElementName=dataGrid}"
CommandParameter="{Binding SelectedItems, ElementName=dataGrid}">
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid>
If you bind your DataGrid using ItemsSource, then Command and CommandParameter binding is associated to the current item - the way you've written.
You should use alternative source in this case. Command should be binded to the DataContext.UpdateSearchParametersCommand and CommandParameter - to DataContext.SelectedItems.
In your case neither UpdateSearchParametersCommand, nor SelectedItems cannot be found in the binded item.
UPDATED
Be sure to set the right type for ancestor. I've set it to window, but maybe you are using UserControl.
<sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="15" Height="15" Content=">"
Command="{Binding Path=DataContext.UpdateSearchParametersCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
CommandParameter="{Binding Path=DataContext.SelectedItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
In silverlight 5 you can do this
<Button Command="{Binding Path=DataContext.PreviewPublishCommand, RelativeSource={RelativeSource AncestorType=controls:ChildWindow}}" Content="Publish" />
Just adjust AncestorType to be whatever your top level element is (UserControl, ChildWindow, etc).
Many of you tried to help me out on this. Thank you for that.
Unfortunately the provided answers were mostly relative to WPF.
Here is how I've solved the problem:
<helpers:BindingHelper.Binding>
<helpers:BindingList>
<helpers:RelativeSourceBinding TargetProperty="Command" Path="DataContext.ToggleDataArchiveInheritanceCommand" RelativeMode="FindAncestor" AncestorType="ChildWindow" />
</helpers:BindingList>
</helpers:BindingHelper.Binding>
Ok, this comes from another point of the same application, but the principle is the same.
If a binding is defined inside a , the only way you have in Silverlight to reach out other elements that normally would be out-of-scope (as they are not part of the DataTemplate) is to walk through the xaml object tree. That's what the BindingHelper does.
Posting here as I hope the information will be useful to someone else.
Cheers,
Gianluca

Categories