2

I am having problems with Binding. Since RelativeSource needs the visual tree to travel up and find the desired Ancestor, you are only allowed to use it on an UIElement but I am trying to do a RelativeSource binding on an Non-UIElement, such as is a ValidationRule, which as you all know isnt inside the VisualTree nor its UIElement. As you can expect the binding breaks. RelativeSource couldn't be found because like i said there is no VisualTree or LogicalTree available. I need to make it work though.

Here is an example of XAML:

<StackPanel DataContext{Binding}>
  <Grid>
    <ContentControl Content{Binding MVPart1>
      <TextBox>
       <TextBox.Text>
        <Binding Path="VMPart1Property1">
         <Binding.ValidationRules>
           <my:MyValidationRule>
            <my:ValidationRule.DOC>
             <my:DepObjClass DepProp={Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}}/>
            </my:ValidationRule.DOC>
          </Binding.ValidationRules>
         </Binding>
       </TextBox.Text>
      </TextBox>  
    </ContentControl>
  </Grid>
</StackPanel>

So basically MyValidationRule is derivering from ValidationRule class, but thats not UIElement nor DependencyObject and therefore I had to create a class which derivates from DependencyObject called DepObjClass to be able to write down the xaml binding expression.

Here is code:

public class MyValidationRule : ValidationRule
{
  public DepObjClass DOC
  {
    get;
    set;
  }

  public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
  {
      string text = value as string;
      if (!string.IsNullOrEmpty(text))
      {
         return new ValidationResult(true, string.Empty);
      }

      return new ValidationResult(false, "Not working blahhh");
   }
}

public class DepObjClass : DependencyObject
{
  public object DepProp
  {
    get
    {
      return (object)GetValue(DepPropProperty);
    }
    set
    {
      SetValue(DepPropProperty, value);
    }
  }

  public static DependencyProperty DepPropProperty
     = DependencyProperty.Register(typeof(object), typeof(DepObjClass)......);
}

Now to sum up. MyValidatonRule is not UIElement its not DependencyObject but it has a property of a type that is, hence why the xaml binding expression compiles.

When I run the application the binding itself isnt working because StackPanel couldnt be found because ValidationRule doesnt have VisualTree nor my validation rule participates in Logical or Visual Tree.

The question is how do I make such case work, how to find StackPanel from an Non-UIElement such as my ValidationRule?

