1

初期化時にコントロールが表示されない場合、ユーザー コントロールの 1 つでバインドが適切に設定されないという問題が発生しました。この問題は、次の単純化されたコントロールで再現しました。

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

        public string Test
        {
            get { return (string)GetValue(TestProperty); }
            set { SetValue(TestProperty, value); }
        }

        public static readonly DependencyProperty TestProperty =
            DependencyProperty.Register("Test", typeof(string), 
            typeof(Test3), new UIPropertyMetadata("test3 default text"));        
    }  

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

        public FrameworkElement Test3Control
        {
            get { return (FrameworkElement)GetValue(Test3ControlProperty); }
            set { SetValue(Test3ControlProperty, value); }
        }

        public static readonly DependencyProperty Test3ControlProperty =
            DependencyProperty.Register("Test3Control", typeof(FrameworkElement), 
            typeof(Test2), new UIPropertyMetadata(null));

        public string Test
        {
            get { return (string)GetValue(TestProperty); }
            set { SetValue(TestProperty, value); }
        }

        public static readonly DependencyProperty TestProperty =
            DependencyProperty.Register("Test", typeof(string), typeof(Test2), 
            new UIPropertyMetadata("test2 default text"));
    }

public partial class Test1 : UserControl
    {
        public Test1()
        {
            InitializeComponent();
        }

        public string Test
        {
            get { return (string)GetValue(TestProperty); }
            set { SetValue(TestProperty, value); }
        }

        public static readonly DependencyProperty TestProperty =
            DependencyProperty.Register("Test", typeof(string), 
            typeof(Test1), new UIPropertyMetadata("test1 default text"));        
    }

ユーザーコントロールの XAML:

<UserControl x:Class="Test.Test1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:dummy="clr-namespace:WpfTestApplication.Test"
             Name="ucThis">

    <UserControl.Resources>
        <Style TargetType="{x:Type test:Test2}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type test:Test2}">
                        <GroupBox Header="Test2">
                            <StackPanel>
                                <TextBox IsEnabled="False" Text="{TemplateBinding Test}"/>
                                <GroupBox Header="Test3">
                                    <ContentPresenter Content="{TemplateBinding Test3Control}"/>
                                </GroupBox>
                            </StackPanel>
                        </GroupBox>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>

    <Grid>
        <test:Test2 Test="{Binding ElementName=ucThis,Path=Test}">
            <test:Test2.Test3Control>
                <test:Test3 Test="{Binding ElementName=ucThis,Path=Test}">
                    <test:Test3.Template>
                        <ControlTemplate TargetType="{x:Type test:Test3}">
                            <TextBox IsEnabled="False" Text="{TemplateBinding Test}"/>
                        </ControlTemplate>
                    </test:Test3.Template>
                </test:Test3>
            </test:Test2.Test3Control>
        </test:Test2>
    </Grid>
</UserControl>

...そしてメインウィンドウ用のXAML(とにかくその根性):

<DockPanel>            
    <StackPanel>
        <TextBox Name="tbInput"/>    

        <Expander Header="Initially Visible" IsExpanded="True">
            <test:Test1 Test="{Binding ElementName=tbInput, Path=Text}" />
        </Expander>

        <Expander Header="Initially Collapsed" IsExpanded="False">
            <test:Test1 Test="{Binding ElementName=tbInput, Path=Text}" />
        </Expander>
    </StackPanel>    
</DockPanel>

テキスト ボックス ("tbInput") に入力されたテキストは、Test2 ボックスと Test3 ボックスに表示されると予想されます。最初に折りたたまれた Test3 は、テキストが入力されたときに表示されている場合でも、常に既定のテキストを表示します。

Snoop を使用してこれを調査しようとしましたが、Snoop を使用してツリーの関連部分を評価すると、問題は自動的に修正されるため、あまり役に立ちませんでした。

この動作の原因は何ですか? どうすれば修正できますか?詳細はどこで確認できますか?

アップデート:

出力ウィンドウを見て、次のエラー メッセージを発見しました。 System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=ucThis'. BindingExpression:Path=Test; DataItem=null; target element is 'Dummy3' (Name=''); target property is 'Test' (type 'String')

これらのコントロールでロードおよび初期化されたイベントを処理することで、起動時に Dummy1 と Dummy2 がロードされた後にこのエラーが発生することがわかります。ダミー 3 は、表示されるまで読み込まれません。

4

2 に答える 2

1

問題は、折りたたまれたTest3インスタンスにテンプレートが適用されていないため、ビジュアル ツリーに挿入されていないことだと思います。Test1したがって、バインディングが作成されたときに外部インスタンスの名前スコープ内にないため、 にucThis指定された名前を解決できませんElementName

Test3Controlの論理ツリーに追加することで、これに対処できますTest2。依存関係プロパティの定義を次のように変更してみてください。

public static readonly DependencyProperty Test3ControlProperty =
    DependencyProperty.Register("Test3Control", typeof(FrameworkElement),
    typeof(Test2), 
    new UIPropertyMetadata(null, OnTest3ControlPropertyChanged));

private static void OnTest3ControlPropertyChanged(
    DependencyObject d,
    DependencyPropertyChangedEventArgs e)
{
    var source = (Test2)d;

    var oldValue = e.OldValue as FrameworkElement;
    var newValue = e.NewValue as FrameworkElement;

    if (oldValue != null)
        source.RemoveLogicalChild(oldValue);

    if (newValue != null)
        source.AddLogicalChild(newValue);
}

ほとんどの場合、新しい にUIElement基づくプロパティをコントロールに追加するときは、それが論理ツリーに確実に追加されるようにする必要があります。これは自動的には行われません。さらに言えば、ビジュアル ツリーにも自動的に追加されません。この場合、テンプレートに明示的に挿入されるため、ビジュアル ツリーにのみ読み込まれます。

WPF のコア コントロールの内部Decorator(Border派生元) とそのChildプロパティを見て、新しいコントロール タイプを定義するときにどのような配管が必要になるかを確認してください。

また、依存関係プロパティではない「子コントロール」プロパティの数にも注意してください。ベースの依存関係プロパティで問題が発生しやすくVisual、特に setter を介してプロパティを変更したり、アニメーション化したりする場合に問題が発生します。子コントロールを通常の CLR プロパティとして公開するだけで、開発者がプロ​​パティを誤用したり、頭を悩ませたりするのを思いとどまらせるのが最善だと思います。

于 2013-11-12T22:09:55.230 に答える