0

I am trying to make cutom validator controls but dont know why the Message Property that i have on the ValidationRules that i wanted to bind to the templated parent. It works runs but everytime its empty. Dont know why its empty everytime.

The Sample project can be found here

STYLE

   <Style TargetType="{x:Type local:RequiredFieldBox}">
    <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="{x:Type local:RequiredFieldBox}">
                <StackPanel Orientation="Vertical">

                    <TextBox>
                        <Binding  RelativeSource="{RelativeSource TemplatedParent}" Path="Text">
                        <Binding.ValidationRules>
                            <rule:RequiredFieldRule>
                                <rule:RequiredFieldRule.Params>
                                    <rule:ValidationParams 
                                        Message="{TemplateBinding Msg}"
                                        ValidationType="{TemplateBinding Type}"/>
                                </rule:RequiredFieldRule.Params>
                            </rule:RequiredFieldRule> 
                        </Binding.ValidationRules>
                    </Binding>
                        <TextBox.Style>
                    <Style TargetType="{x:Type TextBox}">
                      <Style.Triggers>
                           <Trigger Property="Validation.HasError" Value="true" >
                           <Setter Property="Foreground" Value="Red"/>
                           <Setter Property="Background" Value="MistyRose"/>
                           <Setter Property="BorderBrush" Value="Red"/>
                           <Setter Property="BorderThickness" Value="1.0"/>
                            <Setter Property="VerticalContentAlignment" Value="Center"/>
                           </Trigger>
                       </Style.Triggers>
                      <Setter Property="Validation.ErrorTemplate">
                         <Setter.Value>
                            <ControlTemplate>
                               <StackPanel>
                                  <StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="40">
                                    <AdornedElementPlaceholder x:Name="Holder"/>
                                    <Image Height="{Binding Height, ElementName=Holder}" Width="20" Margin="10,0"
                                        Source="/Images/restricted.png" ToolTip="{Binding ElementName=Holder, 
                                        Path=AdornedElement.(Validation.Errors)
                     [0].ErrorContent}"/>
                                 </StackPanel>
                               </StackPanel>
                            </ControlTemplate>
                         </Setter.Value>
                    </Setter>
                 </Style>
             </TextBox.Style>
          </TextBox>
         </StackPanel>
        </ControlTemplate>
   </Setter.Value>
</Setter>

VALIDATION PARAMS CLASS

public class ValidationParams : DependencyObject
{

    public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
      "Message",
      typeof(string),
      typeof(ValidationParams),
      new FrameworkPropertyMetadata(null));
    // Dependency Properties

    public static readonly DependencyProperty ValidationTypeProperty = DependencyProperty.Register("ValidationType",
                                                                                          typeof(RequiredFieldRule.EnmValidationType),
                                                                                          typeof(ValidationParams),
                                                                                          new FrameworkPropertyMetadata(RequiredFieldRule.EnmValidationType.FieldNotEmpty));
    public static readonly DependencyProperty FieldNameProperty = DependencyProperty.Register("FieldName",
                                                                                         typeof(string),
                                                                                         typeof(ValidationParams),
                                                                                         new FrameworkPropertyMetadata(string.Empty));
    public string FieldName
    {
        get { return (string)GetValue(FieldNameProperty); }
        set { SetValue(FieldNameProperty, value); }
    }
    // Properties
    public string Message
    {
        get { return (string)GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }

    [Category("ValidationType")]
    public RequiredFieldRule.EnmValidationType ValidationType
    {
        get { return (RequiredFieldRule.EnmValidationType)GetValue(ValidationTypeProperty); }
        set { SetValue(ValidationTypeProperty, value); }

    }
}

CustomControl Class

public class RequiredFieldBox : Control
{

    static RequiredFieldBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(RequiredFieldBox), new FrameworkPropertyMetadata(typeof(RequiredFieldBox)));
    }
    public static readonly DependencyProperty MsgProperty = DependencyProperty.Register(
     "Msg",
     typeof(string),
     typeof(RequiredFieldBox),
     new FrameworkPropertyMetadata(null)
   );
    public enum EnmValidationType
    {
        FieldNotEmpty,
        FieldNumeric,
        FieldIPAddress
    }
    public static readonly DependencyProperty TypeProperty = DependencyProperty.Register("Type",
                                                                                         typeof(RequiredFieldBox.EnmValidationType),
                                                                                         typeof(RequiredFieldBox),
                                                                                         new FrameworkPropertyMetadata(RequiredFieldBox.EnmValidationType.FieldNotEmpty));
    public EnmValidationType Type
    {
        get { return (EnmValidationType) GetValue(TypeProperty); }
        set { SetValue(TypeProperty, value);}
    }
    private static void MessageChanaged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        RequiredFieldBox sh = (RequiredFieldBox)d;
        if (sh.Msg != (string)e.OldValue)
            sh.Msg = (string)e.NewValue;
    }
    public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
    "Text",                               //Property name
    typeof( object ),                           //Property type
    typeof( RequiredFieldBox ));

    public string Msg
    {
        get { return (string)GetValue(MsgProperty); }
        set { SetValue(MsgProperty, value); }
    }

    public object Text
    {
        get { return GetValue(TextProperty);}
        set { SetValue(TextProperty, value); }
    }
}