I appologize for my code not comipiling but I hope you can understand what I am trying to do. I am giving 50 points to you guys for the right answer.

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
snowy hedgehog
  • 652
  • 1
  • 7
  • 23
  • Maybe something like [this](http://www.codeproject.com/Articles/27432/Artificial-Inheritance-Contexts-in-WPF) can help. – dowhilefor Apr 08 '13 at 15:10
  • What exactly are you trying to achieve? Your `MyValidationRule` class has a property of type `DependencyObject` but doesn't actually make use of it. – Benjamin Gale Apr 08 '13 at 18:53
  • Yea the dependeyobject has a dependencyproperty and i am binding in xaml but binding aint working because on visualtree because the validation itself is not participating in visualtree so how do i make it work – snowy hedgehog Apr 08 '13 at 23:42
  • I meant what problem are you trying to solve? why do you need to have a binding inside the validation rule? – Benjamin Gale Apr 09 '13 at 07:08
  • @dowhilefor there are those tree solution but they seem very "tweak-it-to-make-it-work-somehow". – snowy hedgehog Apr 09 '13 at 07:12
  • @Benjamin I need to bind to get additional information nessecary for validation rule. such as in viewmodel i have values that could be changed and i bind on them, as example text length could be 10 and then changed to 15 and i need to know that – snowy hedgehog Apr 09 '13 at 07:13
  • Just carry out all validation in the viewModel. E.G. check the length of the value in the property setter. – Benjamin Gale Apr 09 '13 at 07:25
  • I need error template, i need binding to viewmodel and i need to show user that he made a mistake.. – snowy hedgehog Apr 09 '13 at 08:04
  • That's why I don't use WPF's ValidationRules to begin with. I don't think that kind of business logic belongs into XAML. – Federico Berasategui Apr 09 '13 at 13:20
  • Well it would be nice if we could use bindable validationrules. I am stuck now with ValidationRules in xaml. Cant change that. There must be solution to have content of validationrule changing dynamically. – snowy hedgehog Apr 09 '13 at 13:34
  • Does [this](http://www.wpfmentor.com/2009/01/how-to-add-binding-to-property-on.html) do anything for you? – Benjamin Gale Apr 09 '13 at 16:54

1 Answers1

3

You can do the following:

  1. Create a helper component which derives from Freezable and defines a DependencyProperty for what you want to bind.

  2. Create a ValidationRule with a property which takes an object of the helper component, similar to what you have done already.

  3. Declare an instance of the helper component in the Resources of an object which can bind to whatever you want to bind. Freezable and its derived classes inherit the binding context (the location in the logical tree) of any control in whose Resources they are declared, so there you can create your binding.

  4. When declaring the ValidationRule, use {StaticResource} to assign the helper component to the property in the ValidationRule. StaticResource works without a binding context, as long as the resource is declared before it is used.

The XAML would look like this:

<StackPanel>
  <StackPanel.Resources>
    <my:Helper x:Key="helper" ValProperty="{Binding}"/>
  </StackPanel.Resources>
  <Grid>
      <TextBox DataContext="{Binding MVPart1}">
       <TextBox.Text>
        <Binding Path="VMPart1Property1">
         <Binding.ValidationRules>
           <my:MyValidationRule Helper="{StaticResource helper}"/>
          </Binding.ValidationRules>
         </Binding>
       </TextBox.Text>
      </TextBox>  
  </Grid>
</StackPanel>
hbarck
  • 2,934
  • 13
  • 16
  • As I said, this is a specialty of Freezable. You are correct when you say that this doesn't work for other classes. But for instance, you can use Binding in a Style, and that is because Style is derived from Freezable. The DataContext needs to be available where the Binding is declared, and Freezables inherit it even when they are declared as resources. When the helper is set to the ValidationRule, the Binding is already in place. I suggest you try it... – hbarck Apr 15 '13 at 20:39
  • I removed my previous comment. This one is better. Explain me how does my:Helper in resources knows about DataContext. There is no DataContext in Resources. The DataContext gets invoked once the StaticResources get resolved which is at initalization of A CONTROL. Objects in Resources dont know about DataContext but once they invoked on a control at some point in run-time they know then. So the question is where does DataContext come from to ValidationRule which is not a FrameworkElement? ValidationRule is not even in VisualTree. – snowy hedgehog Apr 15 '13 at 20:39
  • Styles get invoked on initalization of controls thats how styles become DataContext. Maybe Style aint good example. – snowy hedgehog Apr 15 '13 at 20:40
  • @your second comment: what is wrong in your assumptions is that the DataContext is resolved in the context where the StaticResource is used: When the StaticResource is used, the DataContext is resolved in the context where the resource is declared, and only then the finished result is assigned to whatever property takes the StaticResource – hbarck Apr 15 '13 at 20:44
  • If you say that Binding gets resolved in context where they are located then this example would never work but it does: You have style of a button. Inside the button you have a label. On labe you execute a binding to look for relativesource and find stackpanel. The stackpanel is no in style, its not in resource but its somewhere at the end of the page in xaml part.The style finds the stackpanel once its invoked. Though it would never find it in your case because if we take it the way you explained it,relativesource would be directly the window since the style was defined in window resource. – snowy hedgehog Apr 15 '13 at 20:49
  • 1
    @your third comment: I think here you confuse Style with ControlTemplate, which of course could be set as part of a style, but would create its own visual tree fragment where it is instantiated... Anyway, this discussion leads nowhere. I have used this technique before, so unless you try it and it doesn't work, I have no more to say... – hbarck Apr 15 '13 at 21:08
  • I am not confusing style with contenttemplate i just gave you an example in there you have a relativesource binding to outside. according to what you said the relative parent be the window because style with contenttemplate was defined in window.resource. Here is another example. Take a simple style of a button with a setter.value binding from a property of datacontext to backgroundcolor of button.. once style gets invoked it will apply value of a property from datacontext of the button to buttons backgroundcolor. The datacontext could be an nested object of ViewModel. It could be an POJO. – snowy hedgehog Apr 15 '13 at 21:16
  • I am not like tearing you argument all down but i am rather like just seeking for an explaination why freezable work. If you think your the right guy for 50 points then tell me why instead saying well it just works. Answers like oh somewhere inside happens a bunch of magic are just worth voting as useful. – snowy hedgehog Apr 15 '13 at 21:30
  • I figured it out. Its an internal mechanism called ProvideSelfAsInheritanceContext. It works only for Freezable. You get the 50 points anyways. – snowy hedgehog Apr 15 '13 at 22:10