【问题标题】:Collapse ContentControl if Content is Collapsed如果内容已折叠,则折叠 ContentControl
【发布时间】:2012-09-24 06:47:26
【问题描述】:

我有一个 ContentControl,它的样式包含边框和其他视觉装饰。我希望这些装饰在内容折叠时消失,所以我想在这种情况下我必须将 ContentControl 的可见性设置为折叠。我的 ContentControl 装饰采用了这种风格:

<Style x:Key="DecoratedItem1" TargetType="{x:Type c:DecoratedItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type c:DecoratedItem}">
                <StackPanel Orientation="Horizontal">
                    <Border BorderBrush="Black" BorderThickness="2" CornerRadius="2">
                        <StackPanel Orientation="Horizontal">
                            <Image Source="/Images/file.png"/>
                            <ContentPresenter Name="wContent"/>
                        </StackPanel>
                    </Border>
                </StackPanel>
                <ControlTemplate.Triggers>
                    <DataTrigger Binding="{Binding ElementName=wContent, Path=Content.Visibility}" Value="Collapsed">
                        <DataTrigger.Setters>
                            <Setter Property="Visibility" Value="Collapsed"/>
                        </DataTrigger.Setters>
                    </DataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

DecoratedItem 类只是 ContentControl 的子类,带有与此问题无关的附加 DependencyProperties,我只是想指出,我已经有一个子类,如果需要,我可以添加代码。

这在 ContentControl 的内容是 UIElement 时有效,但是如果内容是由 DataTemplate 生成的,它会抱怨无法找到 Visibility 属性。

<!-- works -->
<c:DecoratedItem Style="{StaticResource DecoratedItem1}">
    <TextBlock Text="ABC" Visibility="Collapsed"/>
</c:DecoratedItem>

<!-- doesn't work -->
<c:DecoratedItem Style="{StaticResource DecoratedItem1}" Content="ABC">
    <c:DecoratedItem.Resources>
        <DataTemplate DataType="{x:Type clr:String}">
            <TextBlock Text="{Binding}" Visibility="Collapsed"/>
        </DataTemplate>
    </c:DecoratedItem.Resources>
</c:DecoratedItem>

调试输出窗口中显示的第二种情况的错误是:

System.Windows.Data Error: 40 : BindingExpression path error:
'Visibility' property not found on 'object' ''String' (HashCode=-885832486)'.
BindingExpression:Path=Content.Visibility;
DataItem='ContentPresenter' (Name='wContent');
target element is 'DecoratedItem' (Name='');
target property is 'NoTarget' (type 'Object')

我理解为什么会发生这种情况,但不知道如何修复我的风格以使其按我的意愿工作。如有必要,我不介意将帮助代码添加到 DecoratedItem 子类。知道如何解决这个问题吗?

[编辑 1]

关于建议的答案的更多解释:

我无法强制要求内容始终是 UIElement。毕竟这是一个模型视图设计,当然我简化了很多例子。在实际项目中,内容是从 DataContext 中选择的一个模型,它可以是几种不同的类型,DataTemplate 为该模型构建一个表示。一些 DataTemplates 决定(取决于模型状态)没有任何东西可以呈现并将 Visibility 切换为 Collapsed。我想将该信息传播到装饰容器。上面的例子真的只是提出了问题而不是动机,对不起。

[编辑 2]

不确定了解更多有关该模型的信息会如何解决问题,但我们开始吧。 Content 字段中的数据没有太多共同点,因为它可以是很多东西,这个 DecoratedItem 应该是可重用的,以便为某些表单上显示的项目提供通用的视觉样式。内容可以是工作项之类的东西,如果它们被禁用,则 DataTemplate 会折叠它们;其他类型的内容可能不完整并被折叠。当然,其他种类永远不会崩溃。

但请注意,数据模型与问题并没有太大关系,这仍然是如何绑定扩展内容元素的可见性(在可能以可绑定方式通过子类公开它之后)。

