ListView 内的 UserControl 数据绑定失败

UserControl databinding inside ListView fails

我有一个自定义控件,这里简化一下:

<UserControl x:Class="WPF.TestControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WPF"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
    </Grid>
</UserControl>

使用情况:

<local:TestControl Grid.Row="0">
    <Border BorderBrush="Red" BorderThickness="1">
        <TextBlock Text="{Binding Path=Message, Mode=OneWay}"/>
    </Border>
</local:TestControl>

目前一切顺利,有效。

但是,当用作 ListView 的 DataTemplate 的一部分时,绑定不再有效:

<ListView ItemsSource="{Binding Path=Items}" Grid.Row="2">
    <ListView.Resources>
        <DataTemplate DataType="{x:Type local:TestViewModel}">
            <local:TestControl>
                <TextBlock Text="{Binding Path=Message, Mode=OneWay}"/>
            </local:TestControl>
        </DataTemplate>
    </ListView.Resources>
</ListView>

需要什么咒语才能使上面的数据绑定工作?

您正在创建一个无限循环。
UserControl 已经是一个 ContentControl,因为它派生自它。所以只要你不覆盖它的ControlTemplate,你就不应该向它添加一个ContentPresenter,它在模板中使用时绑定到TemplatedParent

这种痛苦的导火索是 TemplatedParent 绑定。 TestControl 是模板的一部分。该模板应用于 ListViewItem,因此它是 TestControl 的模板化父级。

现在TestControl里面的ContentPresenter的绑定源的值是ListViewItem.Content属性的值,它持有TestControl本身。通过这种方式,您可以将模板化父 ListViewItem.Content 添加到 TestControlContentPresenter,后者现在包含相同的 TestControl(本身),它将模板化父 ListViewItem.Content(即 TestControl)绑定到 TestControlContentPresenter,后者现在包含相同的 TestControl(自身),它绑定模板化的父... Whosebug。

如果你想像 ContentControl 一样使用 UserControl 即显示其他 "external" 控件的视觉效果,那么像 ContentCobtrol 一样使用它是:

测试控制

<UserControl x:Class="TestControl">
</UserControl>

数据模板

<DataTemplate DataType="{x:Type local:TestViewModel}">
  <local:TestControl>
    <TextBlock Text="{Binding Path=Message, Mode=OneWay}"/>
  </local:TestControl>
</DataTemplate>

或者如果内容比简单的演示更复杂和高级,您应该覆盖 ControlTemplate:

测试控制

<UserControl x:Class="TestControl">
  <UserControl.Template>
    <ControlTemplate TargetType="local:TreeIndex">
      <Border>
        <Grid>
          ...
          <ContentPresenter Content="{TemplateBinding Content}" />
        </Grid>
      </Border>
    </ControlTemplate>
  </UserControl.Template>
</UserControl>

数据模板

<DataTemplate DataType="{x:Type local:TestViewModel}">
  <local:TestControl>
    <TextBlock Text="{Binding Path=Message, Mode=OneWay}"/>
  </local:TestControl>
</DataTemplate>