0
votes

I'm trying to bind to a property from my control style to a property defined in the control. The control inherits from TextBox and is called ChangeTextBoxWithRangeUnits. I'm trying to bind to them from a ValidationRule class that I created. Here is the Setter for where I'm trying to validate the text

 <Setter Property="Text">
        <Setter.Value>
            <Binding RelativeSource="{RelativeSource Self}"
                     Path="Text"
                     NotifyOnValidationError="True">
                <Binding.ValidationRules>
                    <basic:DoubleValidationRule>
                        <basic:DoubleValidationRule.MinMaxDependencyObject>
                            <basic:MinMaxDependencyObject Minimum="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithRangeUnits}}, Path=MinimumValue}"
                                                          Maximum="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithRangeUnits}}, Path=MaximumValue}" />
                        </basic:DoubleValidationRule.MinMaxDependencyObject>
                    </basic:DoubleValidationRule>
                </Binding.ValidationRules>
            </Binding>
        </Setter.Value>
    </Setter>

I don't understand why it says that the source binding cannot be found. Here is the error:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='Frasca.Simplicity.Controls.UnitControls.ChangeTextBoxWithRangeUnits', AncestorLevel='1''. BindingExpression:Path=MinimumValue; DataItem=null; target element is 'MinMaxDependencyObject' (HashCode=16320155); target property is 'Minimum' (type 'Double')

