Setting style based on existence of an ancestor type
I think @Xameli solution is what you are actually looking for...
but if you simply must do it in a style then you can achieve it using VisualTreeHelper
like that:
<Style.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Converter={StaticResource HasAncestorConverter}}" Value="True"> <Setter Property="Background" Value="{Binding Tag,RelativeSource={RelativeSource Self}}" /> </DataTrigger> </Style.Triggers>
the converter:
class HasAncestorConverter : IValueConverter{ public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { //you probably will have to look a few levels up var parent = VisualTreeHelper.GetParent(value) as ItemsControl; return item != null; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); }}
According to @makc's response I solved the problem this way:
<Grid> <Grid.Resources> <local:HasAncestorConverter x:Key="HasAncestorConverter" /> <Style TargetType="TextBlock"> <Style.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Converter={StaticResource HasAncestorConverter}}" Value="True"> <Setter Property="Background" Value="{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" /> </DataTrigger> </Style.Triggers> </Style> </Grid.Resources> <StackPanel> <TextBlock Text="Out of ItemControl" /> <ItemsControl Tag="Blue" > <TextBlock Text="Inside of ItemControl" /> </ItemsControl> </StackPanel></Grid>
Converter:
class HasAncestorConverter : IValueConverter{ public object Convert(object value, Type targetType, object parameter , System.Globalization.CultureInfo culture) { object parent = null; if (value != null && parameter != null && parameter is Type && value is DependencyObject) { var control = value as DependencyObject; Type t = parameter as Type; parent = ParentFinder.FindParent(control, t); } return parent != null; } public object ConvertBack(object value, Type targetType, object parameter , System.Globalization.CultureInfo culture) { throw new NotImplementedException(); }}
Helper class for finding the parent of specific type:
Note: This helper find any kind of parent in logical or visual tree. for example in my case ItemsControl
is a parent in the logical tree, and it can be a grandparent.
class ParentFinder{ public static object FindParent(DependencyObject child, Type parentType) { object parent = null; var logicalParent = LogicalTreeHelper.GetParent(child); var visualParent = VisualTreeHelper.GetParent(child); if (!(logicalParent == null && visualParent == null)) { if (logicalParent != null && logicalParent.GetType() == parentType) parent = logicalParent; else if (visualParent != null && visualParent.GetType() == parentType) parent = visualParent; else { if (visualParent != null) parent = FindParent(visualParent, parentType); if (parent == null && logicalParent != null) parent = FindParent(logicalParent, parentType); } } return parent; }}
Use DataTemplate
for the items in ItemsControl
.
<ItemsControl .... <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding }" Background="{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> </DataTemplate> </ItemsControl.ItemTemplate> <sys:String>Inside of ItemControl</String></ItemsControl>
Keep the style that you have if you need it for other setters, just remove the trigger.