1

境界線やその他の視覚的な装飾を含むスタイルを持つ 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 クラスは、この問題には関係のない追加の DependencyProperties を持つ ContentControl の単なるサブクラスです。必要に応じてコードを追加できるサブクラスが既にあることに注意してください。

これは、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>

デバッグ出力ウィンドウに表示される 2 番目のケースのエラーは次のとおりです。

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]

提案された回答に関するいくつかの説明:

Content が常に UIElement であることを強制することはできません。結局のところ、これはモデル ビューの設計であり、もちろん、例を大幅に単純化しました。実際のプロジェクトでは、コンテンツは DataContext から選択されたモデルであり、いくつかの異なるタイプにすることができ、DataTemplate はそのモデルのプレゼンテーションを構築します。一部の DataTemplates は (モデルの状態に応じて) 表示するものがないと判断し、Visibility を Collapsed に切り替えます。その情報をデコレーティングコンテナに伝播したいと思います。上記の例は、動機ではなく問題を示しているだけです。申し訳ありません。

[編集2]

モデルについて詳しく知ることが問題にどのように役立つかはわかりませんが、ここで説明します。Content フィールドのデータは、多くのものになる可能性があるため、あまり共通点がありません。この DecoratedItem は、一部のフォームに表示されるアイテムに共通の視覚的スタイルを与えるために再利用可能であると想定されています。コンテンツは、無効になっている場合に DataTemplate によって折りたたまれる作業項目のようなものです。他の種類のコンテンツは不完全で、折りたたまれている可能性があります。もちろん、他の種類は決して崩壊しないかもしれません。

ただし、データ モデルは問題とあまり関係がないことに注意してください。問題は、展開されたコンテンツ要素の可視性に対してバインドする方法です (サブクラスを介してバインド可能な方法で公開した後)。

4

1 に答える 1

0

何が悪いのかを説明する方法はいくつかあります。最初の実用的な例では、次のようになります。

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

ContentControlのContentプロパティは、Visibilityプロパティを持つUIElementであるTextBlockに設定されます。(これは、派生クラスDecoratedItemのContentPropertyAttributeをContent以外のものに変更していないことを前提としています)。したがって、DataTriggerバインディングは以下を正しく評価できます。

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

動作するケースと失敗するケースを比較してください。

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

Contentプロパティが、Visibilityプロパティを持たないStringのインスタンスに設定されています。

何が悪いのかを説明するもう1つの方法は、コンテンツが文字列の場合にDataTemplateを指定しても、コンテンツは文字列であり、Visibilityプロパティを持たないことに注意することです。言い換えると、コンテンツがDataTemplateによって生成されるというステートメントは正しくありません。つまり、DataTemplateは、String型のコンテンツを表示する方法をコントロールに指示しただけです。

あなたの質問に対する一般的な答えは、DecoratedItem1のDataTriggerをContent.Visibilityのパスにバインドする場合、そこに入れるコンテンツが常にUIElementであることを確認する必要があるということです。逆に、あらゆる種類のコンテンツをコントロールに配置できるようにする場合は、他の何かをトリガーする必要があります。

あなたの質問に対する具体的な答えは、厳密には、あなたのより広い意図に依存しています(特に、あなたのコントロールのコンテンツの可視性がどのように設定/変更されるかに依存します)。いくつかの可能性:

  • フォーム「Content.Visibility」のDataTriggerバインディングが本当に必要な場合は、Contentが常にUIElementであることを確認してください。たとえば、スタイルの作業形式を使用してから、TextBlockのテキストを適切なものにバインドします。ただし、これは、派生コントロールをContentControlとして考えることにはあまり適合しないため、...

  • DataTriggerはおそらく他のものにバインドする可能性があります。質問が形成される方法から、さまざまなコンテンツエンティティが表示可能かどうかを制御する他のプロパティまたはコードビハインドがあるように見えます。

  • 最後に、TextBlockにDataTriggerを追加できます。このDataTriggerは、自身の可視性に基づいて親の可視性を設定します。次に、DataTriggerをスタイルDecoratedItem1で、「Content.Visibility」ではなくパス「Visibility」でバインドします。基本的に、Visibilitiesを手動でチェーンします。

編集

これをどのように使用したいかについて説明したことに基づいて、ビジュアルツリーを検討する必要があるように思われます。DecoratedItemコントロールを拡張して、次の機能を持たせることができます。UIElmentsであるすべての視覚的な子が折りたたみの可視性を持っている場合(または視覚的な子がない場合)、それも折りたたみられます(または、目的の機能に適したロジックがあれば)その視覚的な子の可視性の観点から)。コードのVisualTreeHelperクラス、特にGetChildrenCountメソッドとGetChildメソッドを使用する必要があります。また、DecoratedItemクラスで、OnVisualChildrenChangedをオーバーライドして(基本クラスメソッドを呼び出したまま)、表示されている子のUIElement.IsVisibleChangedイベントを取得できるようにします。

于 2012-10-03T17:03:00.710 に答える