内容presenter基于类型的DataTemplate选择和绑定

内容presenter基于类型的DataTemplate选择和绑定

问题描述:

我有结合的项目列表一个ItemsControl。这些项目有一个名字和值属性。 value属性的类型是Object,以允许使用不同的数据类型。要显示值属性正确地使用了内容presenter与每个数据类型我可能会使用一个DataTemplate。

I have an ItemsControl that binds to a list of items. These items have a name and value property. The value property is of type Object to allow different datatypes to be used. To display the value property correctly I use a ContentPresenter with a datatemplate for every datatype I might use.

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

内容presenter使用正确的数据类型和伟大工程。我的问题是,在编辑这些值不具有上结合的项目的任何影响。我怀疑这是因为我绑定到内容presenter的内容属性,而不是直接价值。我已经使用Content presenter这样的尝试:

The ContentPresenter uses the correct datatype and works great. My problem is that editing these values does not have any effect on the bound items. I suspect it is because I bind to the content property of the ContentPresenter rather than directly to the Value. I've tried using the ContentPresenter like this:

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

不过这样一来正确的DataTemplate没有选择,它只是显示的对象,而不是如String。我也试着离开了路径结合的DataTemplate中是这样的:

But this way the correct DataTemplate isn't selected and it just displays the Object instead of a String for example. I also tried to leave out the path in the binding of the DataTemplate like this:

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

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

With this I get an exception telling me to use the Path or XPath attribute.

所以我的问题是:我怎么正确地绑定到该值,使其显示用正确的DataTemplate和值的任何编辑应用到绑定项。

So my question is: how do I correctly bind to the Value so it displays with the right DataTemplate and that any editing of the values is applied to the bound item.

顺便说一句出于某种原因,在我的问题缩进得多格式化code块的第一行之后。我试图修复它,但我不明白发生了什么。

Btw for some reason the formatted code blocks in my question indent much more after the first line. I tried fixing it but I don't understand what's happening.

已经在我的解决方案我也遇到了不能够列表数据类型添加到的DataTemplates问题不舒服。最后我用这导致好得多code一DataTemplateSelector。在这里,它是:

Already being uncomfortable with my solution I also ran into the problem of not being able to add a List DataType to the DataTemplates. I ended up using a DataTemplateSelector which resulted in much nicer code. Here it is:

在ContentControl中。

The ContentControl. A container for the data which the DataTemplate is applied over:

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

几的DataTemplates和声明中DataTemplateSelector:

A few DataTemplates and a declaration for the 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>
...

在code为DataTemplateSelector:

The code for the 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;
                }
                 ....