我有一个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显示,并且任何值的编辑都将应用于绑定项。
由于某些原因,我的问题中格式化的代码块在第一行之后缩进了很多。我试着修理它,但我不明白发生了什么。答案 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;
}
....