2017-04-26 2 views
3

私は同じ方法でコンテナになることを意図したWPFコントロールを作成していますBorderScrollViewerはコンテナです。これはEllipsisButtonControlと呼ばれ、コンテンツの右側に省略記号ボタンを配置することになっています。ここでは、それが使用されるために、私はつもりかの例です:ここでコンテナとして機能するWPFコントロールでは、どのようにコンテンツを配置しますか?

<local:EllipsisButtonControl> 
    <TextBlock Text="Testing" /> 
</local:EllipsisButtonControl> 

EllipsisButtonControlのためのXAMLです:

using System.Windows; 
using System.Windows.Input; 

namespace WpfApplication1 
{ 
    public partial class EllipsisButtonControl 
    { 
     public EllipsisButtonControl() 
     { 
      InitializeComponent(); 
     } 

     public static string GetCommand(DependencyObject obj) 
     { 
      return (string)obj.GetValue(CommandProperty); 
     } 

     public static void SetCommand(DependencyObject obj, string value) 
     { 
      obj.SetValue(CommandProperty, value); 
     } 

     public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
       name: "Command", 
       propertyType: typeof(ICommand), 
       ownerType: typeof(EllipsisButtonControl), 
       defaultMetadata: new UIPropertyMetadata()); 
    } 
} 

このdoesnの:

<ContentControl 
    x:Class="WpfApplication1.EllipsisButtonControl" 
    x:Name="ContentControl" 
    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" 
    mc:Ignorable="d" 
    d:DesignHeight="30" d:DesignWidth="300"> 

    <Grid> 

     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="*" /> 
      <ColumnDefinition Width="Auto" /> 
     </Grid.ColumnDefinitions> 

     <ContentPresenter Grid.Column="0" Content="{Binding ElementName=ContentControl, Path=Content}" /> 

     <Button Grid.Column="1" Command="{Binding ElementName=ContentControl, Path=Command}" Margin="3,0" Width="30" Height="24" MaxHeight="24" VerticalAlignment="Stretch" Content="..." /> 

    </Grid> 

</ContentControl> 

そしてここでは、背後にあるコードがあります仕事はありません。デザイナーはSystem.Runtime.Remoting.RemotingExceptionでクラッシュします。

XAML EllipsisButtonControl XAMLのContentPresenterへのバインディングは間違っていると思いますが、それを正しく行う方法はわかりません。その行がコントロールの内容を参照するための適切な構文は何ですか? (例えば使用例で定義されてTextBlock

編集:

突くが、(作業コードを含む)以下の総合的な答えを提供しますが、私の最初の誤解を共有するかもしれない他の人の利益のために、私がまとめてみましょうここでの重要な概念:コンテナコントロールはそれ自体がコンテンツを配置することはできません。呼び出し元のXAMLがコンテンツを表示する方法を変更するテンプレートを定義することによって、望ましい効果が得られます。このソリューションの残りの部分は、その前提に従います。生成されたレンダリングしなければならないContentPresenterを含んで既存のコードあなたはこのContentPresenter表示EllipsesButtonControlの生成されたコンテンツを作っている、ではこの

<ContentPresenter Grid.Column="0" Content={Binding Content} /> 

<ContentPresenter Grid.Column="0" Content="{Binding ElementName=ContentControl, Path=Content}" /> 

+0

Commandは通常の依存関係プロパティではなく、接続されたプロパティですか。 – Clemens

+0

ええと...私がコピーした例がそうしていたので...あなたの質問でプロンプトが表示され、添付プロパティを調べて、DependencyProperty.RegisterAttached()、DependencyProperty.RegisterAttached()をLine 23で使用する必要があることを知りました。ありがとう。 – Lork

+0

* "コンテナコントロールはコンテンツ自体を置くことはできません" * - それは可能ですが、コンテンツは1つしかないので、コンテンツを外側に設定すると内側に置き換えられます。あなたが望むのは、コンテンツが内部に置かれる(フレーミング)ことです(=テンプレート)。 – poke

答えて

1
<local:EllipsisButtonControl> 
    <TextBlock Text="Testing" /> 
</local:EllipsisButtonControl> 

これは、ユーザーコントロールのContentを設定しません。しかし、そのユーザーコントロールのXAMLで、次のん:

<ContentControl …> 
    <Grid> 
     … 
    </Grid> 
</ContentControl> 

呼び出しXAMLは、ここにも優先を持っているので、何でもあなたがそのユーザーコントロールのXAML内で行うが、実際には無視されます。

解決策は、ユーザーコントロールのテンプレートを設定することです。テンプレート(この場合はコントロールのコントロールテンプレート)は、コントロール自体のレンダリング方法を決定します。ユーザーコントロールのための最も単純なテンプレート(デフォルトも)はContentPresenterを使用しているだけですが、もちろんその周りにいくつかのものを追加したいので、テンプレートを上書きする必要があります。これは一般的に次のようになります。

<ContentControl …> 
    <!-- We are setting the `Template` property --> 
    <ContentControl.Template> 
     <!-- The template value is of type `ControlTemplate` and we should 
      also set the target type properly so binding paths can be resolved --> 
     <ControlTemplate> 

      <!-- This is where your control code actually goes --> 

     </ControlTemplate> 
    </ContentControl.Template> 
</ContentControl> 

これはこの作業を行うために必要なフレームです。ただし、コントロールテンプレートの内部に入ったら、適切なバインディングタイプを使用する必要があります。テンプレートを作成し、親コントロールのプロパティにバインドしたいので、親コントロールをバインディングの相対ソースとして指定する必要があります。しかし、これを行う最も簡単な方法は、マークアップ拡張子TemplateBindingを使用することです。その使用、ContentPresenterは、上記ControlTemplateの内側に、このように配置することができます:

<ContentPresenter Content="{TemplateBinding Content}" /> 

そして、それはすべてあなたが働いコンテンツプレゼンターを取得するために、ここで必要でなければなりません。

しかし、コントロールテンプレートを使用するようになったので、もちろん他のバインディングも調整する必要があります。特に、カスタム依存プロパティCommandへのバインディング。 、私たちはここで明示的にカスタム依存関係プロパティを参照する必要があるこれは、一般的にタイプContentControl標的にされContentに私たちのコントロールテンプレートので、バインディングテンプレートとちょうど同じように見えるだろうとContentControlは、カスタムプロパティがありません。

<Button Command="{TemplateBinding local:EllipsisButtonControl.Command}" … /> 

これを済ませたら、すべてのバインディングが正常に動作するはずです。 (あなたが今思っている場合:はい、バインディングは常にタイプの静的な依存関係プロパティを対象):


だから、このすべてを合計する、カスタムコンテンツコントロールは次のようになります。

<ContentControl 
     x:Class="WpfApplication1.EllipsisButtonControl" 
     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:WpfApplication1" 
     d:DesignHeight="30" d:DesignWidth="300" mc:Ignorable="d"> 
    <ContentControl.Template> 
     <ControlTemplate TargetType="ContentControl"> 

      <Grid> 
       <ContentPresenter Grid.Column="0" 
         Content="{TemplateBinding Content}" /> 

       <Button Grid.Column="1" Content="…" 
         Command="{TemplateBinding local:EllipsisButtonControl.Command}" /> 
      </Grid> 

     </ControlTemplate> 
    </ContentControl.Template> 
</ContentControl> 
+0

このアプローチでは別の考え方が必要ですが、私は理解し始めています。しかし、それは2つの質問を提起する:1)コードを機能させるために、私はContentControlをUserControlに変更しなければならなかった。 ContentControlはなぜ機能しないのですか? 2)テンプレートのバインディングを使用して、依存関係プロパティとしてコーディングするのではなく、ボタンのコマンドをサポートすることができますか? – Lork

