Search code examples
wpfmultibindingwpf-style

TemplatedParent not working in MultiBinding: getting "UnsetValue" everytime


I am trying to bind visibility of a styled control to the condition that its tag is equal to the selected index of a TabControl.

I use RelativeSource TemplatedParent but it's not being set, and I suspect it's because I am not setting Template property in Style.

Here is my code:

    <Grid x:Name="Telas" Grid.Column="1">
        <Grid.Resources>
            <vw:TagIsIndexBooleanConverter x:Key="TagIsIndexBooleanConverter"/>
            <Style x:Key="SelectedIndexVisibleStyle" TargetType="UserControl">
                <Setter Property="Visibility" Value="Hidden"/>
                <Style.Triggers>
                    <DataTrigger Value="True">
                        <DataTrigger.Binding>
                            <MultiBinding Converter="{StaticResource TagIsIndexBooleanConverter}">
                                <Binding Path="Tag" RelativeSource="{RelativeSource TemplatedParent}"/>
                                <Binding Path="SelectedIndex" ElementName="menu"/>
                            </MultiBinding>
                        </DataTrigger.Binding>
                        <Setter Property="Visibility" Value="Visible"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Grid.Resources>

        <vw:TelaColetaView
            x:Name="telaColeta"
            DataContext="{Binding TelaColetaVM}"
            Style="{StaticResource SelectedIndexVisibleStyle}"
            Tag="0"/>

        <vw:TelaPacientesView
            x:Name="telaPacientes"
            DataContext="{Binding TelaPacientesVM}"
            Style="{StaticResource SelectedIndexVisibleStyle}"
            Tag="1"/>

        <vw:TelaConfiguraçõesView
            x:Name="telaConfigurações"
            DataContext="{Binding TelaConfiguraçõesVM}"
            Style="{StaticResource SelectedIndexVisibleStyle}"
            Tag="4"/>

    </Grid>

And converter:

public class TagIsIndexBooleanConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Any(v => v == null || v == DependencyProperty.UnsetValue))
            return Binding.DoNothing;

        var tag = System.Convert.ToInt32(values[0]);
        var index = System.Convert.ToInt32(values[1]);
        var result = tag == index;
        return result;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Solution

  • You can't use TemplatedParent in this case because there isn't one; that's meant to used inside a ControlTemplate to specify that the source for the binding is the control that you're applying the template to.

    <Binding Path="Tag" RelativeSource="{RelativeSource TemplatedParent}"/>
    

    But this isn't inside a ControlTemplate. Instead, Tag is just a property of the thing you're styling. Ordinarily you'd do a trigger like this for Tag:

    <Trigger Property="Tag" Value="0">
    

    ...but you need a multibinding to get the SelectedIndex from menu, and it's got to be a MultiDataBinding because you need to specify ElementName.

    So you need a binding. To bind to one of your own properties instead of a property of your DataContext, you bind with a RelativeSource of Self:

    <Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
    

    OP also found he had to set the TargetType of the Style to "UserControl", for the Tag binding to work.