none
カスタムコントロールの子要素を別要素へ配置した際のBindingについて RRS feed

  • 質問

  • お世話になります。

    カスタムコントロールを作成しています。
    機能の概要はカスタムコントロールの子要素を別要素の子に移動(厳密には移動とちょっと違いますが)するものです。

    サンプルでは子要素でTextBlockのTextに対してNameを設定したTextBoxのTextをBindingして表示をするものです。
    これを実行すると、子要素を移動は行えましたがBindingが意図したものになっていないのか、移動後のTextBlockのTextは表示されません。
    (赤枠内のTextBlockには表示され、青枠のTextBlockには表示されない。Bindingが元の状態のままになっている?)

    このような場合にBindingを再構築(?)させるためにはどうすればよいでしょうか?

    <Window x:Class="Sample3"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WPFSample"
            mc:Ignorable="d"
            Title="Sample3" Height="300" Width="300">
        <Grid>
            <local:SampleCustom x:Name="SampleCustom1">
                <StackPanel Name="Content1">
                    <TextBox Name="TextBlock1" Text="123456"/>
                    <TextBlock>
                            <Run Text="入力テキスト:"/>
                            <Run Text="{Binding Text,ElementName=TextBlock1}"/>
                    </TextBlock>
                </StackPanel>
            </local:SampleCustom>
            <StackPanel Margin="10">
                <Border Name="Border1" BorderBrush="Red" BorderThickness="1" CornerRadius="5" Height="100" Padding="5">
                    <StackPanel>
                        <TextBlock>
                            <Run Text="入力テキスト:"/>
                            <Run Text="{Binding Text,ElementName=TextBlock1}"/>
                        </TextBlock>
                    </StackPanel>
                </Border>
                <Border Name="Border2" BorderBrush="Blue" BorderThickness="1" CornerRadius="5" Height="100" Padding="5" Margin="0,10,0,0" >
                </Border>
                <Button Content="表示" Margin="0,10,0,0" Click="Button_Click" />
            </StackPanel>
        </Grid>
    </Window>

    Public Class Sample3
        Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
            SampleCustom1.IsShow = True
        End Sub
    End Class
    ''' <summary>
    ''' SampleCustomコントロール
    ''' </summary>
    ''' <remarks></remarks>
    <Markup.ContentProperty(NameOf(SampleCustom.Content))>
    Public Class SampleCustom
        Inherits System.Windows.Controls.Control
    
        Shared Sub New()
            ''この OverrideMetadata 呼び出しは、この要素が基底クラスと異なるスタイルを提供することをシステムに通知します。
            ''このスタイルは themes\generic.xaml に定義されています
            'DefaultStyleKeyProperty.OverrideMetadata(GetType(SampleCustom), New FrameworkPropertyMetadata(GetType(SampleCustom)))
        End Sub
    
        Public Overrides Sub OnApplyTemplate()
            MyBase.OnApplyTemplate()
            'ChildContent = GetTemplateChild("PART_XXXXX")
        End Sub
    
    #Region "Content依存プロパティ"
        ''' <summary>
        ''' 依存プロパティ コンテンツ
        ''' </summary>
        Public Shared ReadOnly ContentProperty As DependencyProperty =
            DependencyProperty.Register(
                "Content",
                GetType(Object),
                GetType(SampleCustom),
                New PropertyMetadata(Nothing))
        ''' <summary>
        ''' CRLラッパー コンテンツ
        ''' </summary>
        ''' <returns></returns>
        Public Property Content() As Object
            Get
                Return GetValue(ContentProperty)
            End Get
            Set(ByVal value As Object)
                SetValue(ContentProperty, value)
            End Set
        End Property
    #End Region
    #Region "IsShow依存プロパティ"
        ''' <summary>
        ''' 依存プロパティ 表示するかどうか
        ''' </summary>
        Public Shared ReadOnly IsShowProperty As DependencyProperty =
                DependencyProperty.Register(
                    "IsShow",
                    GetType(Boolean),
                    GetType(SampleCustom),
                    New FrameworkPropertyMetadata(False, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, New PropertyChangedCallback(AddressOf OnIsShowChanged)))
        ''' <summary>
        ''' CRLラッパー 表示するかどうか
        ''' </summary>
        ''' <returns></returns>
        Public Property IsShow() As Boolean
            Get
                Return Convert.ToBoolean(GetValue(IsShowProperty))
            End Get
            Set(ByVal value As Boolean)
                SetValue(IsShowProperty, value)
            End Set
        End Property
        ''' <summary>
        ''' IsShowプロパティ変更
        ''' </summary>
        ''' <param name="d"></param>
        ''' <param name="e"></param>
        Private Shared Sub OnIsShowChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
            Dim content = DirectCast(d.GetValue(ContentProperty), DependencyObject)
            Dim insertTarget = d.Parents.OfType(Of Window).First.Descendants.OfType(Of Border).Where(Function(x) x.Name = "Border2").First
            insertTarget.Child = content
        End Sub
    #End Region
    End Class
    Module DependencyObjectExtensions
        ''' <summary>
        ''' 親要素を取得
        ''' </summary>
        ''' <param name="d">依存関係プロパティ システムに関連するオブジェクト</param>
        ''' <returns></returns>
        <Runtime.CompilerServices.Extension>
        Public Iterator Function Parents(d As DependencyObject) As IEnumerable(Of DependencyObject)
            If d Is Nothing Then
                Throw New ArgumentNullException(NameOf(d))
            End If
            Dim pre = d
            Do
                pre = VisualTreeHelper.GetParent(pre)
                If Not IsNothing(pre) Then
                    Yield pre
                Else
                    Exit Do
                End If
            Loop
        End Function
        ''' <summary>
        ''' 子要素を取得
        ''' </summary>
        ''' <param name="d"></param>
        ''' <returns></returns>
        <Runtime.CompilerServices.Extension>
        Public Iterator Function Children(d As DependencyObject) As IEnumerable(Of DependencyObject)
            If d Is Nothing Then
                Throw New ArgumentNullException(NameOf(d))
            End If
    
            Dim count = VisualTreeHelper.GetChildrenCount(d)
            If count = 0 Then
                Exit Function
            End If
    
            For i As Integer = 0 To count - 1
                Dim child = VisualTreeHelper.GetChild(d, i)
                If Not IsNothing(child) Then
                    Yield child
                End If
            Next
        End Function
        ''' <summary>
        ''' 子孫要素を取得
        ''' </summary>
        ''' <param name="d"></param>
        ''' <returns></returns>
        <Runtime.CompilerServices.Extension>
        Public Iterator Function Descendants(d As DependencyObject) As IEnumerable(Of DependencyObject)
            If d Is Nothing Then
                Throw New ArgumentNullException(NameOf(d))
            End If
    
            For Each child In d.Children()
                Yield child
                For Each grandChild In child.Descendants()
                    Yield grandChild
                Next
            Next
        End Function
    End Module




    • 編集済み mikupedia 2016年8月10日 2:53
    2016年8月10日 2:19

