Contentpresenter,基于类型的datatemplate选择和绑定

时间:2014-02-03 15:42:13

标签: c# wpf xaml binding datatemplate

我有一个ItemsControl,它绑定到一个项目列表。这些项目具有名称和值属性。 value属性的类型为Object,以允许使用不同的数据类型。要正确显示value属性,我可以为我可能使用的每种数据类型使用带有datatemplate的ContentPresenter。

  <ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>

                <TextBlock Text="{Binding Path=Name}"/>

                <GridSplitter Width="1" 
                              Grid.RowSpan="4" Grid.Column="1" 
                              HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>

                <ContentPresenter Grid.Column="2" Content="{Binding Value}">
                    <ContentPresenter.Resources>
                        <DataTemplate DataType="{x:Type System:String}">
                            <TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}" 
                                     BorderThickness="0"/>
                        </DataTemplate>
                        <DataTemplate DataType="{x:Type System:Int32}">
                            <TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}" 
                                     TextAlignment="Right"
                                     BorderThickness="0"/>
                        </DataTemplate>
                        <DataTemplate DataType="{x:Type System:Double}">
                            <TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}" 
                                     TextAlignment="Right"
                                     BorderThickness="0"/>
                        </DataTemplate>
                        <DataTemplate DataType="{x:Type System:Boolean}">
                            <CheckBox IsChecked="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
                                          HorizontalAlignment="Center"/>
                        </DataTemplate>
                    </ContentPresenter.Resources>
                </ContentPresenter>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

ContentPresenter使用正确的数据类型并且效果很好。我的问题是编辑这些值对绑定项没有任何影响。我怀疑这是因为我绑定到ContentPresenter的content属性而不是直接绑定到Value。我尝试过像这样使用ContentPresenter:

<ContentPresenter Grid.Column="2" Content="{Binding}">
    <ContentPresenter.Resources>
        <DataTemplate DataType="{x:Type System:String}">
            <TextBox Text="{Binding Value}" 
                 BorderThickness="0"/>
        </DataTemplate>

但是这样就没有选择正确的DataTemplate,它只显示了Object而不是String。我还试图忽略DataTemplate绑定中的路径:

 <DataTemplate DataType="{x:Type System:String}">
    <TextBox Text="{Binding}" BorderThickness="0"/>
 </DataTemplate>

有了这个,我得到一个异常,告诉我使用Path或XPath属性。

所以我的问题是:如何正确绑定到Value,以便使用正确的DataTemplate显示,并且任何值的编辑都将应用于绑定项。

由于某些原因,我的问题中格式化的代码块在第一行之后缩进了很多。我试着修理它,但我不明白发生了什么。

3 个答案:

答案 0 :(得分:4)

我认为你会从阅读有关DataTemplate的内容中受益。首先,我建议您长时间阅读MSDN上的Data Templating Overview页面。正如我在评论中提到的那样,您DataTemplate中使用ContentPresenter。从链接页面:

  

您通常使用ContentControl的ControlTemplate中的ContentPresenter来指定要添加内容的位置

似乎缺少的是数据如何以及从内部 DataTemplate绑定到的内容。 DataContext的{​​{1}}将自动设置为DataTemplate属性中指定类型的实例。因此,DataType有权访问的属性也将取决于DataTemplate属性中指定的类型。例如,您无法执行此操作,因为DataType没有string属性。

Value

相反,对于<DataTemplate DataType="{x:Type System:String}"> <TextBox Text="{Binding Value}" BorderThickness="0" /> </DataTemplate> ,您需要将数据绑定到整个string值,如下所示:

DataContext

或者,如果您有一个类名<DataTemplate DataType="{x:Type System:String}"> <TextBox Text="{Binding}" BorderThickness="0" /> </DataTemplate> 且该类具有SomeClass属性,然后,您可以这样做:

Value

现在,因为已经定义了这些<DataTemplate DataType="{x:Type YourDataTypesPrefix:SomeClass}"> <TextBox Text="{Binding Value}" BorderThickness="0"/> </DataTemplate> 而没有设置其DataTemplate值,所以只要框架看到相关类型的对象,框架就会自动呈现每个x:Key的内容(没有其他明确的模板设置)。所以试试这个,如果你还有问题,请告诉我。

答案 1 :(得分:3)

我找到了解决这个问题的方法。绑定不起作用的原因是因为我绑定了ContentControl的内容。对绑定源的双向绑定不符合规定的here。这就是我得到例外的原因。我仍然使用ContentControl和DataTemplates来区分数据类型。但是,不是绑定到ContentControl的内容,而是绑定到ContentControl绑定的值。注意绑定中的Path。

<ContentControl Content="{Binding Value}" Grid.Column="2">
    <ContentControl.Resources>
        <DataTemplate DataType="{x:Type System:String}">
            <TextBox Text="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=DataContext.Value}" BorderThickness="0" />
        </DataTemplate>
        <DataTemplate DataType="{x:Type System:Int32}">
            <TextBox Text="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=DataContext.Value}"
                     TextAlignment="Right"
                     BorderThickness="0"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type System:Double}">
            <TextBox Text="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=DataContext.Value}"
                     TextAlignment="Right"
                     BorderThickness="0"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type System:Boolean}">
            <CheckBox IsChecked="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=DataContext.Value}"
                          HorizontalAlignment="Center"/>
        </DataTemplate>
    </ContentControl.Resources>
</ContentControl>

这是解决问题的方法。我只是觉得使用ContentControl来区分数据类型和不合逻辑的绑定感到有点不舒服。

另外,谢谢你帮我解开这个问题Sheridan。

答案 2 :(得分:3)

我的解决方案已经不舒服了,我也遇到了无法向DataTemplates添加List DataType的问题。我最终使用了一个DataTemplateSelector,它产生了更好的代码。这是:

ContentControl。 DataTemplate应用于的数据的容器:

<ContentControl Grid.Column="2" Content="{Binding}"
                ContentTemplateSelector="{StaticResource propertyItemTemplateSelector}">
</ContentControl>

一些DataTemplates和DataTemplateSelector的声明:

<Style.Resources>
    <local:PropertyTemplateSelector x:Key="propertyItemTemplateSelector"/>
    <DataTemplate x:Key="dtStringValue">
        <TextBox Text="{Binding Path=Value}"
                 BorderThickness="0"
                 IsReadOnly="{Binding Path=IsReadOnly}">
        </TextBox>
    </DataTemplate>

    <DataTemplate x:Key="dtIntegerValue">
        <TextBox Text="{Binding Path=Value}"
                 TextAlignment="Right"
                 BorderThickness="0"
                 IsReadOnly="{Binding Path=IsReadOnly}"/>
    </DataTemplate>
...

DataTemplateSelector的代码:

 public class PropertyTemplateSelector : DataTemplateSelector
 {
    public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
    {
        DataTemplate template = null;

        IPropertyItem propertyItem = item as IPropertyItem;

        if (propertyItem != null)
        {
            FrameworkElement element = container as FrameworkElement;
            if (element != null)
            {
                var value = propertyItem.Value;

                if (value is String)
                {
                    template = element.FindResource("dtStringValue") as DataTemplate;
                }
                else if (value is Int32)
                {
                    template = element.FindResource("dtIntegerValue") as DataTemplate;
                }
                 ....