WPF FindAncestor in binding - wpf

One particular thing about FindAncestor confuses me, have a look at the example below:
<Expander.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Name="headerLabel" Content="Show Contents" Padding="0" VerticalAlignment="Center" />
<Button Name="headerButton" Margin="6,0,0,0" Content="Button" Padding="6,1" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Expander}}, Path=IsExpanded}" Value="True">
<Setter TargetName="headerLabel" Property="Content" Value="Hide Contents" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Expander.HeaderTemplate>
I use the xaml above to change the text of my custom expander header. My question is, when do I actually need to explicitly use FindAncestor when I want to use a property of an ancestor in my binding? Because the following three bindings appear to yield the same result in my scenario at least:
Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Expander}}, Path=IsExpanded}"
Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}, Path=IsExpanded}"
Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type Expander}}, Path=IsExpanded}"
I have seen lots of examples of all three, is it just a matter of personal taste?

From the MSDN page about the RelativeSource.Mode property:
If this property is
not set explicitly, setting the AncestorType or the AncestorType and
the AncestorLevel properties will implicitly lock this property value
to FindAncestor.

Related

How fire trigger when one or multiple property had changed?

I have many components in my XAML, I need fire Trigger when one or many components has your value changed.
I not want use one PropertyChangedTrigger for each components, I want use one Trigger for all components.
thanks.
Yes, use a MultiDataTrigger. Here's an example from the linked MSDN documentation about how you can use this on multiple properties:
<Window.Resources>
<c:Places x:Key="PlacesData"/>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=State}" Value="WA">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=Name}" Value="Portland" />
<Condition Binding="{Binding Path=State}" Value="OR" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Cyan" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
<DataTemplate DataType="{x:Type c:Place}">
<Canvas Width="160" Height="20">
<TextBlock FontSize="12"
Width="130" Canvas.Left="0" Text="{Binding Path=Name}"/>
<TextBlock FontSize="12" Width="30"
Canvas.Left="130" Text="{Binding Path=State}"/>
</Canvas>
</DataTemplate>
</Window.Resources>
<StackPanel>
<TextBlock FontSize="18" Margin="5" FontWeight="Bold"
HorizontalAlignment="Center">Data Trigger Sample</TextBlock>
<ListBox Width="180" HorizontalAlignment="Center" Background="Honeydew"
ItemsSource="{Binding Source={StaticResource PlacesData}}"/>
</StackPanel>
EDIT: Not the cleanest solution ever, but you could do something like this. Basically use the MultiDataTrigger to execute whenever any of the properties change. Then, you use a converter for a simple null check (or perhaps you could always return true in your case). That way, your value in the MultiDataTrigger is just True instead of a specific value.

How to make a ToolTip not bigger than it's Control?

I have a Xceed DataGrid, and in one of the Columns there is a text that can be very long. I want to show just part of the text, and if the user hovers the mouse on top of it, a Tooltip will show the entire text.
I am trying to set the ToolTip's MaxWidh like this:
<DataTemplate x:Key="TooltipTextBlockTemplate">
<TextBlock Text="{Binding}" x:Name="DataTextBlock">
<TextBlock.ToolTip>
<TextBlock Text="{Binding}" TextWrapping="Wrap" MaxWidth="{Binding ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type xcdg:Column}}}" />
</TextBlock.ToolTip>
</TextBlock>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="">
<Setter TargetName="DataTextBlock" Property="ToolTipService.IsEnabled" Value="False" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
I have tried AncestorType={x:Type xcdg:Column}}}" and AncestorType={x:Type TextBlock}}}" but it just expands to the whole screen.

Image in first tab header in wpf TabHeader

