web-dev-qa-db-ja.com

子をUserControlに追加する

私の仕事

UserControlを作成します。これには、WPFで使用できる任意の視覚的な子を含めることができます。子は、UserControlの子であるコンテナーに表示されます。

私の問題

コンテナに子を正しく表示させることができません。サーバルの方法を試しましたが、デザイナーで機能する方法が見つかりませんでした。 ContentControlも使用しようとしましたが、何も表示されません。

私のアプローチ

最初に this リンクを見つけ、いくつかのバリエーションを試してみました。コンテンツを適切なコンテナに表示することができましたが、コンテンツプロパティがset-privateであり、デザイナーがそれを上書きしたいため、デザイナーでは機能しません。 XAMLにすべてを配置することは機能しますが、デザイナーと共同で作業する場合はこれは適切ではありません。これは好きな方法かもしれません。

この後、ContentControl- typeのバインド可能なプロパティにContent- propertyをバインドしてUIElementCollectionを使用しようとしました。このアプローチはデザイナーでエラーをスローしていませんが、コンテナーにコントロール(例:Button)が表示されないことを認めなければなりません。空のままですが、子が追加されます。

結論

簡単で迅速な解決策を探すサーバル時間後、私はここで解決策を求めることにしました。少しがっかりです。 MicrosoftがMSDNにサンプルを入手できれば、非常に役立ちます。

これをアーカイブする簡単な方法があるに違いない。

現在の状況

Andrei Gavrilaおよびjbergerのおかげで、表示するノードを作成するためにアーカイブしましたコンテンツ(以下のコードを参照)ですが、まだ2つの問題があります。-デザイナーのサポートなし-ボーダー(xamlを参照)はデザイナーに表示されず、アプリが実行されているときに表示されず、マージンもありません

public class NodeContent : ContentControl
{
    static NodeContent()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(NodeContent), new FrameworkPropertyMetadata(typeof(NodeContent)));
    }
}

public partial class Node : UserControl, INotifyPropertyChanged
{
    UIElementCollection _Elements;

    public event PropertyChangedEventHandler PropertyChanged;

    public UIElementCollection NodeContent
    {
        get { return _Elements; }
        set
        {
            _Elements = value;
            OnPropertyChanged("NodeContent");
        }
    }

    public Node()
    {
        InitializeComponent();
        NodeContent = new UIElementCollection(NodeContentContainer, this);
    }



    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

Node-Xaml:

<UserControl x:Class="Pipedream.Nodes.Node"
             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="216" d:DesignWidth="174" Background="Transparent" Name="NodeControl" xmlns:my="clr-namespace:Pipedream.Nodes">

    <Border BorderThickness="1" CornerRadius="20" BorderBrush="Black" Background="White">
        <Grid>
            <my:NodeContent x:Name="NodeContentContainer" Margin="20" Content="{Binding Source=NodeControl, Path=NodeContent}" />
        </Grid>
    </Border>
</UserControl>

Generic-Xaml:

<ResourceDictionary
    xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Pipedream.Nodes">


    <Style TargetType="{x:Type local:NodeContent}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:Node}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <ContentPresenter />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>
24
Felix K.

通常、UIElementCollectionタイプの依存関係プロパティをバインドすることはできません。このようなものを試してください:

MultiChildDemo.xaml

ここで見るものは何もありません。 StackPanelは子要素を保持します。あなたは明らかにかなり多くを行うことができます。

コード:

_<UserControl x:Class="Demo.MultiChildDemo"
             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:demo="clr-namespace:Demo"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel x:Name="PART_Host" />
</UserControl>
_

MultiChildDemo.xaml.cs

注意することが重要:

  • ContentPropertyAttribute属性は、このタイプの親要素で囲まれたすべての要素によって設定されるプロパティを設定します。したがって、_<MultiChildDemo></MultiChildDemo>_内の要素はChildrenプロパティに追加されます。
  • UserControlを拡張しています。これは、完全にカスタムコントロールを必要としません。
  • WPFでは、DependencyProperty.Register()およびバリアントを使用してプロパティを作成することをお勧めします。 Childrenプロパティにバッキング変数がないことに気づくでしょう:DependencyPropertyがデータを保存してくれます。読み取り専用プロパティを作成していなかった場合、これによりバインディングやその他の優れたWPF機能を使用できるようになります。したがって、インターネット上の例でよく見られるようなプレーンプロパティではなく、依存関係プロパティを使用する習慣を身に付けることが重要です。
  • これは、比較的単純な依存関係プロパティの例です。参照を子の依存関係プロパティにコピーするだけで、_UIElementCollection.Add_への呼び出しが転送されます。はるかに複雑な例が、特にMSDNにあります。

コード:

_using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

namespace Demo
{
    [ContentProperty(nameof(Children))]  // Prior to C# 6.0, replace nameof(Children) with "Children"
    public partial class MultiChildDemo : UserControl
    {
        public static readonly DependencyPropertyKey ChildrenProperty = DependencyProperty.RegisterReadOnly(
            nameof(Children),  // Prior to C# 6.0, replace nameof(Children) with "Children"
            typeof(UIElementCollection),
            typeof(MultiChildDemo),
            new PropertyMetadata());

