WPF - 无法将ContentPresenter中的控件属性绑定到父控件

时间:2012-10-21 20:35:17

标签: c# wpf binding contentcontrol contentpresenter

我有一个在表单中使用的自定义控件,此控件组合了标签和文本框。当表单处于只读模式时,控件将更改为显示标签而不是文本框。 为此,我为我的控件定义了一个内容模板,如下所示:

<Style TargetType="{x:Type Controls:FormFieldControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="200"/>
                        <ColumnDefinition Width="200"/>
                    </Grid.ColumnDefinitions>

                    <AdornerDecorator Grid.Column="1"
                                      Visibility="{Binding RelativeSource={RelativeSource AncestorType=Controls:FormFieldControl}, 
                                                   Path=IsReadOnly, 
                                                   Converter={StaticResource BooleanToInverseVisibilityConverter}, 
                                                   Mode=OneWay}">
                        <ContentPresenter Name="ContentPresenter" />
                    </AdornerDecorator>

                    <AdornerDecorator Grid.Column="1"
                                      Visibility="{Binding RelativeSource={RelativeSource AncestorType=FormFieldControl}, 
                                      Path=IsReadOnly, Converter={StaticResource BooleanToVisibilityConverter}, 
                                      Mode=OneWay}">
                        <ContentPresenter Name="ReadOnlyContentPresenter" />
                    </AdornerDecorator>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

ReadOnlyContentPresenter的内容是一个已应用以下风格的标签:

<Style x:Key="ReadOnlyFormFieldControl" TargetType="{x:Type Label}">
    <Setter Property="Width" Value="400" />
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Controls:FormFieldControl}, 
                               Path=ReadOnlyWidth, 
                               Converter={StaticResource IsSetConverter}}"
                     Value="True">
            <Setter Property="Width" 
                    Value="{Binding RelativeSource={RelativeSource AncestorType=Controls:FormFieldControl}, Path=ReadOnlyWidth}" />
        </DataTrigger>
    </Style.Triggers>
</Style>

这适用于大约80个控件,但是当我向表单添加更多控件时,标签样式中的绑定无法找到源(错误4):

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='MyProject.Controls.FormFieldControl', AncestorLevel='1''. BindingExpression:Path=ReadOnlyWidth; DataItem=null; target element is 'Label' (Name=''); target property is 'ReadOnlyWidth' (type 'Double?')

应用于控件的可编辑部分的样式也会发生同样的情况。我认为这个问题与正在处理的控件数量有关,如果我移动控制代码没有。 81(失败)在控制号之上。 80(工作),控制中的绑定没有。 81现在有效,但对照没有。 80失败了。我为几个控件做了这个,行为是一致的。

我仍然不清楚AdornerDecorator如何正常工作,而在调查中我发现这个question提到了一个问题,即在一个adornerlayer中有超过144个装饰者,所以我删除了AdornerDecorator在我的代码中只发现正在发生的绑定问题控制没有。 81及以上现在发生在我FormFieldControl的所有人身上。

最后,我决定在我的控件模板中尝试ContentControl而不是ContentPresenter。这适用于表单中的所有控件。我知道ContentPresenterContentControl的轻量级形式,并且它比ContentControl更适合在内容模板中使用。但是,我不太明白为什么绑定在使用ContentPresenter时失败并且在使用ContentControl时工作。

我也觉得很奇怪,只有一组有限的控件可以使用,而且当不使用AdornerDecorator时,它们都不会找到绑定源。

有没有人遇到类似的东西?我ContentPresenter有什么问题吗?或者,如果这是预期的行为,有人可以帮我理解发生了什么吗?

任何想法都表示赞赏。

2 个答案:

答案 0 :(得分:0)

从您的问题不清楚标签是否只显示文本框中的内容,或者是否必须显示自定义标签,但如果是前者,那么我认为您应该使用已提及的IsReadOnly属性。

您可以定义一个自定义ControlTemplate,当TextBox被读取时,它会隐藏Label的背景和边框,使其看起来好像只是<StackPanel> <StackPanel.Resources> <Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBoxBase}"> <Setter Property="SnapsToDevicePixels" Value="True" /> <Setter Property="OverridesDefaultStyle" Value="True" /> <Setter Property="KeyboardNavigation.TabNavigation" Value="None" /> <Setter Property="FocusVisualStyle" Value="{x:Null}" /> <Setter Property="MinWidth" Value="120" /> <Setter Property="MinHeight" Value="20" /> <Setter Property="AllowDrop" Value="true" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TextBoxBase}"> <Border Name="Border" CornerRadius="2" Padding="2" Background="AliceBlue" BorderBrush="Black" BorderThickness="1"> <ScrollViewer Margin="0" x:Name="PART_ContentHost" /> </Border> <ControlTemplate.Triggers> <Trigger Property="IsReadOnly" Value="True"> <Setter TargetName="Border" Property="Background" Value="Transparent" /> <Setter TargetName="Border" Property="BorderBrush" Value="Transparent" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </StackPanel.Resources> <TextBox Margin="50,25" Name="tb" /> <Button Content="Switch modes" Margin="50,25" Click="Button_Click" /> </StackPanel>

试试这个简单的例子:

private void Button_Click(object sender, RoutedEventArgs e)
{
    tb.IsReadOnly = !tb.IsReadOnly;
}

您需要以下按钮单击处理程序才能切换只读模式:

TextBox

以下是TextBox可编辑时的结果:

enter image description here

这就是{{1}}在只读时的样子。

enter image description here

答案 1 :(得分:0)

我认为原因可能就是这个:

var contentPresenter = new ContentPresenter { Content = new Button() };
var contentControl = new ContentControl { Content = new Button() };
if ((contentPresenter.Content as FrameworkElement).Parent == null)
    Debug.WriteLine("ContentPresenter won't let you get ancestors");
if ((contentControl.Content as FrameworkElement).Parent != null)
    Debug.WriteLine("ContentControl will let you get ancestors");

显示两行,这意味着contentPresenter是罕见的FrameworkElement之一,没有正确设置&#34; Parent&#34;属性。