web-dev-qa-db-ja.com

TextBlock.Inlinesをバインドするデータ

WPFアプリは、UIに表示する必要のあるメッセージのストリームをバックエンドサービスから受信します。これらのメッセージは大きく異なり、メッセージごとに異なる視覚的レイアウト(文字列形式、色、​​フォント、アイコンなど)が必要です。

メッセージごとにインライン(Run、TextBlock、Italicなど)を作成し、それらをすべてObservableCollection<>に入れて、UIのTextBlock.InlinesでWPFデータバインディングの魔法を使用できるようにしたいと思っていました。 。これを行う方法が見つかりませんでした、これは可能ですか?

27
will

_TextBlock.Inlines_プロパティは依存関係プロパティではないため、これは不可能です。依存関係プロパティのみがデータバインディングのターゲットになります。

正確なレイアウト要件によっては、ItemsControlを使用してこれを実行できる場合があります。ItemsPanelWrapPanelに設定され、ItemsSourceはコレクション。 (InlineUIElementではないため、ここでいくつかの実験が必要になる場合があります。そのため、デフォルトのレンダリングは、表示されるのではなく、おそらくToString()を使用して行われます。)

または、新しいコントロールを作成する必要がある場合があります。 MultipartTextBlock、バインド可能なPartsSourceプロパティ、およびデフォルトのテンプレートとしてTextBlockPartsSourceが設定されると、コントロールはCollectionChangedイベントハンドラーを(直接またはCollectionChangedEventManagerを介して)アタッチし、コードから_TextBlock.Inlines_コレクションをPartsSourceコレクションとして更新します。かわった。

いずれの場合も、コードがInline要素を直接生成している場合は注意が必要な場合があります(Inlineを2つの場所で同時に使用することはできないため)。あるいは、テキストやフォントなどの抽象モデル(つまり、ビューモデル)を公開し、Inlineを介して実際のDataTemplateオブジェクトを作成することを検討することもできます。これにより、テスト容易性も向上する可能性がありますが、明らかに複雑さと労力が追加されます。

11
itowlson

TextBlockサブクラスにDependencyプロパティを追加できます

public class BindableTextBlock : TextBlock
{
    public ObservableCollection<Inline> InlineList
    {
        get { return (ObservableCollection<Inline>)GetValue(InlineListProperty); }
        set { SetValue(InlineListProperty, value); }
    }

    public static readonly DependencyProperty InlineListProperty =
        DependencyProperty.Register("InlineList",typeof(ObservableCollection<Inline>), typeof(BindableTextBlock), new UIPropertyMetadata(null, OnPropertyChanged));

    private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        BindableTextBlock textBlock = sender as BindableTextBlock;
        ObservableCollection<Inline> list = e.NewValue as ObservableCollection<Inline>;
        list.CollectionChanged += new     System.Collections.Specialized.NotifyCollectionChangedEventHandler(textBlock.InlineCollectionChanged);
    }

    private void InlineCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
        {
            int idx = e.NewItems.Count -1;
            Inline inline = e.NewItems[idx] as Inline;
            this.Inlines.Add(inline);
        }
    }
}
13
Frank A.

WPFのバージョン4では、Runオブジェクトにバインドできるようになります。これにより、問題が解決する場合があります。

私は過去にItemsControlをオーバーライドし、ItemsControlのアイテムとしてテキストを表示することでこの問題を解決しました。 Dr. WPFがこの種のものについて行ったチュートリアルのいくつかを見てください: http://www.drwpf.com

6
timothymcgrath

要件を正しく取得している場合は、着信メッセージを手動で確認し、メッセージごとにTextBlock.Inlinesプロパティに要素を追加できます。 DataBindingは必要ありません。私はこれを次のように行いました:

public string MyBindingPath
    {
        get { return (string)GetValue(MyBindingPathProperty); }
        set { SetValue(MyBindingPathProperty, value); }
    }

    // Using a DependencyProperty as the backing store for MyBindingPath.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty MyBindingPathProperty =
        DependencyProperty.Register("MyBindingPath", typeof(string), typeof(Window2), new UIPropertyMetadata(null, OnPropertyChanged));

    private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        (sender as Window2).textBlock.Inlines.Add(new Run(e.NewValue.ToString()));
    }
4
viky

あなたの解決策をフランクに感謝します。私はそれが私のために働くようにするためにいくつかの小さな変更を加えなければなりませんでした。