EDIT Complete XAML

 <Style x:Key="ChangeTextBoxWithRangeUnits"
       TargetType="{x:Type local:ChangeTextBoxWithRangeUnits}"
       BasedOn="{StaticResource {x:Type TextBox}}">
    <Setter Property="Validation.ErrorTemplate"
            Value="{x:Null}" />
    <Setter Property="Height"
            Value="64" />
    <Setter Property="FontSize"
            Value="22" />
    <Setter Property="Margin"
            Value="5" />
    <Setter Property="Background"
            Value="LightGray" />
    <Setter Property="BorderThickness"
            Value="0" />
    <Setter Property="KeyboardNavigation.TabNavigation"
            Value="None" />
    <Setter Property="AllowDrop"
            Value="true" />
    <Setter Property="ScrollViewer.PanningMode"
            Value="VerticalFirst" />
    <Setter Property="Stylus.IsFlicksEnabled"
            Value="False" />
    <Setter Property="Text">
        <Setter.Value>
            <Binding RelativeSource="{RelativeSource Self}"
                     Path="Text"
                     NotifyOnValidationError="True">
                <Binding.ValidationRules>
                    <basic:DoubleValidationRule>
                        <basic:DoubleValidationRule.MinMaxDependencyObject>
                            <basic:MinMaxDependencyObject Minimum="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithRangeUnits}}, Path=MinimumValue}"
                                                          Maximum="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithRangeUnits}}, Path=MaximumValue}" />
                        </basic:DoubleValidationRule.MinMaxDependencyObject>
                    </basic:DoubleValidationRule>
                </Binding.ValidationRules>
            </Binding>
        </Setter.Value>
    </Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:ChangeTextBoxWithUnits}">
                <Grid>
                    <Border Name="bg"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Width="{TemplateBinding Width}"
                            CornerRadius="15"
                            SnapsToDevicePixels="true">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="3*" />
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="2*" />
                            </Grid.RowDefinitions>

                            <StackPanel Orientation="Horizontal"
                                        Grid.Row="0"
                                        Name="mValueStackPanel"
                                        VerticalAlignment="Bottom"
                                        HorizontalAlignment="Center">
                                <ScrollViewer x:Name="PART_ContentHost"
                                              SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                                <TextBox Name="PART_UnitTextBlock"
                                         Style="{DynamicResource InlineTextBox}"
                                         Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithUnits}}, Path=Units}"
                                         FontSize="14"
                                         Margin="3,0,0,0"
                                         Foreground="{TemplateBinding Foreground}" />
                            </StackPanel>

                            <StackPanel Orientation="Horizontal"
                                        Grid.Row="2"
                                        HorizontalAlignment="Center"
                                        VerticalAlignment="Top">
                                <TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithUnits}}, Path=Label}"
                                           FontSize="14"
                                           Foreground="{TemplateBinding Foreground}" />
                            </StackPanel>

                            <TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithUnits}}, Path=DisabledText}"
                                       Foreground="{TemplateBinding Foreground}"
                                       Grid.Row="0"
                                       FontSize="14"
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Bottom"
                                       Visibility="Collapsed"
                                       x:Name="mDisabledTextBlock" />

                            <!-- Horizontal line -->
                            <Rectangle Height="1"
                                       Margin="10,0"
                                       Grid.Row="1"
                                       Opacity="0.15"
                                       SnapsToDevicePixels="True"
                                       Fill="{TemplateBinding Foreground}" />

                            <!-- Object which flashes when the textbox is selected -->
                            <Border Grid.RowSpan="3"
                                    Background="White"
                                    Name="FlashObject"
                                    CornerRadius="15"
                                    Opacity="0">
                                <Border.Effect>
                                    <BlurEffect Radius="20" />
                                </Border.Effect>
                            </Border>

                            <!-- Object which flashes when the textbox has a validation error-->
                            <Border Grid.RowSpan="3"
                                    Grid.ColumnSpan="2"
                                    Background="Red"
                                    Name="ErrorFlashObject"
                                    CornerRadius="15"
                                    Opacity="0">
                                <Border.Effect>
                                    <BlurEffect Radius="20" />
                                </Border.Effect>
                            </Border>
                        </Grid>
                    </Border>

                    <!-- Object which glows when the user makes a change to the text value. -->
                    <Border Width="{Binding ElementName=bg, Path=ActualWidth}"
                            Height="{Binding ElementName=bg, Path=ActualHeight}"
                            CornerRadius="15"
                            Background="#FFF066"
                            Name="ChangeGlowObject"
                            Panel.ZIndex="-1"
                            Visibility="Collapsed">
                        <Border.Effect>
                            <BlurEffect Radius="20" />
                        </Border.Effect>
                    </Border>
                </Grid>
                <ControlTemplate.Triggers>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="HasChangedValue"
                                       Value="True" />
                            <Condition Property="IsEnabled"
                                       Value="True" />
                        </MultiTrigger.Conditions>
                        <Setter Property="Visibility"
                                TargetName="ChangeGlowObject"
                                Value="Visible" />
                    </MultiTrigger>
                    <Trigger Property="IsEnabled"
                             Value="False">
                        <Setter Property="Background"
                                Value="#686868" />
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsEnabled"
                                       Value="False" />
                            <!--<Condition Property="ShowTextWhenDisabled"
                                       Value="False" />-->
                        </MultiTrigger.Conditions>
                        <Setter Property="Visibility"
                                TargetName="mDisabledTextBlock"
                                Value="Visible" />
                        <Setter Property="Visibility"
                                TargetName="mValueStackPanel"
                                Value="Collapsed" />
                    </MultiTrigger>

                    <!-- trigger to flash the object when the textbox has an error -->
                    <Trigger Property="Validation.HasError"
                             Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation BeginTime="00:00:00"
                                                     Duration="00:00:00.2"
                                                     From="0"
                                                     To="1"
                                                     Storyboard.TargetName="ErrorFlashObject"
                                                     Storyboard.TargetProperty="Opacity">
                                        <DoubleAnimation.EasingFunction>
                                            <PowerEase Power="2"
                                                       EasingMode="EaseIn" />
                                        </DoubleAnimation.EasingFunction>
                                    </DoubleAnimation>
                                    <DoubleAnimation BeginTime="00:00:00.2"
                                                     Duration="00:00:00.5"
                                                     From="1"
                                                     To="0"
                                                     Storyboard.TargetName="ErrorFlashObject"
                                                     Storyboard.TargetProperty="Opacity">
                                        <DoubleAnimation.EasingFunction>
                                            <PowerEase Power="2"
                                                       EasingMode="EaseIn" />
                                        </DoubleAnimation.EasingFunction>
                                    </DoubleAnimation>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                    </Trigger>

                    <!-- trigger to flash the object when the textbox is selected -->
                    <EventTrigger RoutedEvent="FocusManager.GotFocus">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation BeginTime="00:00:00"
                                                 Duration="00:00:00.2"
                                                 From="0"
                                                 To="1"
                                                 Storyboard.TargetName="FlashObject"
                                                 Storyboard.TargetProperty="Opacity">
                                    <DoubleAnimation.EasingFunction>
                                        <PowerEase Power="2"
                                                   EasingMode="EaseIn" />
                                    </DoubleAnimation.EasingFunction>
                                </DoubleAnimation>
                                <DoubleAnimation BeginTime="00:00:00.2"
                                                 Duration="00:00:00.5"
                                                 From="1"
                                                 To="0"
                                                 Storyboard.TargetName="FlashObject"
                                                 Storyboard.TargetProperty="Opacity">
                                    <DoubleAnimation.EasingFunction>
                                        <PowerEase Power="2"
                                                   EasingMode="EaseIn" />
                                    </DoubleAnimation.EasingFunction>
                                </DoubleAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
1
Can you add a complete example of your XAML?Blachshma

1 Answers

1
votes

RelativeSource bindings don't walk up your XML, they walk up the Visual Tree. That means you can only use it whenever the element you are binding is within that tree.

In this case, your DoubleValidationRule is not a UIElement, and it is not in the visual tree, so RelativeSouce bindings on this rule will fail.

Unfortunately, without knowing the implementation or purpose of your DoubleValidationRule.MinMaxDependencyObject, its hard to say what you should do. You might be able to get around this limitation by altering the implementation of the validation rule.

One way that will work is that you can refer to an element in the tree by its x:Name value

Minimum="{Binding MinimumValue, ElementName=TheMinimumTarget}

But that probably means you can't do this using a Style, as each use will (probably) be bound to a different element on your form.


Looking at your edit... Wat? You're binding the text of the control to the text of the control in order to attach a validation rule? That's... I don't even.

I'd suggest subclassing the control in order to add validation logic (e.g., override the property metadata for the Text dependency property), and then apply your template to this new type.