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?
I think you may need some additional code on the PropertyChanged handler, so to initialise the textBlock.Inlines if the bound collection already has content, and to clear any existing context.
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:
I realize this question is very old but I thought I'd share an alternative solution anyway. It utilizes WPF behaviors/attached properties:
In your XAML, use it like this:
This saves you from having to inherit from TextBlock. It could just as well work using an ObservableCollection instead of IEnumerable, in that case you'd need to subscribe to collection changes.
You could add a Dependency Property to a TextBlock Subclass
The Suggestion from Pavel Anhikouski works perfectly. Here the missing part with databinding in MVVM. Use the AddTrace property in the viewmodel to add content to the OutputBlock in the window. The backing property MyBindingPath in the window is not needed.
ViewModel:
TraceWindow.xaml:
TraceWindow.xaml.cs:
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 itsItemsPanel
set to aWrapPanel
and itsItemsSource
set to your collection. (Some experimentation may be required here because anInline
is not aUIElement
, so its default rendering will probably be done usingToString()
rather than being displayed.)Alternatively, you may need to build a new control, e.g.
MultipartTextBlock
, with a bindablePartsSource
property and aTextBlock
as its default template. When thePartsSource
was set your control would attach aCollectionChanged
event handler (directly or via CollectionChangedEventManager), and update theTextBlock.Inlines
collection from code as thePartsSource
collection changed.In either case, caution may be required if your code is generating
Inline
elements directly (because anInline
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 actualInline
objects via aDataTemplate
. This may also improve testability, but obviously adds complexity and effort.