public class BindableTextBlock : TextBlock
{
    public ObservableCollection<Inline> InlineList
    {
        get { return (ObservableCollection<Inline>) GetValue(InlineListProperty); }
        set { SetValue(InlineListProperty, value); }
    }

    public static readonly DependencyProperty InlineListProperty =
        DependencyProperty.Register("InlineList", typeof (ObservableCollection<Inline>), typeof (BindableTextBlock), new UIPropertyMetadata(null, OnPropertyChanged));

    private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        BindableTextBlock textBlock = (BindableTextBlock) sender;
        textBlock.Inlines.Clear();
        textBlock.Inlines.AddRange((ObservableCollection<Inline>) e.NewValue);
    }
}
3
LawMan

この質問は非常に古いと思いますが、とにかく別の解決策を共有したいと思いました。これは、WPFの動作/添付プロパティを利用します。

public static class TextBlockExtensions
    {
    public static IEnumerable<Inline> GetBindableInlines ( DependencyObject obj )
        {
        return (IEnumerable<Inline>) obj.GetValue ( BindableInlinesProperty );
        }

    public static void SetBindableInlines ( DependencyObject obj, IEnumerable<Inline> value )
        {
        obj.SetValue ( BindableInlinesProperty, value );
        }

    public static readonly DependencyProperty BindableInlinesProperty =
        DependencyProperty.RegisterAttached ( "BindableInlines", typeof ( IEnumerable<Inline> ), typeof ( TextBlockExtensions ), new PropertyMetadata ( null, OnBindableInlinesChanged ) );

    private static void OnBindableInlinesChanged ( DependencyObject d, DependencyPropertyChangedEventArgs e )
        {
        var Target = d as TextBlock;

        if ( Target != null )
            {
            Target.Inlines.Clear ();
            Target.Inlines.AddRange ( (System.Collections.IEnumerable) e.NewValue );
            }
        }
    }

XAMLでは、次のように使用します。

<TextBlock MyBehaviors:TextBlockExtensions.BindableInlines="{Binding Foo}" />

これにより、TextBlockから継承する必要がなくなります。 IEnumerableの代わりにObservableCollectionを使用しても同様に機能する可能性があります。その場合、コレクションの変更をサブスクライブする必要があります。

3
B. Tossings
Imports System.Collections.ObjectModel
Imports System.Collections.Specialized

Public Class BindableTextBlock
Inherits TextBlock

Public Property InlineList As ObservableCollection(Of Inline)
    Get
        Return GetValue(InlineListProperty)
    End Get

    Set(ByVal value As ObservableCollection(Of Inline))
        SetValue(InlineListProperty, value)
    End Set
End Property

Public Shared ReadOnly InlineListProperty As DependencyProperty = _
                       DependencyProperty.Register("InlineList", _
                       GetType(ObservableCollection(Of Inline)), GetType(BindableTextBlock), _
                       New UIPropertyMetadata(Nothing, AddressOf OnInlineListPropertyChanged))

Private Shared Sub OnInlineListPropertyChanged(sender As DependencyObject, e As DependencyPropertyChangedEventArgs)
    Dim textBlock As BindableTextBlock = TryCast(sender, BindableTextBlock)
    Dim list As ObservableCollection(Of Inline) = TryCast(e.NewValue, ObservableCollection(Of Inline))
    If textBlock IsNot Nothing Then
        If list IsNot Nothing Then
            ' Add in the event handler for collection changed
            AddHandler list.CollectionChanged, AddressOf textBlock.InlineCollectionChanged
            textBlock.Inlines.Clear()
            textBlock.Inlines.AddRange(list)
        Else
            textBlock.Inlines.Clear()

        End If
    End If
End Sub

''' <summary>
''' Adds the items to the inlines
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub InlineCollectionChanged(sender As Object, e As NotifyCollectionChangedEventArgs)
    Select Case e.Action
        Case NotifyCollectionChangedAction.Add
            Me.Inlines.AddRange(e.NewItems)
        Case NotifyCollectionChangedAction.Reset
            Me.Inlines.Clear()
        Case NotifyCollectionChangedAction.Remove
            For Each Line As Inline In e.OldItems
                If Me.Inlines.Contains(Line) Then
                    Me.Inlines.Remove(Line)
                End If
            Next
    End Select
End Sub

End Class

PropertyChangedハンドラーに追加のコードが必要になる場合があると思います。そのため、バインドされたコレクションに既にコンテンツが含まれている場合はtextBlock.Inlinesを初期化し、既存のコンテキストをクリアします。

0
BadBiki