We have a WPF application that displays text containing the various corporate symbols; such as trademark, registered trademark, copyright, and service mark.
The database has some fields that include the standard corporate symbols. Initially, the data was marked as follows:
Example Corp(TM) or Example Plan (SM)
We can easily change the placeholders to their respective Unicode equivalents; and actually have in most cases.
The problem we have is that the font used by the application doesn't support the Service Mark symbol (which is just a superscripted SM). Chances are we can not replace the font or edit it.
The fields could be a simple product name with the symbol at the end, or a long description with a symbol contained 0 or more times. We bind TextBoxes or Labels directly to a ViewModel and/or the business object (usually through DataTemplates). All the data in the application is read-only.
So, assuming we have to tackle this via code (in C# and WPF), what are my options?
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)"
.
If your display element support multiple fonts like a RichTextbox control, then you can format the string with the appropriate fonts just around the (TM) and (SM) symbols.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With