none
XAMLでInlines RRS feed

  • 質問

  • はじめまして、こんにちは。

    WPFでVB.netを使用してアプリの作成を行っていて、TextblockのInlinesについての
    質問になります。

    そのアプリでは、listboxとdatatemplateを使用していて、動的にTextblockの中身が入るようにしたいのですが、
    UrlなどをHyperlinkを使ってリンクするようにしたいので、自前でConverterを書いてInlinesを返すということまではできました。

    しかし、Inlines={Binding Converter={StaticResource TextConverter}} としようとすると、「"Inlines"にはアクセス可能なセッターがありません」となり、生成したInlinesを代入させることができません。

    なにかいい方法はないでしょうか?

    • 編集済み z_x_c708 2014年5月4日 12:38
    2014年5月4日 12:37

回答

  • まず、TextBlockのInlinesは読み取り専用のプロパティであるため、値を直接設定することはできません。
    読み取り専用であるためプロパティにセッター(プロパティのsetの部分)がありませんとなります。
    さらにこのプロパティはバインディングを行えるDependencyPropertyではないので、バインディングできません。

    次に、TextBlock.Inlinesの型はSystem.Windows.Documents.InlineCollectionです。こちらは追加削除が可能なコレクションであるため、設定は可能ですが、Bindingが可能なDependencyObjectから継承されていないため直接にBindingを行うことはできません。

    ではどうするかというと、別にバインディング可能なプロパティに一度バインディングしてから、値がセットされた時のイベントでInlinesを再構成してやることになります。
    対象が1個だけなら適当なイベントで処理すればいいですが、添付プロパティを作ってやると使いまわしができて便利でしょう。

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" 
        xmlns:app="clr-namespace:WpfApplication1"> <!-- このプログラムのネームスペースを宣言 -->
        <Grid>
            <ListBox ItemsSource="{Binding}">
                <ListBox.ItemTemplate>
                    <DataTemplate >
                        <TextBlock app:TextBlockTool.Inlines="{Binding}" /><!-- 添付プロパティにバインディングする -->            
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Window>

    Class MainWindow 
        Sub New()
    
            ' この呼び出しはデザイナーで必要です。
            InitializeComponent()
    
            ' InitializeComponent() 呼び出しの後で初期化を追加します。
            Dim ui = New Uri("http://social.msdn.microsoft.com/")
            Me.DataContext = New Object() {ui, "a", 1.234E+33, New TextBox()}
        End Sub
    End Class
    
    Class TextBlockTool
    
        Public Shared Function GetInlines(ByVal element As DependencyObject) As Object
            If element Is Nothing Then
                Throw New ArgumentNullException("element")
            End If
    
            Return element.GetValue(InlinesProperty)
        End Function
    
        Public Shared Sub SetInlines(ByVal element As DependencyObject, ByVal value As Object)
            If element Is Nothing Then
                Throw New ArgumentNullException("element")
            End If
    
            element.SetValue(InlinesProperty, value)
        End Sub
    
        Public Shared ReadOnly InlinesProperty As DependencyProperty _
            = DependencyProperty.RegisterAttached("Inlines", _
                GetType(Object), GetType(TextBlockTool), _
                New FrameworkPropertyMetadata(Nothing, AddressOf Inlines_ChangedCallback))
    
        Public Shared Sub Inlines_ChangedCallback(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
            '添付プロパティの値が変化したら添付した対象のTextBlockのInlinesを再構成する
            Dim tb As TextBlock = TryCast(d, TextBlock)
            If (tb Is Nothing) Then
                Return
            End If
            tb.Inlines.Clear()
    
            If (e.NewValue Is Nothing) Then
                Return
            End If
    
            Dim ie As IEnumerable = Nothing
    
            If (TypeOf e.NewValue Is String) Then
                ie = New Object() {e.NewValue}
            ElseIf (TypeOf e.NewValue Is IEnumerable) Then
                ie = DirectCast(e.NewValue, IEnumerable)
            Else
                ie = New Object(0) {e.NewValue}
            End If
    
            For Each o As Object In ie
                If (o Is Nothing) Then
                    Continue For
    
                ElseIf (TypeOf o Is UIElement) Then
                    tb.Inlines.Add(DirectCast(o, UIElement))
    
                ElseIf (TypeOf o Is Inline) Then
                    tb.Inlines.Add(o)
    
                ElseIf (TypeOf o Is Uri) Then
                    Dim uri As Uri = DirectCast(o, Uri)
                    Dim link As Hyperlink = New Hyperlink(New Run(uri.AbsoluteUri)) With {.NavigateUri = uri}
                    tb.Inlines.Add(link)
    
                Else
                    tb.Inlines.Add(o.ToString())
                End If
            Next
        End Sub
    End Class

    コンバーターでHyperlinkなどのInlineを作って添付プロパティに渡すのもよいですし、添付プロパティに変換機能を付けてしまってもいいです


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2014年5月4日 14:27
    • 回答としてマーク z_x_c708 2014年5月4日 23:57
    2014年5月4日 14:17

すべての返信

  • まず、TextBlockのInlinesは読み取り専用のプロパティであるため、値を直接設定することはできません。
    読み取り専用であるためプロパティにセッター(プロパティのsetの部分)がありませんとなります。
    さらにこのプロパティはバインディングを行えるDependencyPropertyではないので、バインディングできません。

    次に、TextBlock.Inlinesの型はSystem.Windows.Documents.InlineCollectionです。こちらは追加削除が可能なコレクションであるため、設定は可能ですが、Bindingが可能なDependencyObjectから継承されていないため直接にBindingを行うことはできません。

    ではどうするかというと、別にバインディング可能なプロパティに一度バインディングしてから、値がセットされた時のイベントでInlinesを再構成してやることになります。
    対象が1個だけなら適当なイベントで処理すればいいですが、添付プロパティを作ってやると使いまわしができて便利でしょう。

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" 
        xmlns:app="clr-namespace:WpfApplication1"> <!-- このプログラムのネームスペースを宣言 -->
        <Grid>
            <ListBox ItemsSource="{Binding}">
                <ListBox.ItemTemplate>
                    <DataTemplate >
                        <TextBlock app:TextBlockTool.Inlines="{Binding}" /><!-- 添付プロパティにバインディングする -->            
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Window>

    Class MainWindow 
        Sub New()
    
            ' この呼び出しはデザイナーで必要です。
            InitializeComponent()
    
            ' InitializeComponent() 呼び出しの後で初期化を追加します。
            Dim ui = New Uri("http://social.msdn.microsoft.com/")
            Me.DataContext = New Object() {ui, "a", 1.234E+33, New TextBox()}
        End Sub
    End Class
    
    Class TextBlockTool
    
        Public Shared Function GetInlines(ByVal element As DependencyObject) As Object
            If element Is Nothing Then
                Throw New ArgumentNullException("element")
            End If
    
            Return element.GetValue(InlinesProperty)
        End Function
    
        Public Shared Sub SetInlines(ByVal element As DependencyObject, ByVal value As Object)
            If element Is Nothing Then
                Throw New ArgumentNullException("element")
            End If
    
            element.SetValue(InlinesProperty, value)
        End Sub
    
        Public Shared ReadOnly InlinesProperty As DependencyProperty _
            = DependencyProperty.RegisterAttached("Inlines", _
                GetType(Object), GetType(TextBlockTool), _
                New FrameworkPropertyMetadata(Nothing, AddressOf Inlines_ChangedCallback))
    
        Public Shared Sub Inlines_ChangedCallback(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
            '添付プロパティの値が変化したら添付した対象のTextBlockのInlinesを再構成する
            Dim tb As TextBlock = TryCast(d, TextBlock)
            If (tb Is Nothing) Then
                Return
            End If
            tb.Inlines.Clear()
    
            If (e.NewValue Is Nothing) Then
                Return
            End If
    
            Dim ie As IEnumerable = Nothing
    
            If (TypeOf e.NewValue Is String) Then
                ie = New Object() {e.NewValue}
            ElseIf (TypeOf e.NewValue Is IEnumerable) Then
                ie = DirectCast(e.NewValue, IEnumerable)
            Else
                ie = New Object(0) {e.NewValue}
            End If
    
            For Each o As Object In ie
                If (o Is Nothing) Then
                    Continue For
    
                ElseIf (TypeOf o Is UIElement) Then
                    tb.Inlines.Add(DirectCast(o, UIElement))
    
                ElseIf (TypeOf o Is Inline) Then
                    tb.Inlines.Add(o)
    
                ElseIf (TypeOf o Is Uri) Then
                    Dim uri As Uri = DirectCast(o, Uri)
                    Dim link As Hyperlink = New Hyperlink(New Run(uri.AbsoluteUri)) With {.NavigateUri = uri}
                    tb.Inlines.Add(link)
    
                Else
                    tb.Inlines.Add(o.ToString())
                End If
            Next
        End Sub
    End Class

    コンバーターでHyperlinkなどのInlineを作って添付プロパティに渡すのもよいですし、添付プロパティに変換機能を付けてしまってもいいです


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2014年5月4日 14:27
    • 回答としてマーク z_x_c708 2014年5月4日 23:57
    2014年5月4日 14:17
  • 回答ありがとうございました。
    WPFに慣れていなかったのでわからなかったのですが、添付プロパティを作ってやれば
    いいのですね。とても参考になりました。
    2014年5月5日 0:04