To avoid this being an X-Y problem I will explain what I want to achieve first, and then what I tried to achieve it and failed.
Problem: I have a bunch of texts which I am searching through. The search produces a number of hits from different texts, when I click a hit, I want the viewer to open the text, bolden the match of the search pattern, and scroll to the location of the that text automatically. All while abiding by MVVM pattern and not breaking it with using UI elements in the ViewModel, as much as possible. I am using Caliburn Micro as the MVVM framework. Finally, the texts are in Arabic.
What I tried:
ListBox: while it supports scrolling and formatting, it breaks the text into list items which does not allow for selection of multiple lines. If I combine the text into one item, I lose the ability to format and scroll. So I quickly discarded it.
TextBox: it lacks the ability to format a specific part of the text while leaving the rest unformatted, and it does not support scrolling to a specific location.
RichTextBox (native, and Extended WPF): It can be tweaked to allow for scrolling with binding. But, it is horrendous for Arabic language, the available parsers I found were incapable of producing RTF text for Arabic.
ScrollView & TextBlock: I found code that extends it to allow selection and copy of text, as well as the ability to bind to the formatting text. I can get it to highlight the part I need with ease, and I can select and copy from it as well. The issue is, I cannot dynamically determine its height and bind to it so I can scroll to the proper location.
The 4th option is the one I am currently employing, and here is the XAML:
<Border Grid.Row="1" Grid.Column="4" BorderThickness="0.5" BorderBrush="Gray" Margin="5">
<ScrollViewer local:Attached.VerticalOffset="{Binding ViewerScrollOffset, Mode=TwoWay}">
<local:SelectableTextBlock Margin="5" local:Attached.FormattedText="{Binding DisplayText}"
FlowDirection="RightToLeft" TextWrapping="Wrap" />
</ScrollViewer>
</Border>
As shown above, I am using 2 attached properties: VerticalOffset
for ScrollViewer
and FormattedText
for SelectableTextBlock
. (sources linked in the keywords).
I can scroll to locations in the ScrollViewer
, but given the height varies by the size of the text in the TextBlock
, it is not possible to tell where to go without knowing the full height. I am aware of the ScrollableHeight
property which can be accessed from the code behind, but it will break the MVVM pattern and I was wishing to have a solution that can achieve this with binding properly. I tried binding to Height
of both ScrollViewer
and TextBlock
in several ways (changing mode, getting ancestor height, using triggers, etc.) but it does not work and I don't think it is even the correct property to retrieve.
How can I bind to ScrollableHeight
so I can calculate exactly where my VerticalOffset
needs to be? And are there better methods that I am oblivious to which can achieve the problem I stated in the start of the question?