Style DataTrigger with reference to Type of parent control

10,104

Solution 1

<DataTrigger Binding="{Binding Path=Parent.Tag, RelativeSource={RelativeSource Mode=Self}}" Value="blub">

This code would not work, because type of Mode is actually BindingMode which is an Enumeration, and none of it's member is Self. So this assignment Mode=Self is wrong in your code. To know the possible values of Mode, click this.

The correct way to write this is,

<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}, Path=Tag}" Value="blub">

And of course, for this to work, you've to keep that Style for GroupBox which you've already written.

Solution 2

This worked for me:

        <Style.Triggers>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StatusBar}}, Path=DependencyObjectType.Name}" Value="StatusBar">
                <Setter Property="Margin" Value="0"/>
                <Setter Property="Padding" Value="0"/>
                <Setter Property="Background" Value="Chartreuse"/>
            </DataTrigger>
        </Style.Triggers>

It allows you to set style based on parent type without having to resort to Tag which should really be used by code rather than by markup.

Solution 3

Use following Code :

using DevExpress.Xpf.Core.Native;
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace BindingErrorHelper
{
    public class IsTypeFoundConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            FrameworkElement element = value as FrameworkElement;
            Type type = parameter as Type;
            if (element != null && type != null)
            {
                element = LayoutHelper.FindElement(element,type);
                if (element != null)
                    return true;
            }
            return false;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }
    }

    public class LayoutHelper
    {
        public static FrameworkElement FindElement(FrameworkElement treeRoot, Type type)
        {
        FrameworkElement parentElement = VisualTreeHelper.GetParent(treeRoot) as FrameworkElement;
        while (parentElement != null)
        {
            if (parentElement.GetType() == type)
                return parentElement;
            else
                parentElement = VisualTreeHelper.GetParent(parentElement) as FrameworkElement;
        }
        return null;
        }
    }
}

Write the XAML Code as :

<tt:IsTypeFoundConverter x:Key="isTypeFoundConverter"/>

<Style TargetType="Grid">
    <Style.Triggers>
        <DataTrigger Binding={Binding RelativeSource={RelativeSource Self}, Converter={StaticResource isTypeFoundConverter}, ConverterParameter={x:Type GroupBox}}" Value="true">
           <!-- <Setter Property="..." Value="..."/> -->
        </DataTrigger>
    </Style.Triggers>
</Style>
Share:
10,104
PeterE
Author by

PeterE

Updated on July 25, 2022

Comments

  • PeterE
    PeterE almost 2 years

    On my Window there are several GroupBox Controls, each containing a Grid Control. To those Grids I want to asign a Style. But only to those Grids that are directly in a GroupBox, all other Grids should not be affected.

    I have tried the following, which does not work as GetType() is no property.

    <Style TargetType="Grid">
        <Style.Triggers>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Parent.GetType()}" Value="{x:Type GroupBox}">
               <!-- <Setter Property="..." Value="..."/> -->
            </DataTrigger>
        </Style.Triggers>
    </Style>
    

    I have found a workaround, but it's not really a beautiful solution, as I have to modify the GroupBoxes:

    <Style TargetType="GroupBox">
        <Setter Property="Tag" Value="blub"/>
     </Style>
    <Style TargetType="Grid">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=Parent.Tag, RelativeSource={RelativeSource Mode=Self}}" Value="blub">
               <!-- <Setter Property="..." Value="..."/> -->
            </DataTrigger>
        </Style.Triggers>
    </Style>
    

    Obviously I could set the style for each Grid manually, but I'm trying to avoid that, as there are quite a lot of them. I hope you can find a way to make the first example work.

  • PeterE
    PeterE over 13 years
    I'm afraid you are wrong about this. My code does work just fine. Mode is not of type BindingMode, but of type RelativeSourceMode. My guess would be that you overlooked the braces in RelativeSource={RelativeSource Mode=Self} which make Mode a property of RelativeSource and not one of Binding.
  • Nawaz
    Nawaz over 13 years
    @Peter : You're right. This horizontal scrolling disturbed me and I didn't really notice the inner braces that makes Mode property of RelativeSource. Thanks for pointing out the mistake! :-)
  • PeterE
    PeterE over 8 years
    This looks promising. I find it interesting, that the (CLR) Type is only available through the GetType() method, while the DependencyObjectType, which AFAIK is basically a wrapper around the CLR type, is a property.
  • bigbyte
    bigbyte over 8 years
    Yep, I found it interesting as well. I was also looking into why GetType is a method instead of a property. There are a variety of opinions including efficiency, abusing, conventions about what a property getter should do, etc.
  • PeterE
    PeterE over 8 years
    ATM I have not oportunity to test it. Can you tell me if the following trigger definitions are equivalent? (Does mine even work?) My original idea: <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Parent.DependencyObjectType.Name}" Value="GroupBox"> Your version: <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StatusBar}}, Path=DependencyObjectType.Name}" Value="StatusBar">
  • bigbyte
    bigbyte about 8 years
    I don't have much WPF/xaml experience so take this with a grain of salt: yours specifies Path =Parent.DependencyObjectType.Name. This will only look at immediate parent. mine looks at any ancestor.