10

私は最初の WPF カスタム コントロールを開発していますが、いくつかの問題に直面しています。現在使用しているコードの簡略化されたバージョンは次のとおりです。

using System.Windows;
using System.Windows.Controls;

namespace MyControls
{
    [TemplatePart(Name = "PART_Button", Type = typeof (Button))]
    public class MyControl : Control
    {
        public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof (object), typeof (MyControl), new PropertyMetadata(null, OnLabelPropertyChanged));

        private Button _buttonElement;

        public object Content
        {
            get { return this.GetValue(LabelProperty); }
            set { this.SetValue(ContentProperty, value); }
        }

        static MyControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof (MyControl), new FrameworkPropertyMetadata(typeof (MyControl)));
        }

        private static void OnContentPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            MyControl myControl = sender as MyControl;
            if (myControl != null && myControl._buttonElement != null)
                myControl._buttonElement.Content = e.NewValue;
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this._buttonElement = this.Template.FindName("PART_Button", this) as Button;
        }
    }
}

これは私のカスタム コントロールのテンプレートです。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:MyControls">
    <Style TargetType="{x:Type local:MyControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:MyControl}">
                    <Button x:Name="PART_Button" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

次に、それを Grid 内に配置し、その Content プロパティを設定しようとします。

<Grid x:Name="layoutRoot">
    <controls:MyControl x:Name="myControl" />
</Grid>

コードビハインドは次のとおりです。

using System.Windows;

namespace MyControls
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();

            this.myControl.Content = "test";
        }
    }
}

何らかの理由で、OnContentPropertyChanged コールバックが OnApplyTemplate の前に呼び出されるため、myControl._buttonElement の割り当てが遅すぎて、コンテンツを設定しようとしても null のままです。なぜこれが起こっているのですか?どうすればこの動作を変更できますか?

また、完全な設計時のサポートを提供する必要がありますが、Grid コントロールが ColumnDefinitions で行うように、カスタム コントロールが追加のマークアップを受け入れるようにする方法が見つかりません。

<Grid x:Name="layoutRoot">
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
</Grid>

どんな助けでも大歓迎です!

アップデート

コントロール プロパティが設定された後に OnApplyTemplate メソッドが呼び出される理由を説明するドキュメントを見つけました。

http://msdn.microsoft.com/en-us/library/dd351483%28v=vs.95%29.aspx

問題は、(XAML またはプログラムで) 設定されたプロパティと、コントロールが初期化されていないときに呼び出されるメソッドを追跡して、OnApplyTemplate メソッドが呼び出されたときにそれらを設定/呼び出すことができるようにするにはどうすればよいかということです。コードを複製せずに、コントロールの初期化の前後で同じコールバック/メソッドを機能させるにはどうすればよいでしょうか?

4

2 に答える 2

5

アップデート:

パーツを探して値の変更をテンプレートの要素にプッシュする「プロパティ」の代わりに、テンプレートをテンプレート化されているコントロールのプロパティにバインドする必要があります。

通常、これはテンプレート内のプレゼンターを使用して行われます。たとえばContentPresenter、「コンテンツ」として指定されたプロパティにバインドし (属性を検索してその名前を見つけ[ContentProperty]ます)、テンプレート内のバインディングを使用して、TemplateBindingまたはTemplatedParentそのプロパティに接続します。カスタム コントロール。

次に、プロパティを設定する順序とテンプレートがいつ適用されるかについて問題はありません....コントロールに設定されたデータ/プロパティの「外観」を提供するのはテンプレートであるためです。

カスタム コントロールは、ボタンの「パーツ」にクリック イベントをフックするなど、特定の動作/機能を提供する必要がある場合にのみ、「パーツ」を認識して対話する必要があります。

この場合、コード ビハインドのコンストラクターで Content を設定する代わりに、テンプレートをプロパティにバインドする必要があります。以下に示す例は、それが一般的に Content プロパティでどのように行われるかを示しています。

または、より明示的にプロパティを引き出すこともできます。たとえば、これはテンプレート内にある可能性があります。

 <Label Content="{TemplateBinding MyPropertyOnMyControl}" .....

 <Button Content="{TemplateBinding AnotherPropertyOnMyControl}" .....

Content DependencyProperty をフックするよりも、[ContentProperty] 属性を使用して「コンテンツ」を指定し、テンプレートで ContentPresenter を使用してボタン内に挿入できるようにする方がよいと思います。(ContentControl から継承すると、「コンテンツ」の動作が提供されます)。

[TemplatePart(Name = "PART_Button", Type = typeof (Button))]
public class MyControl : Control
[ContentProperty("Content")]

<ControlTemplate TargetType="{x:Type local:MyControl}">
<Button x:Name="PART_Button">
<ContentPresenter/>
</Button>
</ControlTemplate>

Grid が ColumnDefinition で行うように、XAML を介してデザイン時のデータを指定できるようにしたい場合は、Property Element 構文を使用して IList/ICollection タイプのプロパティを満たす項目を指定するだけです。

したがって、受け入れるタイプのコレクションを保持できる独自のプロパティを作成するだけです。

public List<MyItem> MyItems { get; set; }    // create in your constructor.
于 2012-08-06T15:05:51.837 に答える