Validation Rule Class

public class RequiredFieldRule : ValidationRule {

    public enum EnmValidationType
    {
        FieldNotEmpty,
        FieldNumeric,
        FieldIPAddress
    }

    // Local variables and objects
    private ValidationParams mParams = new ValidationParams();

    public ValidationParams Params
    {
        get { return mParams; }
        set { mParams = value;}
    }

    // Override
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        ValidationResult objResult = null;
        string sValue = value as string;
        objResult = new ValidationResult(true, null);
        switch (Params.ValidationType)
        {
            case EnmValidationType.FieldNotEmpty:
                if (string.IsNullOrEmpty(sValue) == true)
                    return new ValidationResult(false, Params.Message);
                break;
            case EnmValidationType.FieldNumeric:
                int iValue = 0;
                if (int.TryParse(sValue, out iValue) == false)
                    objResult = new ValidationResult(false, Params.Message);
                break;
            case EnmValidationType.FieldIPAddress:
                break;
        }
        return objResult;
    }

}
Nivid Dholakia
  • 5,272
  • 4
  • 30
  • 55

1 Answers1

1

In a validation rule there is no context or tree connection, as a template binding is roughly equivalent to a RelativeSource binding it will not work. You might be out of luck...

Usually the only thing that works is Source in combination with StaticResource, x:Reference or x:Static, as you cannot refer to the templated parent via name you maybe can tunnel it through another control, e.g. something like:

<TextBox x:Name="RequiredFieldBox"
         Tag="{Binding RelativeSource={RelativeSource TemplatedParent}}">
    <TextBox.Resources>
        <rule:RequiredFieldRule x:Key="rule">
            <rule:RequiredFieldRule.Params>
                <rule:ValidationParams 
                    Message="{Binding Tag.Msg, Source={x:Reference RequiredFieldBox}}"
                    ValidationType="{Binding Tag.Type, Source={x:Reference RequiredFieldBox}}"/>
            </rule:RequiredFieldRule.Params>
        </rule:RequiredFieldRule>
    </TextBox.Resources>
    <!-- ... --->
         <Binding.ValidationRules>
             <StaticResource ResourceKey="rule"/>
         </Binding.ValidationRules>

(If the ValidationRule would be left in place the x:Reference in all likelihood would complain about cyclical dependency)


An alternative using StaticResource and a tunnel element:

<StackPanel Orientation="Vertical">
    <StackPanel.Resources>
        <FrameworkElement x:Key="Tunnel"
                Tag="{Binding RelativeSource={RelativeSource AncestorType=local:RequiredFieldBox}}" />
    </StackPanel.Resources>
    <StaticResource ResourceKey="Tunnel" />
    <TextBox x:Name="RequiredFieldBox">
        <TextBox.Text>
            <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Text">
                <Binding.ValidationRules>
                    <rule:RequiredFieldRule>
                        <rule:RequiredFieldRule.Params>
                            <rule:ValidationParams
                                    Message="{Binding Tag.Msg, Source={StaticResource Tunnel}}"
                                    ValidationType="{Binding Tag.Type, Source={StaticResource Tunnel}}" />
                        </rule:RequiredFieldRule.Params>
                    </rule:RequiredFieldRule>
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
H.B.
  • 166,899
  • 29
  • 327
  • 400
  • Just a thought then why is ValidationType working? and the Message only is not working and this binding throws an exception. Don't know why but unable to create Tag.Type Dependency Property – Nivid Dholakia Jan 16 '12 at 22:07
  • @NividDholakia: `ValidationType` worked with a `TemplateBinding`? That's interesting, you might want to check out any [binding error](http://blogs.msdn.com/b/wpfsldesigner/archive/2010/06/30/debugging-data-bindings-in-a-wpf-or-silverlight-application.aspx) output then, maybe the template binding does work then and something else is amiss. – H.B. Jan 16 '12 at 22:07
  • There is no binding error in the code that i have posted everything is correct. If you want you can download a copy of sample project from the link added. – Nivid Dholakia Jan 16 '12 at 22:12
  • Sorry But the link is up to date now the WpfCustomValidator Project – Nivid Dholakia Jan 16 '12 at 22:16
  • @NividDholakia: The ValidationType is not working either as far as i can see, it's just always on the default value... I added another method to tunnel the needed properties. – H.B. Jan 16 '12 at 23:09
  • Your Alternative method did the work for me. There's only one thing now when i set the default value of validation type to null and set it from the XAML as Enum it will always take the first enum value? Do you know the cause of it? – Nivid Dholakia Jan 17 '12 at 16:19
  • @NividDholakia: No, i don't, i also saw that happening and i am not keen on debugging this for you... – H.B. Jan 17 '12 at 16:22
  • Anyways thanks for the help though. I have found a workaround for that too. Thanks again – Nivid Dholakia Jan 17 '12 at 17:25