35

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

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

4

9 に答える 9

15

依存関係プロパティをTextBlockサブクラスに追加できます

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);
        }
    }
}
于 2012-03-03T13:08:07.843 に答える
13

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

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

MultipartTextBlockまたは、バインド可能なPartsSourceプロパティとをTextBlockデフォルトのテンプレートとして使用して、新しいコントロールを作成する必要がある場合もあります。が設定されると、コントロールはイベントハンドラーを(直接またはCollectionChangedEventManagerを介して)PartsSourceアタッチし、コレクションの変更に応じてコードからコレクションを更新します。CollectionChangedTextBlock.InlinesPartsSource

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

于 2009-12-25T00:21:31.813 に答える
12

これは、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を使用しても同様に機能する可能性があります。その場合は、コレクションの変更をサブスクライブする必要があります。

于 2017-12-01T18:54:11.023 に答える
6

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

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

于 2009-12-26T12:41:53.537 に答える
6

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

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);
    }
}
于 2015-05-28T19:01:06.243 に答える
4

要件を正しく取得している場合は、着信メッセージを手動で確認し、メッセージごとに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()));
}
于 2009-12-25T03:55:27.603 に答える
1

PavelAnhikouskiからの提案は完璧に機能します。ここでは、MVVMのデータバインディングで欠落している部分があります。ビューモデルのAddTraceプロパティを使用して、ウィンドウのOutputBlockにコンテンツを追加します。ウィンドウのバッキングプロパティMyBindingPathは必要ありません。

ViewModel:

private string _addTrace;
public string AddTrace
{
  get => _addTrace;
  set
  {
    _addTrace = value;
    NotifyPropertyChanged();
  }
}

public void StartTrace()
{
  AddTrace = "1\n";
  AddTrace = "2\n";
  AddTrace = "3\n";
}

TraceWindow.xaml:

  <Grid>
    <ScrollViewer Name="Scroller" Margin="0" Background="#FF000128">
      <TextBlock Name="OutputBlock"  Foreground="White" FontFamily="Consolas" Padding="10"/>
    </ScrollViewer>
  </Grid>

TraceWindow.xaml.cs:

public TraceWindow(TraceWindowModel context)
{
  DataContext = context;
  InitializeComponent();

  //bind MyBindingPathProperty to AddTrace
  Binding binding = new Binding("AddTrace");
  binding.Source = context;
  this.SetBinding(MyBindingPathProperty, binding);
}

public static readonly DependencyProperty MyBindingPathProperty =
        DependencyProperty.Register("MyBindingPath", typeof(string), typeof(TraceWindow), new UIPropertyMetadata(null, OnPropertyChanged));



private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
  (sender as TraceWindow).OutputBlock.Inlines.Add(new Run(e.NewValue.ToString()));
}
于 2019-12-11T10:06:27.633 に答える
0
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を初期化し、既存のコンテキストをクリアします。

于 2013-01-06T23:48:20.267 に答える
0

誰もが良い解決策を与えましたが、私は同様の問題を抱えていました。何時間も解決策を探した後、デフォルトのコンテンツに直接バインドしてみることにしました。依存関係プロパティなし。申し訳ありませんが私の時代遅れの英語...hehehehe

[ContentProperty("Inlines")]
public partial class WindowControl : UserControl
{
    public InlineCollection Inlines { get => txbTitle.Inlines; }
}

OK、これをxamlファイルで使用しましょう...

<local:WindowControl>
    .:: Register Logbook : Connected User - <Run Text="{Binding ConnectedUser.Name}"/> ::.
</local:WindowControl>

そして出来上がり!

インラインでバインドする必要がないため、バインドせずに別のコントロールコンテンツからテキストの一部を変更できます。このソリューションは役に立ちます。

于 2020-07-24T19:24:01.767 に答える