        public UIElementCollection Children
        {
            get { return (UIElementCollection)GetValue(ChildrenProperty.DependencyProperty); }
            private set { SetValue(ChildrenProperty, value); }
        }

        public MultiChildDemo()
        {
            InitializeComponent();
            Children = PART_Host.Children;
        }
    }
}
_

MultiChildDemoWindow.xaml

ラベルが_<demo:MultiChildDemo>_要素の直接の子孫であることに注意してください。これらを_<demo:MultiChildDemo.Children>_要素で囲むこともできます。ただし、MultiChildクラスに追加したContentPropertyAttribute属性を使用すると、この手順を省略できます。

コード:

_<Window x:Class="Demo.MultiChildDemoWindow"
        xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
        xmlns:demo="clr-namespace:Demo"
        Title="MultiChildDemoWindow" Height="300" Width="300">
    <demo:MultiChildDemo>
        <Label>Test 1</Label>
        <Label>Test 2</Label>
        <Label>Test 3</Label>
    </demo:MultiChildDemo>
</Window>
_
46
Zenexer

UserControlタグを削除してグリッドに置き換えるだけです

4
Andreas

まず、 ユーザーコントロール とカスタムコントロール( コントロール / コンテンツコントロール )の違いを理解しようとします。

シンプルに保つには:

「標準のWPFコントロールは、多くの組み込み機能を提供します。プログレスバーやスライダーなどのプリセットコントロールのいずれかの機能が、組み込みたい機能と一致する場合は、新しいテンプレートを作成する必要があります新しいテンプレートを作成することは、カスタム要素を作成するための最も簡単なソリューションなので、最初にそのオプションを検討する必要があります。

アプリケーションに組み込む機能が、既存のコントロールとコードの組み合わせで実現できる場合は、ユーザーコントロールの作成を検討してください。ユーザーコントロールを使用すると、複数の既存のコントロールを1つのインターフェイスにバインドし、それらの動作を決定するコードを追加できます。

既存のコントロールまたはコントロールの組み合わせで必要な機能を実現できない場合は、カスタムコントロールを作成します。カスタムコントロールを使用すると、コントロールの視覚的表現を定義する完全に新しいテンプレートを作成し、コントロールの機能を決定するカスタムコードを追加できます。」

アダムネイサン-WPFアンリーシュド4

必要なのがContentControlだけの場合:

  1. ContentControlを派生させる新しいCustomControlを作成する
  2. テーマでgeneric.xamlを見つけ、コンテンツプレゼンターをコントロールテンプレートに追加します。上記のように、カスタムコントロールロジックは、その視覚的プレゼンテーションから分離されています
  3. コントロールを通常のContentControlとして使用します。

コンテンツとしての複数のアイテムについては、 ItemsControl を見てください。

上記の手順は次のように変更されます。

派生アイテムコントロール

public class MyCtrl : ItemsControl
{
    static MyCtrl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCtrl), new FrameworkPropertyMetadata(typeof(MyCtrl)));
    }
}

Generic.xamlを変更してItemsPresenterを含める

<Style TargetType="{x:Type local:MyCtrl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyCtrl}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ItemsPresenter />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

コントロールを使用する

<StackPanel>
    <ctrl:MyCtrl>
        <Button Width="100" Height="50">Click</Button>
        <Button Width="100" Height="50">Click</Button>
        <Button Width="100" Height="50">Click</Button>
    </ctrl:MyCtrl>
</StackPanel>

上記のように、この単純なケースでは、ItemsControlを派生させる必要はなく、ItemsControlを使用してテンプレートを定義するだけです。機能を追加して拡張を計画しているときにItemsControlを派生させる

編集:

ボーダー(xamlを参照)はデザイナーには表示されず、アプリの実行中にも表示されないため、マージンもありません

コントロール自体にBorderプロパティを設定する必要があります。

<ctrl:MyCtrl BorderBrush="Red" BorderThickness="3" Background="Green" >
4
Andrei Gavrila