1

MyTextBoxから継承するカスタム コントロール を作成しましたTextBox。名前付きコントロールを含むスタイルが関連付けられています。

<Style x:Key="{x:Type MyTextBox}" TargetType="{x:Type MyTextBox}">
    <!-- ... -->
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type MyTextBox}">
                <!-- ... -->
                    <SomeControl x:Name="PART_SomeControl" />
                <!-- ... -->
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

MyTextBoxには依存関係プロパティがあり、設定すると、その値が次のように伝播されますSomeControl

public class MyTextBox : TextBox
{
    // ...

    public static new readonly DependencyProperty MyParameterProperty =
        DependencyProperty.Register(
            "MyParameter",
            typeof(object),
            typeof(MyTextBox),
            new PropertyMetadata(default(object), MyParameterChanged));

    private static void MyParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var me = (MyTextBox)d;
        var someControl = (SomeControl)me.GetTemplateChild("PART_SomeControl");
        someControl.SetValue(SomeControl.MyParameterProperty, e.NewValue);
    }
}

これは、次のように単純なバインディングを行う場合にうまく機能します。

<MyTextBox MyParameter="{Binding}" />

しかし、次のように、RelativeSource を使用してより高度なバインディングを使用すると、次のようになります。

<MyTextBox MyParameter="{Binding DataContext, RelativeSource={RelativeSource
    FindAncestor, AncestorType=ParentView}}"

メソッドは をme.GetTemplateChild()返しますnull。つまり、SomeControl見つからない。

なんで?

私が行った観察の 1 つは、 がある場合RelativeSourceMyParameter依存関係プロパティがすべての依存関係プロパティの最初に設定されるということです。つまり、次のようなことをすると:

<MyTextBox
    OtherParameter="{Binding}"
    MyParameter="{Binding DataContext, RelativeSource={RelativeSource
        FindAncestor, AncestorType=ParentView}}"

MyParameterプロパティは (奇妙なことに) の前に設定されていOtherParameterます。単純なバインディングを使用すると、予想どおり、宣言されたのと同じ順序で設定されます。

(ご覧のとおり、私のコードは無関係なものから取り除かれています。うまくいけば、重要なものはすべて含まれています。)

4

4 に答える 4

5

ほとんどの場合、テンプレートが適用される前に設定されています。これを回避するには、いくつかの方法があります。

  • テンプレートを強制的にロードするには、前にApplyTemplateを呼び出しGetTemplateChildます。

  • BeginInvokeを使用しDispatcherPriority.Loadedて、後で操作を遅らせます。

  • MyParameterChangedテンプレートがない場合は失敗することを許可し、 OnApplyTemplateでロジックを繰り返します ((Windows テーマの変更のように) ロード後にテンプレートが置き換えられた場合に備えて、とにかくこれを行う必要があります)。

子要素に値を渡しているだけのようです。値の継承で添付プロパティを使用することを検討しましたか?

生の DataContext バインディングではなく、バインディングで失敗する理由については、それ自体が継承されたプロパティであるRelativeSource FindAncestorという事実に帰着すると思います。DataContext仮説として、操作の順序を次のように仮定します。

  1. 親は DataContext プロパティを受け取ります
  2. 親が子を追加
  3. 子は MyParameter を評価します
  4. 子供がテンプレートを適用する
  5. 子は親から DataContext を継承します

最初のケース ( ) では、バインド先の DataContext がまだないためMyParameter="{Binding}"、ステップ 3 は更新に失敗し、呼び出されず、例外はありません。手順 5 の後、子の DataContext が更新されると、 が再評価され、その時点までにテンプレートが存在するため、プロパティ変更ハンドラーが機能します。MyParameterMyParameterChangedMyParameter

2 番目のケースでは、親の DataContext プロパティを明確に検索していますが、このプロパティ存在するため、ステップ 3MyParameterChanged 呼び出され、テンプレートがまだ適用されていないために失敗します。

于 2014-05-27T14:17:58.570 に答える
0

ApplyTemplate();直後にInitializeComponent();(通常はコンストラクターで)呼び出すとうまくいきます。メソッドをオーバーライドしてOnApplyTemplate()、ここで呼び出しGetTemplateChildます。

元:

        private TextBox PART_TextBox;
        private RepeatButton PART_UpButton;
        private RepeatButton PART_DownButton;

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            PART_TextBox = GetTemplateChild("PART_TextBox") as TextBox;
            PART_UpButton = GetTemplateChild("PART_UpButton") as RepeatButton;
            PART_DownButton = GetTemplateChild("PART_DownButton") as RepeatButton;
        }

このように、OnApplyTemplate()依存関係プロパティを設定する前に を呼び出す必要があります。

于 2015-12-01T19:21:36.450 に答える
0

これに注意してください:

<MyTextBox MyParameter="{Binding}" />

これと同じです:

<MyTextBox MyParameter="{Binding DataContext, RelativeSource={RelativeSource
FindAncestor, AncestorType=ParentView}}" />

MyTextBoxコントロールが、プロパティが設定されParentViewているという名前のビューにある場合。DataContextその場合、あなたが説明しているような問題は実際には発生しないはずです。したがって、UI が初期化される前にSomeControlを介してオブジェクトにアクセスしようとしているとしか思えません。MyParameterProperty

LoadedまたはInitializedイベントのハンドラーを追加することで、これをテストできます。そこにブレーク ポイントを配置し、MyParameterChangedハンドラーに追加のブレーク ポイントを配置して、発生する順序を確認します。オブジェクトが UI で初期化される前に、 s またはインライン XAMLDependencypropertyから s を設定できることに注意してください。Style

于 2014-05-27T14:14:14.087 に答える