none
Binding ValidationRule's property

    Question

  •  

    I have an object with properties 'StartDate' and 'EndDate'. In my XAML, I have set a custom ValidationRule on the textbox binding to the 'EndDate' where it checks to see if the date falls in the range of 6 months to 2 years of the 'StartDate'. As shown in the xaml below, I have a property 'DateToCheckFrom' in my ValidationRule which needs to be bound to 'MyObject' property 'StartDate'. But doing the following gives me an error saying that I nee d to bind to a dependency property of a dependency Object .

     

    How can I do this in xaml? Will using mode "OneWayToSource' help? If so, how do I use it?

     

     

    <TextBox DataContext="{StaticResource MyObject}" Name="EndDateData" Style="{StaticResource TextStyleInherited}" >

    <TextBox.Text>

    <Binding Path="EndDate" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"

    Converter="{StaticResource CustomDateTimeConverter}" >

    <Binding.ValidationRules>

    <rn:MyDateRangeValidationRule DateToCheckFrom="{Binding Path=StartDate}" />

    </Binding.ValidationRules>

    </Binding>

    </TextBox.Text>

    </TextBox>

     

     

    Thursday, May 03, 2007 8:24 PM

Answers

  • To bind a property it must be a dependency property.  A dependency property can only exist on a subclass of DependencyObject, which ValidationRule is not.  So, you can't bind your DateToCheckFrom property as is.  What you can do, however, is create a simple class which derives from DependencyObject and exposes a dependency property: DateToCheckFrom.  Then hang an instance of that class off your MyDateRangeValidationRule class as a public property.  That will allow you to do the following:

     

    Code Snippet

    <m:MyDateRangeValidationRule>

      <m:MyDateRangeValidationRule.ValidDateRange>

        <m:DateRange DateToCheckFrom="{Binding Path=StartDate}" />

      </m:MyDateRangeValidationRule.ValidDateRange>

    </m:MyDateRangeValidationRule>

     

    In that example, DateRange is the DependencyObject subclass.  ValidDateRange is a normal CLR property of type DateRange, which is a member of the validation rule subclass.

     

    For more info about WPF data binding, read this: http://www.codeproject.com/WPF/GuidedTourWPF_3.asp

     

    HTH

    Thursday, May 03, 2007 9:13 PM
  • Oh, that makes sense.  You need to explicitly set the DateToCheckFrom's Source because the DateCheck object is not in the logical tree.  The DataContext is only inherited by objects in the logical tree, but the DateCheck object returned ValidDateRange is not in the tree (and it doesn't even have a DataContext property!).

     

    Thanks for the kind feedback about my blog.  I'm glad to help out.

     

    BTW - if my posts have answered your question, please mark them as The Answer so that others will know this thread contains useful info.  Thanks!

    Friday, May 04, 2007 5:42 PM