【问题讨论】:

    标签: wpf xaml binding datatemplate


    【解决方案1】:

    有几种方法可以描述问题所在。在第一个工作示例中:

    <c:DecoratedItem Style="{StaticResource DecoratedItem1}">
        <TextBlock Text="ABC" Visibility="Collapsed"/>
    </c:DecoratedItem>
    

    ContentControl 的 Content 属性设置为 TextBlock,它是具有 Visibility 属性的 UIElement。 (这假设您没有将派生类 DecoratedItem 的 ContentPropertyAttribute 更改为 Content 以外的内容)。因此,您的 DataTrigger 绑定可以正确评估:

     <DataTrigger Binding="{Binding ElementName=wContent, Path=Content.Visibility}" Value="Collapsed">
    

    将工作案例与失败案例进行对比:

    <c:DecoratedItem Style="{StaticResource DecoratedItem1}" Content="ABC">
    

    其中 Content 属性设置为不具有 Visibility 属性的 String 实例。

    另一种描述错误的方法是注意,即使您为 Content 是 String 的情况提供 DataTemplate,Content 仍然是 String 并且仍然没有 Visibility 属性。换句话说,Content 是由 DataTemplate生成 的说法是不正确的——您的 DataTemplate 只是告诉控件如何显示 String 类型的 Content。

    您的问题的一般答案是,如果您希望 DecoratedItem1 中的 DataTrigger 与 Content.Visibility 的路径绑定,您需要确保放入其中的内容始终是 UIElement。相反,如果您希望能够将任何类型的内容放入控件中,则需要触发其他内容。

    严格来说,您的问题的具体答案取决于您更广泛的意图(特别是如何设置/修改控件内容的可见性)。几种可能性:

    • 如果您确实想要表单“Content.Visibility”的 DataTrigger 绑定,请确保 Content 始终是 UIElement。例如,使用样式的工作形式,然后将 TextBlock 的 Text 绑定到适当的东西。但是,这不太适合将派生控件作为 ContentControl 的想法,所以...

    • 您的 DataTrigger 可能会绑定到其他东西。从问题的形成方式来看,似乎还有一些其他属性或代码隐藏将控制各种内容实体是否可见。

    • 最后,您可以向 TextBlock 添加一个额外的 DataTrigger。此 DataTrigger 将根据其自身的可见性设置其父级的可见性。然后,将 DecoratedItem1 样式中的 DataTrigger 与路径“Visibility”而不是“Content.Visibility”绑定,本质上是手动将 Visibility 链接在一起。

    编辑

    根据您对如何使用它的描述,听起来您需要考虑visual tree。您可以将 DecoratedItem 控件扩充为具有以下功能:如果其所有作为 UIElments 的可视子项都具有 Collapsed 的可见性(或者如果它没有可视子项),那么它也是 Collapsed(或者,对于所需功能有意义的任何逻辑)就其视觉子项的可见性而言)。您需要使用代码中的VisualTreeHelper 类——特别是GetChildrenCount 和GetChild 方法。您还可以在 DecoratedItem 类中覆盖 OnVisualChildrenChanged(同时仍调用基类方法),以便您可以获取可见子项的 UIElement.IsVisibleChanged 事件。

    【讨论】:

    • 这是对问题的一个很好的解释,但这不是问题的重点,如果不够清楚,抱歉。问题是如何解决这个问题,或者如何获得内容的扩展 UIElement。我无法绑定到其他东西,因为我有多个 DataTemplates,这取决于 DataContext(毕竟这是一个模型视图设计)。内容的可见性是从一些 DataTemplates 中设置的。我不知何故需要将该信息传播到装饰容器。
    • 所以对于您的建议:1) 不可能,因为我使用的是模型视图设计并且需要 DataTemplate 来扩展我的模型。 2) 当我有多个 DataTemplate 来支持不同的模型类型时,我不知道该怎么做。事实上,我选择了 Content-Visibility 作为绑定的“共同”点,因为我的模型是如此不同。 3)不确定我是否理解那个,需要更多考虑。
    • 如果我做对了,(3) 将设置 ContentPresenter 的可见性,然后我可以绑定到它而不是扩展内容。应该可以,我试试看。
    • 重点是你仍然必须有 一些 东西可以触发 DataTrigger。从您的编辑中:“一些 DataTemplates 决定(取决于模型状态)没有任何东西可以呈现并将 Visibility 切换为 Collapsed。”你能在这上面花钱吗?
    • 这里有更多细节。如果可以将其排除在外,数据模型仍然不是问题的一部分;)-无论如何,如果可行,我将接受建议#3之类的解决方法并且找不到更好的答案(仍然需要测试不过,明天再做,今天没时间,抱歉)。
    猜你喜欢
    • 2011-08-10
    • 1970-01-01
    • 2016-02-12
    • 2012-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-25
    • 2014-03-10
    相关资源
    最近更新 更多