+0

私の理解が深まるにつれて、既にあなたは答えていることに気が付きました。質問2:いいえ、ContentControlにCommandプロパティがないのでCommandの依存関係プロパティーコードを拒否できませんでした(そしてUserControlに切り替えた後、 )。 – Lork

+0

一般的にWPFでは、コンポーネントの動作を少し再考する必要があります。一般に、コントロールはそれに接続されたいくつかのテンプレートを持つ単なるコードです(「ルックレスコンポーネント」も参照してください)。多くの場合、上書きするデフォルトのテンプレートがあります。あなたの質問に答えるには:1)これは 'ContentControl'でも動作します。実際の例は[このGist](https://gist.github.com/poke/bd9bcb6d1f1bf50b988ea8909edb99af)を参照してください。 2)コントロールテンプレート内の他のものにバインドすることができますが、ここでは 'EllipsisButtonControl'にプロパティを公開したいので、実際には依存プロパティが必要です。 – poke

1

このラインを交換してみてください内容はContentPresenter .....無限再帰を含むElipsesButtonControlです。

+0

Andrew、デザイナーのクラッシュ問題を解決しましたが、何かが間違っています。使用例を試してみると、EllipsisButtonControlでラップされていないかのようにTextBlockだけが表示されます。 – Lork

1

EllipsisButtonControlのXAMLは、すでにそのコンテンツをトップレベルのグリッドに設定しています。おそらくあなたが望むのは、ControlTemplateを作成することです。このように:

<ContentControl x:Class="WpfApplication1.EllipsisButtonControl" 
       x:Name="ContentControl" 
       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" 
       mc:Ignorable="d" d:DesignHeight="30" d:DesignWidth="300"> 
    <ContentControl.Template> 
     <ControlTemplate> 
      <Grid> 
       <Grid.ColumnDefinitions> 
        <ColumnDefinition Width="*" /> 
        <ColumnDefinition Width="Auto" /> 
       </Grid.ColumnDefinitions> 

       <ContentPresenter Grid.Column="0" 
        Content="{Binding ElementName=ContentControl, Path=Content}"/> 

       <Button Grid.Column="1" 
        Command="{Binding ElementName=ContentControl, Path=Command}" 
        Margin="3,0" Width="30" Height="24" MaxHeight="24" 
        VerticalAlignment="Stretch" Content="..." /> 
      </Grid> 
     </ControlTemplate> 
    </ContentControl.Template> 
</ContentControl> 
関連する問題