Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bind a property to DataTemplateSelector

Tags:

wpf

xaml

I want to design a DataTemplateSelector who compare the given value with a one passed in parameter and choose the right template if the value is superior or inferior

I came with the following :

class InferiorSuperiorTemplateSelector : DataTemplateSelector
{
    public DataTemplate SuperiorTemplate { get; set; }
    public DataTemplate InferiorTemplate { get; set; }

    public double ValueToCompare { get; set; }

    public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
    {
        double dpoint = Convert.ToDouble(item);
        return (dpoint >= ValueToCompare || dpoint == null) ? SuperiorTemplate : InferiorTemplate;
    }
}

and the XAML :

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition Height="30" />
        <RowDefinition Height="30" />
    </Grid.RowDefinitions>

    <TextBox Name="theValue" Grid.Row="0">1</TextBox>
    <ContentControl Grid.Row="2" Content="{Binding ElementName=theValue, Path=Text}" >
        <ContentControl.ContentTemplateSelector>
            <sel:InferiorSuperiorTemplateSelector ValueToCompare="12" SuperiorTemplate="{StaticResource posTemplate}" InferiorTemplate="{StaticResource negTemplate}" />
        </ContentControl.ContentTemplateSelector>
    </ContentControl>
</Grid>

This works pretty fine if valueToCompare parameter is set manually (here with 12). When I try to make this one dynamic, by applying a binding I got the following error :

A 'Binding' cannot be set on the 'ValueToCompare' property of type 'InferiorSuperiorTemplateSelector'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.

And here comes the problem : how can we declare a DependencyProperty in a DataTemplateSelector or is there any other option to acheve this goal ? I tried to define a dependencyproperty using the usual way but I can't resole the SetValue and GetValue methods.

Thanks by advance.

EDIT : As an appendix of the solution mentionned above, here is the fixed XAML code of my sample.

    <TextBox Name="theValue" Grid.Row="0">1</TextBox>
    <TextBox Name="theValueToCompare" Grid.Row="1">50</TextBox>

    <ContentControl Grid.Row="2" Content="{Binding ElementName=theValue, Path=Text}"
      local:DataTemplateParameters.ValueToCompare="{Binding ElementName=theValueToCompare, Path=Text}">
        <ContentControl.ContentTemplateSelector>
            <local:InferiorSuperiorTemplateSelector SuperiorTemplate="{StaticResource posTemplate}" InferiorTemplate="{StaticResource negTemplate}" />
        </ContentControl.ContentTemplateSelector>
    </ContentControl>

The other parts of the code are similar.

like image 512
eka808 Avatar asked Feb 25 '14 10:02

eka808


1 Answers

As evident from the error you can only bind with dependency property. But since it's already inheriting from DataTemplateSelector, you cannot inherit from DependencyObject class.

So, I would suggest to create an Attached property for binding purpose. But catch is attached property can only be applied on class deriving from DependencyObject.

So, you need to tweak a bit to get it working for you. Let me explain step by step.


First - Create attached property as suggested above:

public class DataTemplateParameters : DependencyObject
{
    public static double GetValueToCompare(DependencyObject obj)
    {
        return (double)obj.GetValue(ValueToCompareProperty);
    }

    public static void SetValueToCompare(DependencyObject obj, double value)
    {
        obj.SetValue(ValueToCompareProperty, value);
    }

    public static readonly DependencyProperty ValueToCompareProperty =
        DependencyProperty.RegisterAttached("ValueToCompare", typeof(double),
                                              typeof(DataTemplateParameters));

}

Second - Like I said it can be set only on object deriving from DependencyObject, so set it on ContentControl:

<ContentControl Grid.Row="2" Content="{Binding Path=PropertyName}"
          local:DataTemplateParameters.ValueToCompare="{Binding DecimalValue}">
   <ContentControl.ContentTemplateSelector>
      <local:InferiorSuperiorTemplateSelector
           SuperiorTemplate="{StaticResource SuperiorTemplate}"
           InferiorTemplate="{StaticResource InferiorTemplate}" />
   </ContentControl.ContentTemplateSelector>
</ContentControl>

Third. - Now you can get the value inside template from container object passed as parameter. Get Parent (ContentControl) using VisualTreeHelper and get value of attached property from it.

public override System.Windows.DataTemplate SelectTemplate(object item, 
                                      System.Windows.DependencyObject container)
{
   double dpoint = Convert.ToDouble(item);
   double valueToCompare = (double)VisualTreeHelper.GetParent(container)
             .GetValue(DataTemplateParameters.ValueToCompareProperty); // HERE
   // double valueToCompare = (container as FrameworkElement).TemplatedParent; 
   return (dpoint >= valueToCompare) ? SuperiorTemplate : InferiorTemplate;
}

Also you can get ContentControl like this (container as FrameworkElement).TemplatedParent.

like image 88
Rohit Vats Avatar answered Nov 17 '22 23:11

Rohit Vats