All replies

  • Hmmm... can you try explicitly setting the Source of the Binding on your validation rule to your object? Like so:

     

    Code Snippet

    <rn:MyDateRangeValidationRule DateToCheckFrom="{Binding Source={StaticResource MyObject}, Path=StartDate}" />

     

    I realize you have the DataContext set for the TextBox, but something tells me that's not carrying down to the Binding for some reason.

     

    HTH,
    Drew

    Thursday, May 03, 2007 8:30 PM
  • To bind a property it must be a dependency property.  A dependency property can only exist on a subclass of DependencyObject, which ValidationRule is not.  So, you can't bind your DateToCheckFrom property as is.  What you can do, however, is create a simple class which derives from DependencyObject and exposes a dependency property: DateToCheckFrom.  Then hang an instance of that class off your MyDateRangeValidationRule class as a public property.  That will allow you to do the following:

     

    Code Snippet

    <m:MyDateRangeValidationRule>

      <m:MyDateRangeValidationRule.ValidDateRange>

        <m:DateRange DateToCheckFrom="{Binding Path=StartDate}" />

      </m:MyDateRangeValidationRule.ValidDateRange>

    </m:MyDateRangeValidationRule>

     

    In that example, DateRange is the DependencyObject subclass.  ValidDateRange is a normal CLR property of type DateRange, which is a member of the validation rule subclass.

     

    For more info about WPF data binding, read this: http://www.codeproject.com/WPF/GuidedTourWPF_3.asp

     

    HTH

    Thursday, May 03, 2007 9:13 PM
  • Ahh, duh, right. Good catch Josh.
    Thursday, May 03, 2007 9:43 PM
  •  

    Thank you for your responses.

     

    Josh, I do realize the reason for the error I was getting but did not know a way to work around it. Your solution looks like the way to go. I'll definitely try it out today itself and shall let you know. Thank you so much. And yes, I have read your post in code-project and you have done a good job.

    Friday, May 04, 2007 2:54 PM
  •  

    Josh, I tried your approach and this is what I have

      

    Code Snippet

    namespace ValidationTest

    {

       public class LeaseDateValidationRule : ValidationRule

       {

         private DateTime _minDateTime;

         private DateTime _maxDateTime;

         private DateCheck _dateCheck;

     

        public LeaseDateValidationRule()

        {

          _dateCheck = new DateCheck();

          _minDateTime = _dateCheck.DateToCheckFrom.AddMonths(3);

          _maxDateTime = _dateCheck.DateToCheckFrom.AddYears(2);

        }

     

        public DateCheck ValidDateRange

        {

          get

          {

            return _dateCheck;

          }

          set

          {

    _dateCheck = value;

    _minDateTime = _dateCheck.DateToCheckFrom.AddMonths(3);

    _maxDateTime = _dateCheck.DateToCheckFrom.AddYears(2);

     }

        }

     

        public override ValidationResult Validate(object value, CultureInfo cultureInfo)

        {

          DateTime currentDateTime = new DateTime();

          try

         {

            if (((string)value).Length > 0)

               currentDateTime = DateTime.Parse((String)value);

         }

         catch (Exception e)

         {

             return new ValidationResult(false, "DateTime is not in right format " + e.Message);

         }

         if ((currentDateTime < _minDateTime) || (currentDateTime > _maxDateTime))

         {

             return new ValidationResult(false,

               "Lease should be for a minimum of 3 months or maximum of 2 yrs. Please enter  date in the range: " + _minDateTime.ToShortDateString() + " - " + _maxDateTime.ToShortDateString() + ".");

         }

         else

         {

            return new ValidationResult(true, null);

         }

        }

     }

     

     public class DateCheck : DependencyObject

     {

        public static readonly DependencyProperty _dateToCheckFromProperty = DependencyProperty.Register(

    "DateToCheckFrom", typeof(DateTime), typeof(DateCheck), new PropertyMetadata(DateTime.Now));

     

        public DateTime DateToCheckFrom

        {

           get

           {

              return (DateTime)GetValue(_dateToCheckFromProperty);

            }

           set

           {

              SetValue(_dateToCheckFromProperty, value);

           }

        }

     }

    }

     

     

    Code Snippet

    XAML

     

    <TextBox Name="EndDateData" >

       <TextBox.Text>

           <Binding Path="EndDate" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"

                    Converter="{StaticResource CustomDateTimeConverter}">

               <Binding.ValidationRules>

          <rn:LeaseDateValidationRule.ValidDateRange>
                   <rn:DateCheck DateToCheckFrom="{Binding Path=StartDate}" />

                   </rn:LeaseDateValidationRule.ValidDateRange>

               </Binding.ValidationRules>

            </Binding>

        </TextBox.Text>

    </TextBox>

     

     

    Now I get an error saying that 'LeaseDateValidationRule.ValidDateRange ' does not exist in XML namespace 'clr-namespace:ValidationTest. Am I  missing something?

    • Totally off context .. I have been trying to access your blogs in Infusion but looks like it does not work . How can I read your posts?
    Friday, May 04, 2007 3:41 PM
  • I guess the xaml should look like this:

     

    Code Snippet
    <Binding.ValidationRules>
        <rn:LeaseDateValidationRule>
          <rn:LeaseDateValidationRule.ValidDateRange>     
            <rn:DateCheck DateToCheckFrom="{Binding Path=StartDate}" />      
          <rn:LeaseDateValidationRule.ValidDateRange>
        </rn:LeaseDateValidationRule>
    </Binding.ValidationRules>

     

     

    Infusion deleted my old blog and didn't bother to keep a backup or send me the old content.

     

    Someone was kind enough to send me a big PDF doc with many of my old posts, and I've been slowly reposting them (http://www.codeproject.com/WPF/HiliteListViewItemsInWPF.asp and http://www.codeproject.com/useritems/CustomListBoxLayoutInWPF.asp)

     

    It's a really time consuming task, so I haven't reposted too many of them yet.  Maybe I should just make that PDF doc available on my blog...

    Friday, May 04, 2007 3:57 PM
  •  

    Thanks Josh .. of course my XAML was wrong .. so stupid that I missed it

     

     

    I must also thank you for your posts at http://joshsmithonwpf.wordpress.com/. the posts have helped me so much especially your debugging tips (though I could not get Snoop working in my machine for some reason which I'm still disappointed about), accessing elements from templates/resourceDictionaries, etc, etc, etc. ( I can go on and on). Anyways .. thank you for your great work

    Friday, May 04, 2007 4:32 PM
  •  

    Josh, I'm having another problem here. For some reason the Binding is not working for DateToCheckFrom

    Code Snippet

     <rn:DateCheck DateToCheckFrom="{Binding Path=StartDate}" /> 

     

    eventhough I have set the DataContext at the parent level as shown below

     

    Code Snippet

    <TextBox DataContext="{StaticResource myObject}" Style="{StaticResource TextStyleInherited}" >

    <TextBox.Text>

    <Binding Path="EndDate" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"

    Converter="{StaticResource CustomDateTimeConverter}" >

    <Binding.ValidationRules>

    <rn:LeaseDateValidationRule>

    <rn:LeaseDateValidationRule.ValidDateRange>

    <rn:DateCheck DateToCheckFrom="{Binding Path=StartDate}" />

    </rn:LeaseDateValidationRule.ValidDateRange>

    </rn:LeaseDateValidationRule>

    </Binding.ValidationRules>

    </Binding>

    </TextBox.Text>

    </TextBox>

     

     

    This works if I set the source as part of my binding for  DateToCheckFrom as shown below

    Code Snippet

    TextBox Style="{StaticResource TextStyleInherited}" >

    <TextBox.Text>

    <Binding Path="EndDate" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"

    Converter="{StaticResource CustomDateTimeConverter}" >

    <Binding.ValidationRules>

    <rn:LeaseDateValidationRule>

    <rn:LeaseDateValidationRule.ValidDateRange>

    <rn:DateCheck DateToCheckFrom="{Binding Source={StaticResource myObject},Path=StartDate}" />

    </rn:LeaseDateValidationRule.ValidDateRange>

    </rn:LeaseDateValidationRule>

    </Binding.ValidationRules>

    </Binding>

    </TextBox.Text>

    </TextBox>

     

    Why is it behaving this way?

     

    Friday, May 04, 2007 5:28 PM
  • Oh, that makes sense.  You need to explicitly set the DateToCheckFrom's Source because the DateCheck object is not in the logical tree.  The DataContext is only inherited by objects in the logical tree, but the DateCheck object returned ValidDateRange is not in the tree (and it doesn't even have a DataContext property!).

     

    Thanks for the kind feedback about my blog.  I'm glad to help out.

     

    BTW - if my posts have answered your question, please mark them as The Answer so that others will know this thread contains useful info.  Thanks!

    Friday, May 04, 2007 5:42 PM
  •  

    I'll not be able to provide the source directly since this textbox is part of my datatemplate and this datatemplate is used by my contentControl where I provide the DataContext. Is there a work around for this? Any ideas or am I pretty much stuck with this limitation?

     

    Sure, I'll mark it 'Answer' .. I was just waiting to get everything to work first before marking this as 'Answer'

    Friday, May 04, 2007 5:48 PM
  • Off the top of my head I have two possible workarounds...

     

    1) Create another dependency property on the DateCheck class, perhaps called DataContext, and then bind that to the DataContext of the TextBox (ex. {Binding ElementName="theNameOfTheTextBox", Path="DataContext"} ).  Then set the Source of the DateToCheckFrom Binding to that property (and set the Binding's RelativeSource={xTongue Tiedtatic RelativeSource.Self} ).

     

    2) Set the DateToCheckFrom Binding's ElementName to the name of the TextBox, and the Path to it's DataContext.  Then assign that binding a value converter which takes in the data object and returns the value of property to bind to.

     

    There might be another way, but those should both work.

    Friday, May 04, 2007 6:00 PM
  • Here's an article I just posted which was inspired by this thread: http://www.codeproject.com/useritems/AttachingVirtualBranches.asp
    Sunday, May 06, 2007 8:26 PM
  •  

    Thanks Josh for posting this article. I took a look at it and I think this virtual branching concept will be very helpful in many scenarios. The one scenario where I think this may not help is when you are using a Control on a Page and its template resides in a ResourceDictionary.

     

    For example, I have a ContentControl in a Page and its template is defined in the ResourceDictionary. In this case the ValidationRule is applied inside your template (which is in the ResourceDictionary) but the DataContext is defined at the ContentControl level which resides within the Page. Since ResourceDictionary is a loose XAML,  defining

    <FrameworkElement x:Key="DataContextBridge" /> should happen within your ResourceDictionary which means there will be no way to refer to the DataContext defined within  my Page as a result you cannot build this virtual bridge. Did I understand this right?

     

    - Rajani.

    Monday, May 07, 2007 12:45 PM
  • When all else fails in XAML, there must be a way to do it in code.  Smile

     

    By "loose XAML" I assume you mean a standalone xaml file which is loaded from disc at runtime.  In that case you can use VisualTreeHelper (or perhaps LogicalTreeHelper) to find the first element created by the control template, after it has been applied to the ContentControl.  If you put the DataContextBridge element in that element's Resources, then you should be able to call FindResource on it to get the bridge element.  At that point you can bind to its DataContext and complete the bridge.

     

    I haven't tested this scenario, but I'm sure there must be a way to do it.  When I get some time I'll look into it. 

     

    If you come up with a solution, please post it here, or in a comment on the article's messageboard.   Thanks!

    Monday, May 07, 2007 2:48 PM
  • I think I ran into a similar problem and solved it using code rather than a binding. I would have preferred to do it in XAML, but, based on what you recommended it seems a lot more complicated that way.

     

    I just defined a normal .NET property in the ValidationRule, then I added the ValidationRule in the code behind file.

     

    Binding b = connectionStringTextBox.GetBindingExpression(TextBox.TextProperty).ParentBinding;

    b.ValidationRules.Add(new ConnectionStringValidationRule { DataProviderComboBox = dataProviderComboBox });

     

    It seems like ValidationRule should be derived from DependencyObject.

     

    Thursday, May 01, 2008 8:35 PM
  • Hi, finally have you found an answer (not by code behind) ?

    Regards,

    Mihai

    Tuesday, June 23, 2009 1:59 PM