Edit: I discovered in a different answer that the TextBlock
also has an Inlines
collection to which one can add Run
s. Anvaka's answer ingeniously uses an attached property as a sort of converter.
I had a couple ideas about how to approach this. Only one would handle word wrap correctly and handle character runs with different fonts.
Use a FlowDocumentScrollViewer
and bind the string to it using a ValueConverter
that converts the string to a FlowDocument
.
<FlowDocumentScrollViewer
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
Document="{Binding MyString, Converter={StaticResource MyConverter}}" />
You can create properties on the converter to set the regular Font properties, which can't be set on the FlowDocumentScrollViewer
and has to be set on the FlowDocument
that the converter creates. You'll probably also need some Font properties for the exception substrings that need a different Font (and possibly different size). Another option is to create Binding
s on the FlowDocument
for some of those properties (RelativeSource
).
And here's how you create a FlowDocument
in code:
FlowDocument doc = new FlowDocument();
doc.FontFamily = new FontFamily( "Our Own Font" );
Paragraph par = new Paragraph();
doc.Blocks.Add( par );
Then you'll need to split the incoming string on the special substrings while keeping those substrings intact. You'll have to roll your own splitter and have a collection of substrings in the converter or supplied to the converter.
Add a normal substring to the paragraph:
Run r = new Run( substring );
par.Inlines.Add( r );
Add a special substring to the paragraph with a different Font:
Run r = new Run( substring );
r.FontFamily = new FontFamily( "Arial" );
par.Inlines.Add( r );
The above are just little snippets. I don't know how you want to approach splitting the string or iterating over the substrings since I'm not familiar with the data, so I didn't supply the method I slapped together just to see if my idea would work. You could also use a Dictionary
in order to detect one substring and use a replacement in the output, such as detecting "(SM)"
and replacing it with "℠"
.
Let me know if you have any questions or if there's anything I can elaborate on.
(It's good that you said it is read-only. A RichTextBox
would not work because its Document
property is not a DependencyProperty
and therefore can't be the target of a Binding
. Although, one might be able to use Mode=OneWayToSource
to reverse it, implementing ConvertBack
instead of Convert
.)
"I think what you left out (iterating over the string and creating those Runs) is the tricky part."
I was indeed very brief about splitting the string on the special substrings. When I said, "I don't know how you'll split the string," I was not saying that I had no idea how to do it at all (I rephrased it), but that I had no idea how you would want to handle it. I assumed it would not be hard for you to figure that part out because it's the kind of string manipulation problem I would have potential hires figure out. That, and you may discover edge cases amongst your data that require changes in how you handle it.
I'll describe to you a relatively crude version using IndexOf()
and Substring()
.
So here's the problem-within-the-problem: You have many strings (e.g., "Company Name(R), makers of Product(TM)"
) that contain 0 or more special substrings. These substrings are few and known, and the input string needs to be split into several strings where the special and nonspecial substrings have been isolated from each other (e.g., {"Company Name", "(R)", ", makers of Product", "(TM)"}
).
The special substrings are few and known, so you need an array of those. You know by the return value of .IndexOf()
whether it found a substring or not. Looping over the known special substrings, you can find the first instance of any of them with an index comparison, setting aside the length of the substring, too.
Each time you find the earliest special substring in string S
(if any), you make derived strings A
, B
, and C
. B
is the special substring, A
and C
are the before and after. Append A
and B
to a List
and C
becomes the new S
and you do it again. Except when you find no special substrings in S
and it isn't empty, in which case you just append it whole.
Now every odd-indexed string in the List
is a special substring. You may want to refer to my mention of a Dictionary
in the previous portion of this answer for use as a lookup to add a Run
of "℠"
when the substring you found was "(SM)"
.