0

I created HighlightTextBox that derived TextBox. And the code is as shown below.

<Style TargetType="{x:Type host:HighlightTextBox}">
    <Setter Property="AcceptsReturn" Value="True" />
    <Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
    <Setter Property="VerticalScrollBarVisibility" Value="Auto" />
    <Setter Property="TextWrapping" Value="NoWrap"/>
    <Setter Property="Foreground" Value="#00000000"/>
    <Setter Property="FontSize" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=FontSize, Mode=TwoWay}"/>

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate x:Name="textArea" TargetType="{x:Type host:HighlightTextBox}">
                <Border BorderThickness="{Binding BorderTickness}"
                            BorderBrush="{Binding BorderBrush}"
                            Background="{Binding BackGround}">

                    <Grid Margin="{TemplateBinding Padding}" x:Name="PART_Grid">
                        <host:TextCanvas x:Name="PART_RenderCanvas" ClipToBounds="True"
                                                    TextOptions.TextRenderingMode="ClearType" TextOptions.TextFormattingMode="Display"
                                                    LineHeight="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=LineHeight}"/>

                        <ScrollViewer x:Name="PART_ContentHost" />
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The important point is ControlTemplate. As you can see, the content of the HighlightTextBox consists of TextCanvas and ScrollViewer.

The HighlightTextBox highlights the currently selected line by painting on the TextCanvas but it invasion the VerticalScrollViewer section as below.

enter image description here

I want to display it by not invasion the VerticalScrollViewer.

I think that maybe the cause is TextCanvas occupy entire section of TextBox. So I tried to move TextCanvas into the ScrollViewer but this way make facing the run-time error that "PART_ContentHost can't have child element.

I think that another way is to get the content width of the TextBox except for the width of the VerticalScrollBar and binding it to the width of the TextCanvas. But I don't know how to get it.

What I should do to solve this problem? If you have a better way or another way to solve then please let me know.

Thank you for reading.

jjw
  • 282
  • 3
  • 20

1 Answers1

1

Search the visual tree for your ScrollViewer's content presenter, that will give you the width of the content area itself:

var scrollViewer = yourTextBox.Template.FindName("PART_ContentHost", yourTextBox) as ScrollViewer;
var contentPresenter = UIHelper.FindChild<ScrollContentPresenter>(scrollViewer, String.Empty);
var width = contentPresenter.ActualWidth;

UPDATE: You can bind to the ScrollContentPresenter's content control directly like this:

<Grid Margin="{TemplateBinding Padding}" x:Name="PART_Grid">
    <TextBlock Text="{Binding ElementName=PART_ContentHost, Path=Content.ActualWidth}" Background="CornflowerBlue" VerticalAlignment="Top"/>
    <ScrollViewer x:Name="PART_ContentHost" />
</Grid>

Keep in mind though that this gives you the area of the ScrollViewers content width which, in the example I've given above, will a bit smaller than the TextBlock's width due to the fact that the ScrollViewers content is a TextBoxView with a 2,0,2,0 margin:

enter image description here

To compensate for this you'll probably want to bind your Canvas margin to the TextBoxView margin (which in my case is a TextBlock rather than a Canvas):

<Grid Margin="{TemplateBinding Padding}" x:Name="PART_Grid">
    <TextBlock Text="{Binding ElementName=PART_ContentHost, Path=Content.ActualWidth}"
               Margin="{Binding ElementName=PART_ContentHost, Path=Content.Margin}"
               Background="CornflowerBlue" VerticalAlignment="Top" />
    <ScrollViewer x:Name="PART_ContentHost" Padding="0" Margin="0"/>
</Grid>

This will keep your Canvas's alignment in the parent Grid the same as for the TextBoxView so that everything lines up properly:

enter image description here

(You could also just remove the TextBoxView's margin, but that's probably not what you want).

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • Thank you for your answer. Is there a way to know width in XAML? – jjw Dec 31 '19 at 23:14
  • um... I wrote the code by reference to the above code but I can't find contentPresenter because of the value of the VisualTreeHelper.GetChildrenCount(scrollViewer) is 0. Could you tell me what I did wrong? – jjw Dec 31 '19 at 23:35
  • Answer updated. With respect to your preceding comment, the template has to have been applied first in order for it to work, so it won't work if you do it immediately after `InitializeComponent()`. Try doing it in the control's `SizeChanged` handler instead. – Mark Feldman Dec 31 '19 at 23:59
  • 1
    great! I applied the above content to my application and it works well. Thanks for the detailed explanation. – jjw Jan 01 '20 at 00:35