views:

832

answers:

3

My WPF App receives a stream of messages from a backend service that I need to display in the UI. These messages vary widely and I want to have different visual layout (string formats, colors, Fonts, icons, whatever etc.) for each message.

I was hoping to just be able to create an inline (Run, TextBlock, Italic etc) for each message then somehow put them all in a ObservableCollection<> and using he magic of WPF Data Binding on my TextBlock.Inlines in the UI. I couldn't find how to do this, is this possible?

Thanks in advance!

+1  A: 

This is not possible because the TextBlock.Inlines property is not a dependency property. Only dependency properties can be the target of a data binding.

Depending on your exact layout requirements you may be able to do this using an ItemsControl, with its ItemsPanel set to a WrapPanel and its ItemsSource set to your collection. (Some experimentation may be required here because an Inline is not a UIElement, so its default rendering will probably be done using ToString() rather than being displayed.)

Alternatively, you may need to build a new control, e.g. MultipartTextBlock, with a bindable PartsSource property and a TextBlock as its default template. When the PartsSource was set your control would attach a CollectionChanged event handler (directly or via CollectionChangedEventManager), and update the TextBlock.Inlines collection from code as the PartsSource collection changed.

In either case, caution may be required if your code is generating Inline elements directly (because an Inline can't be used in two places at the same time). You may alternatively want to consider exposing an abstract model of text, font, etc. (i.e. a view model) and creating the actual Inline objects via a DataTemplate. This may also improve testability, but obviously adds complexity and effort.

itowlson
+2  A: 

If i am getting your requirement correctly, you can manually check for the coming messages and for each message you can add an element to TextBlock.Inlines property. It will not take any DataBinding. I have done this with the following:

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()));
    }
viky
What does the XAML look like for your above example?
discorax
+4  A: 

In version 4 of WPF you will be be able to bind to a Run object, which may solve your problem.

I have solved this problem in the past by overriding an ItemsControl and displaying the text as items in the ItemsControl. Look at some of the tutorials that Dr. WPF has done on this kind of stuff: http://www.drwpf.com

timothymcgrath