Search code examples
c#wpfbindingdatatrigger

Conditional binding in a template using dependency properties


I have a button that has four dependency properties, each representing a state the button is in. Each state's source is an image. What I would like to achieve is an ability for any state other than NormalState to bind to NormalState's value if it's own value was not provided.

I tried doing it using DataTrigger, but I'm getting an exception:

Set property 'System.Windows.Setter.Value' threw an exception.

It's position points to this line:

<Setter Property="MouseOverState" Value="{TemplateBinding NormalState}"/>

Here's part of the XAML code, which is located in a ResourceDictionary:

<Style TargetType="{x:Type local:ImageButton}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ImageButton}">
                    <Grid>
                        <ContentControl>
                            <Grid>
                                <Image x:Name="Normal" Source="{TemplateBinding NormalState}" Visibility="Visible"/>
                                <Image x:Name="MouseOver" Source="{TemplateBinding MouseOverState}" Visibility="Hidden"/>
                                <Image x:Name="Pressed" Source="{TemplateBinding PressedState}" Visibility="Hidden"/>
                                <Image x:Name="Disabled" Source="{TemplateBinding DisabledState}" Visibility="Hidden"/>
                            </Grid>
                        </ContentControl>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=MouseOverState}" Value="{x:Null}">
                            <Setter Property="MouseOverState" Value="{TemplateBinding NormalState}"/>
                            <Setter Property="Width" Value="50"/>
                        </DataTrigger>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Normal" Property="Visibility" Value="Hidden"/>
                            <Setter TargetName="MouseOver" Property="Visibility" Value="Visible"/>
                            <Setter TargetName="Pressed" Property="Visibility" Value="Hidden"/>
                            <Setter TargetName="Disabled" Property="Visibility" Value="Hidden"/>
                        </Trigger>

...and so on...

Here's the use code:

In the template code you may notice the Setter of Width to the value of 50. This was done to check if that code detects missing ouseOverState property at all; and it does. However, when I try to set the MouseOverState property, I get the Setter Exception described above. Does anyone know what I'm doing wrong or is there an easier way? I couldn't figure out a way to do it with the control's code-behind within the dependency property code (that dependency code is pretty common to all other, so I'm not going to post it here, unless it's requested).

Here's another way I tried, but it did not work either (throwing the same exception, which does not provide a whole lot of details):

<Setter TargetName="Normal" Property="Source" Value="{TemplateBinding NormalState}"/>

I couldn't find a way, after much searching, of accomplishing this task within the C# code using Dependency Property setters either. However, if someone knows of it, that'll be a very acceptable solution as well.


Solution

  • TemplateBinding can only be used within a ControlTemplate.TemplateBinding is used for binding to the element properties within the template definition.Setters do not support TemplateBinding.


    A TemplateBinding is an optimized form of a Binding for template scenarios, analogous to a Binding constructed with {Binding RelativeSource={RelativeSource TemplatedParent}}.

    Visit this link1 and link2 for binding


    in your case try with

    <Setter TargetName="Normal" Property="Source" Value="{Binding Path=Propertyname, RelativeSource={RelativeSource AncestorType={x:Type local:ImageButton}}"/>
    

    or

       <Setter TargetName="Normal" Property="Source" Value="{Binding Path=Propertyname,  RelativeSource={RelativeSource TemplatedParent}}"/>