views:

34

answers:

1

I am creating a custom text box control to have some default formatting and validation functionality, including a custom validation property which accepts an Action or custom function.

In my current situation I can't use the Attribute validation framework that some people have posted, since I don't quite have access to modify the data model objects. I also cannot use ValidationRules because not all textboxes that require these validations will be bound.

I have used the generic textbox style in the static constructor rather than define my own, but I added some data triggers to set the border and tooltip based on my custom IsValid dependency property.

Everything seems to be working well, but the problem is when I mouse over or click on a textbox that has failed validation, the "invalid" style disappears and it goes to the default textbox style.

I have tried to create some additional datatriggers for IsMouseOver, IsFocused and IsMouseCaptured event (shown below) to no avail.

Am I missing something?

The Static Constructor (showing I'm using the TextBox style:

    static ValidatorTextBox()
    {
        //Commenting this line out to use the default textbox style
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ValidatorTextBox), new FrameworkPropertyMetadata(typeof(TextBox)));
    }

And here's my style:

 <Style TargetType="{x:Type local:ValidatorTextBox}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsValid}" Value="False">
            <Setter Property="BorderBrush" Value="Red" />
            <Setter Property="BorderThickness" Value="1" />
        </DataTrigger>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsRequired}" Value="True">
            <Setter Property="Background" Value="AliceBlue" />
        </DataTrigger>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsFocused}" Value="True">
            <Setter Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource Self}, Path=BorderBrush}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True">
            <Setter Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource Self}, Path=BorderBrush}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseCaptured}" Value="True">
            <Setter Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource Self}, Path=BorderBrush}" />
        </DataTrigger>
    </Style.Triggers>
</Style>
A: 

The problem is most likely being caused by the default TextBox template. In Aero (and I think similarly in some other themes too) the border is actually a chrome control that is changed to a specific color on mouseover. Since this happens inside the ControlTemplate, it overrides the TemplateBinding normally used to pull in the BorderBrush set on the control's Style. The most direct fix would be to actually copy the default ControlTemplate into your custom control's default Style and use a simple Border with customized triggers there instead of just in the containing Style.

<Style TargetType="{x:Type local:ValidatorTextBox}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:ValidatorTextBox}">
                <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true" >
                    <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsValid" Value="False">
                        <Setter Property="Background" TargetName="Bd" Value="Red"/>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
John Bowen
Great, that was exactly what I needed, thank you very much.
Travis