将属性绑定到 DataTemplateSelector

10

我希望设计一个DataTemplateSelector,该选择器将给定值与传递的参数进行比较,如果该值大于或小于参数,则选择正确的模板。

我提出了以下方案:

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;
    }
}

以及 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>

如果手动设置valueToCompare参数(例如这里设置为12),则它可以很好地工作。 但是,当我尝试通过应用绑定来使其动态化时,我遇到了以下错误:

无法在类型为“InferiorSuperiorTemplateSelector”的“ValueToCompare”属性上设置“Binding”。仅可以在DependencyObject的DependencyProperty上设置“Binding”。

这里出现了问题:我们如何在DataTemplateSelector中声明DependencyProperty或是否有其他选项可以实现此目标? 我尝试使用通常的方法定义dependencyproperty,但我无法解决SetValue和GetValue方法。

提前致谢。

编辑:作为上述解决方案的附录,这是我的示例修复后的XAML代码。

    <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>

代码的其他部分类似。


你的“通常方式”是什么?在使用SetValue和GetValue时遇到了什么问题?数据绑定在幕后完成,因此您只需要使用这些方法从代码中获取和设置值。考虑到它只是一个双精度浮点数,我不明白为什么会有问题。 - Craig Graham
通常的方式是,对我来说,类似于这样: public Boolean State { get { return (Boolean)this.GetValue(StateProperty); } set { this.SetValue(StateProperty, value); } } public static readonly DependencyProperty StateProperty = DependencyProperty.Register( "State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false));根据Aron的说法,这需要继承自DependencyObject,而这是不可能的。那么,有没有什么解决办法呢? - eka808
@AnatoliyNikolaev 很高兴听到这个消息! - eka808
@eka808 - 请检查已发布的答案是否适用于您。 - Rohit Vats
不客气,eka808。很高兴它起作用了! :) - Rohit Vats
显示剩余3条评论
1个回答

19
从错误信息可以看出,你只能绑定依赖属性。但是由于它已经继承了 DataTemplateSelector,所以无法继承 DependencyObject 类。
因此,我建议为绑定目的创建一个 附加属性。但是需要注意的是,附加属性只能应用于从 DependencyObject 派生的类。
因此,你需要进行一些调整才能让它对你起作用。让我一步一步地解释。
第一步 - 如上所述创建附加属性:
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));

}

第二步 - 正如我所说,它只能在从DependencyObject派生的对象上设置,因此在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>

第三步 - 现在你可以从传递的容器对象中获取模板内的值。使用VisualTreeHelper获取父级(ContentControl),然后从中获取附加属性的值。

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;
}

你也可以通过这种方式获取 ContentControl:(container as FrameworkElement).TemplatedParent


1
+1:你比我先完成了 :),对我来说一切都正常工作。 - Anatoliy Nikolaev
@Anatoily - 很想听听你的方法。你是想着同样的方向还是有其他的方法? - Rohit Vats
我已经使用了相同的方式。唯一的问题是,在控件没有完全加载时,尝试在PropertyChanged处理程序中获取附加依赖属性的值 - 经常犯这样的错误。 - Anatoliy Nikolaev
1
我意识到我的问题所在。在这种情况下,容器的父级为 nullContentControl contentControl = container.Parent as ContentControl;。但在您的情况下,它找到了:ContentControl contentControl = VisualTreeHelper.GetParent(container) as ContentControl; - Anatoliy Nikolaev
1
是的,Parent 查找 LogicalParent,而 VisualTreeHelper 则查找 VisualParent - Rohit Vats
1
@Anatoliy - 谢谢..!! 作为回答,我还添加了另一种使用TemplatedParent获取父级的方法,它确实可以获取VisualParent - Rohit Vats

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接