除了VerticalScrollBar的宽度,如何获取TextBox的内容宽度?

How to get the content width of the TextBox except for width of the VerticalScrollBar?

我创建了派生 TextBox 的 HighlightTextBox。代码如下所示。

<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>

重点是ControlTemplate。 可以看到,HighlightTextBox的内容由TextCanvas和ScrollViewer组成。

HighlightTextBox 通过在 TextCanvas 上绘制来突出显示当前选定的行,但它侵入了 VerticalScrollViewer 部分,如下所示。

我想在不侵入VerticalScrollViewer的情况下显示

我认为可能是 TextCanvas 占据了 TextBox 的整个部分。 所以我尝试将 TextCanvas 移动到 ScrollViewer 中,但这样会面临 运行 时间错误,即“PART_ContentHost 不能有子元素。

我认为另一种方法是获取除VerticalScrollBar 宽度之外的TextBox 的内容宽度并将其绑定到TextCanvas 的宽度。但是不知道怎么获取。

我应该怎么做才能解决这个问题? 如果您有更好的方法或其他解决方法,请告诉我。

感谢您的阅读。

Search the visual tree 对于您的 ScrollViewer 的内容展示器,这将为您提供内容区域本身的宽度:

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

更新:您可以像这样直接绑定到 ScrollContentPresenter 的内容控件:

<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>

请记住,这会为您提供 ScrollViewers content 宽度的区域,在我上面给出的示例中,由于事实上,ScrollViewers 内容是一个具有 2,0,2,0 边距的 TextBoxView:

为了弥补这一点,您可能需要将 Canvas 边距绑定到 TextBoxView 边距(在我的例子中是 TextBlock 而不是 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>

这将使您的 Canvas 在父网格中的对齐方式与 TextBoxView 的对齐方式相同,以便一切都正确排列:

(您也可以只删除 TextBoxView 的边距,但这可能不是您想要的)。