回答

  • Tak1wa 様

    早々のご回答ありがとうございました。
    「x:Reference」を使用することにより正しくbindされるようになりました。

    • 回答としてマーク mikupedia 2016年8月10日 5:04
    2016年8月10日 5:01
  • 自己レスです。

    別の方法として、カスタムコントロールの子要素をユーザコントロール化してしまえば
    ElementNameで問題なくバインドされます。

    • 回答としてマーク mikupedia 2016年8月10日 5:04
    2016年8月10日 5:04

すべての返信

  • こんにちは。

    調べてみたところカスタムコントロールのContent内でBindingSourceが設定されていないので、
    おそらくElementNameが使えないのだと思います。

    x:Referenceを変わりに使ってみてはどうでしょうか。

    <StackPanel Name="Content1">
        <TextBox Name="TextBlock1" Text="123456"/>
        <TextBlock>
            <Run Text="入力テキスト:"/>
            <!--<Run Text="{Binding Text,ElementName=TextBlock1}"/>-->
            <Run Text="{Binding Text}" DataContext="{x:Reference Name=TextBlock1}" />
        </TextBlock>
    </StackPanel>

    2016年8月10日 3:47
    モデレータ
  • Tak1wa 様

    早々のご回答ありがとうございました。
    「x:Reference」を使用することにより正しくbindされるようになりました。

    • 回答としてマーク mikupedia 2016年8月10日 5:04
    2016年8月10日 5:01
  • 自己レスです。

    別の方法として、カスタムコントロールの子要素をユーザコントロール化してしまえば
    ElementNameで問題なくバインドされます。

    • 回答としてマーク mikupedia 2016年8月10日 5:04
    2016年8月10日 5:04