Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
857 views
in Technique[技术] by (71.8m points)

wpf - Bind a property to DataTemplateSelector

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...