I want to display an image in a first tab header of a TabControl in a currently fully working Prism MVVM WPF application.
Complete description as follows:
When the user select an item from the Category list in the left region it displays “More Details” and “Related Products” on the right region. This right region contains a TabControl inside a UserControl.
First Tab shows “More category Details” while second tab shows “Related Products”. Data is shown correctly. Now I want to display category thumbnail and the Category name in First tab header only.
I tried Using a HeaderTemplate on the First tab as follows
<TabControl VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" >
<TabItem Name="tabItemCategoryMoreInfo" >
<TabItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image x:Name="viewImage" Height="20" Width="20" Margin="0,0,2,0"
Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl} }, Path=Content.DataContext.SelectedParent.PictureBinary}"/>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem} }, Path=Content.DataContext.SelectedParent.CategoryName}"
VerticalAlignment="Center" FontSize="14" FontWeight="SemiBold" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem} }, Path=Content.DataContext.SelectedParent.PictureBinary}" Value="{x:Null}" >
<Setter TargetName="viewImage" Property="Source" Value="/CatalogModule;component/Images/ItemIcon.png" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</TabItem.HeaderTemplate>
<ContentControl prism:RegionManager.RegionName="CategoryMoreDetailsRegion" />
</TabItem>
<TabItem Header="Products" Name="tabItemCategoryProducts">
<ContentControl prism:RegionManager.RegionName="CategoryProductsRegion" />
</TabItem>
It didn’t show the product name or the product image. But it show only default image, so the Triggers looks working. Can some please help?
EDIT:
Initially I used the TabItem instead of TabControl in the image data path:
<Image x:Name="viewImage" Height="20" Width="20" Margin="0,0,2,0"
Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabItem} }, Path=Content.DataContext.SelectedParent.PictureBinary}"/>
The Paths in your DataTemplate Bindings are different... that may explain why it doesn't work. If I understand you correctly, you say that the Binding Path in the DataTrigger works, so perhaps changing your Binding Path for the ImageSource and TextBlock.Text properties might work?:
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image x:Name="viewImage" Height="20" Width="20" Margin="0,0,2,0"
Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabItem}}, Path=Content.DataContext.SelectedParent.PictureBinary}"/>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Content.DataContext.SelectedParent.CategoryName}"
VerticalAlignment="Center" FontSize="14" FontWeight="SemiBold" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Content.DataContext.SelectedParent.PictureBinary}" Value="{x:Null}" >
<Setter TargetName="viewImage" Property="Source" Value="/CatalogModule;component/Images/ItemIcon.png" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Just in case you can't see the difference, you were using this in your Image.Source Binding.Path:
AncestorType={x:Type TabControl}

How to set DataContext of elements based on datacontext of ContentControl on same UserControl

Following is the section from My Shell:
<StackPanel x:Name="stack" Orientation="Horizontal" Height="25" HorizontalAlignment="Right" Margin="0,4,0,0" Grid.Row="0">
<Button DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}, Path=DataContext}" Content="Back" prism:Click.Command="{Binding Path=GoBackCommand}"/>
<Button DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}, Path=DataContext}" Content="Forward" prism:Click.Command="{Binding Path=GoForwardCommand}" Margin="10,0,0,0"/>
</StackPanel>
<ContentControl x:Name="ActionContent" prism:RegionManager.RegionName="{x:Static inf:RegionNames.WorkspaceRegion}" Grid.Row="1">
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl" >
<Grid >
<Controls:RoundedBox/>
<ContentPresenter Margin="10,0,10,0" Content="{TemplateBinding Content}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasContent" Value="false">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
<DataTrigger Binding="{Binding ElementName=stack}">
<Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource Self},
Path=Content.DataContext}" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ContentControl.Template>
In ContentControl views are injecting from Modules (using ribbon tab). I want two button inside StackPanel may use ViewModels (DataContext) of injected views, for backward and Forward navigation.
Please Help, thanks!
I would start by having a good read at Prism Navigation Chapter,
and how to use the navigation journal.
Edit:
I don't think you can do what you want directly in XAML, but i can see 2 workarounds:
Define the commands as global CompositeCommand in the shell viewmodel, then have your views reister and unregister their own implementations as they are navigated.
Define a new Region in wich the views can inject their own navigation buttons.
Hope this can help you.

Getting parent ListBox selected index as CommandParamater from context menu item

I am trying to pass a listbox's Selected Index property as a command paramater to a context menu item, I have the command binding working (thanks to Will # ElementName Binding from MenuItem in ContextMenu) but I'm have trouble with my command paramater.
<UserControl>
<ListBox ItemsSource="{Binding myItems}">
<ListBox.Resources> <!-- The selected item is the item the mouse is over -->
<Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver,RelativeSource={RelativeSource Self}}"
Value="True">
<Setter Property="IsSelected" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="Edit" Grid.Column="4" Grid.Row="0" Tag="{Binding DataContext, ElementName=ProductBacklog}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove"
Command="{Binding PlacementTarget.Tag.RemoveStoryClickCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding <!--I NEED TO BIND TO THE LISTBOX-->, Path=SelectedIndex}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>
You can set the CommandParameter="{Binding }" to pass the current data item in that row to your Command
Edit
Just noticed your command is in a ContextMenu. ContextMenus are not part of WPF's default Visual Tree, so bindings do not work the same way. To bind to the current item, use the following:
<MenuItem Header="Remove"
Command="{Binding PlacementTarget.Tag.RemoveStoryClickCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding PlacementTarget.DataContext,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}" />
This will bind to the DataContext of whatever control the ContextMenu is placed on, so in this case it will be Button.DataContext

Resources