WPFアプリは、UIに表示する必要のあるメッセージのストリームをバックエンドサービスから受信します。これらのメッセージは大きく異なり、メッセージごとに異なる視覚的レイアウト(文字列形式、色、フォント、アイコンなど)が必要です。
メッセージごとにインライン(Run、TextBlock、Italicなど)を作成し、それらをすべてObservableCollection<>
に入れて、UIのTextBlock.InlinesでWPFデータバインディングの魔法を使用できるようにしたいと思っていました。 。これを行う方法が見つかりませんでした、これは可能ですか?
_TextBlock.Inlines
_プロパティは依存関係プロパティではないため、これは不可能です。依存関係プロパティのみがデータバインディングのターゲットになります。
正確なレイアウト要件によっては、ItemsControl
を使用してこれを実行できる場合があります。ItemsPanel
はWrapPanel
に設定され、ItemsSource
はコレクション。 (Inline
はUIElement
ではないため、ここでいくつかの実験が必要になる場合があります。そのため、デフォルトのレンダリングは、表示されるのではなく、おそらくToString()
を使用して行われます。)
または、新しいコントロールを作成する必要がある場合があります。 MultipartTextBlock
、バインド可能なPartsSource
プロパティ、およびデフォルトのテンプレートとしてTextBlock
。 PartsSource
が設定されると、コントロールはCollectionChanged
イベントハンドラーを(直接またはCollectionChangedEventManagerを介して)アタッチし、コードから_TextBlock.Inlines
_コレクションをPartsSource
コレクションとして更新します。かわった。
いずれの場合も、コードがInline
要素を直接生成している場合は注意が必要な場合があります(Inline
を2つの場所で同時に使用することはできないため)。あるいは、テキストやフォントなどの抽象モデル(つまり、ビューモデル)を公開し、Inline
を介して実際のDataTemplate
オブジェクトを作成することを検討することもできます。これにより、テスト容易性も向上する可能性がありますが、明らかに複雑さと労力が追加されます。
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);
}
}
}
WPFのバージョン4では、Runオブジェクトにバインドできるようになります。これにより、問題が解決する場合があります。
私は過去にItemsControlをオーバーライドし、ItemsControlのアイテムとしてテキストを表示することでこの問題を解決しました。 Dr. WPFがこの種のものについて行ったチュートリアルのいくつかを見てください: http://www.drwpf.com
要件を正しく取得している場合は、着信メッセージを手動で確認し、メッセージごとに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()));
}
あなたの解決策をフランクに感謝します。私はそれが私のために働くようにするためにいくつかの小さな変更を加えなければなりませんでした。
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);
}
}
この質問は非常に古いと思いますが、とにかく別の解決策を共有したいと思いました。これは、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を使用しても同様に機能する可能性があります。その場合、コレクションの変更をサブスクライブする必要があります。
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を初期化し、既存のコンテキストをクリアします。