Main purpose of application I'm working on in WPF is to allow editing and consequently printing of songs lyrics with guitar chords over it.
You have probably seen chords even if you don't play any instrument. To give you an idea it looks like this:
E E6
I know I stand in line until you
E E6 F#m B F#m B
think you have the time to spend an evening with me
But instead of this ugly mono-spaced font I want to have Times New Roman
font with kerning for both lyrics and chords (chords in bold font). And I want user to be able to edit this.
This does not appear to be supported scenario for RichTextBox
. These are some of the problems that I don't know how to solve:
- Chords have their positions fixed over some character in lyrics text (or more generally
TextPointer
of lyrics line). When user edits lyrics I want chord to stay over right character. Example:
.
E E6
I know !!!SOME TEXT REPLACED HERE!!! in line until you
- Line wrapping: 2 lines (1th with chords and 2th with lyrics) are logically one line when it comes to wrapping. When a word wraps to next line all chords that are over it should also wrap. Also when chord wraps the word that it is over it also wrap. Example:
.
E E6
think you have the time to spend an
F#m B F#m B
evening with me
- Chords should stay over right character even when chords are too near to each other. In this case some extra space is automatically inserted in lyrics line. Example:
.
F#m E6
...you have the ti me to spend...
- Say I have lyrics line
Ta VA
and chord overA
. I want the lyrics to look like not like . Second picture is not kerned betweenV
andA
. Orange lines are there only to visualize the effect (but they mark x offsets where chord would be placed). Code used to produce first sample is<TextBlock FontFamily="Times New Roman" FontSize="60">Ta VA</TextBlock>
and for second sample<TextBlock FontFamily="Times New Roman" FontSize="60"><Span>Ta V<Floater />A</Span></TextBlock>
.
Any ideas on how to get RichTextBox
to do this ? Or is there better way to do it in WPF? Will I sub-classing Inline
or Run
help? Any ideas, hacks, TextPointer
magic, code or links to related topics are welcome.
Edit:
I'm exploring 2 major directions to solve this problem but both lead to another problems so I ask new question:
- Trying to turn
RichTextBox
into chords editor - Have a look at How can I create subclass of class Inline?. Build new editor from separate components like
Panel
sTextBox
es etc. as suggested in H.B. answer. This would need a lot of coding and also led to following (unsolved) problems:- Components will change their Width/Height according to they layout position (white space removal at line beginning etc.)
- Kerning will have to be inserted manually at components boundaries.
- How to make RichTextBox look like TextBlock? (not elegant hack/workaround is known)
Edit#2
Markus Hütter's high quality answer has shown me that a lot more can be done with RichTextBox
then I expected when I was trying to tweak it for my needs myself. I've had time to explore the answer in details only now. Markus might be RichTextBox
magician I need to help me with this but there are some unsolved problems with his solution as well:
- This application will be all about "beautifully" printed lyrics. The main goal is that the text looks perfect from the typographic point of view. When chords are too near to each other or even overlapping Markus suggests that I iteratively add addition spaces before its position until their distance is sufficient. There is actually requirement that the user can set minimum distance between 2 chords. That minimum distance should be honored and not exceeded until necessary. Spaces are not granular enough - once I add last space needed I'll probably make the gap wider then necessary - that will make the document look 'bad' I don't think it could be accepted. I'd need to insert space of custom width.
- There could be lines with no chords (only text) or even lines with no text (only chords). When
LineHeight
is set to25
or other fixed value for whole document it will cause lines with no chords to have "empty lines" above them. When there are only chords and no text there will be no space for them.
There are other minor problems but I either think I can solve them or I consider them not important. Anyway I think Markus's answer is really valuable - not only for showing me possible way to go but also as a demonstration of general pattern of using RichTextBox
with adorner.