使用两个直接重叠的文本框非常容易。后面的是一个普通的文本框,带有额外的填充和透明文本。前面的有你的额外字符,但它的边框隐藏了,是IsHitTestVisible=False 和Focusable=False,所以它不会与用户交互。用户只与后文本框交互,但前文本框是显示文本的文本框。带有值转换器的绑定使前面的文本框准确地显示后面的文本框显示的内容,以及额外的字符。
这就是它的样子:
<ControlTemplate x:Key="TextBoxWithExtraCharacters" TargetType="{x:Type TextBox}">
<ControlTemplate.Resources>
<!-- Remove the border from the inner textboxes -->
<ControlTemplate TargetType="{x:Type TextBox}">
<Decorator x:Name="PART_Content" />
</ControlTemplate>
</ControlTemplate.Resources>
<!-- Now add our own border -->
<Border
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true">
<!-- Scrolling must happen at this level so both text boxes scroll simultaneously -->
<ScrollViewer>
<Grid>
<!-- Rear textbox provides editing and user interaction but the text is transparent -->
<TextBox
Margin="10,0,10,0"
Foreground="Transparent"
Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}"
Background="{x:Null}"
IsReadOnly="{TemplateBinding IsReadOnly}"
IsUndoEnabled="{TemplateBinding IsUndoEnabled}"
AcceptsReturn="{TemplateBinding AcceptsReturn}"
AcceptsTab="{TemplateBinding AcceptsTab}"
/>
<!-- Front textbox displays modified text but does not interact with user -->
<TextBox
IsHitTestVisible="false"
Focusable="false"
Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent},
Converter={x:Static ExtraCharacterConverter.Instance}"
Background="{x:Null}"
IsReadOnly="{TemplateBinding IsReadOnly}"
IsUndoEnabled="{TemplateBinding IsUndoEnabled}"
AcceptsReturn="{TemplateBinding AcceptsReturn}"
AcceptsTab="{TemplateBinding AcceptsTab}"
/>
</Grid>
</ScrollViewer>
</Border>
</ControlTemplate>
ExtraCharacterConverter 是一个简单的 IValueConverter 类,它通过获取给定的字符串、附加引号或 // 或其他任何内容并返回结果来实现 Convert 方法。
请注意,我在后面的文本框中硬编码了 10 个单位的左右边距,它假定引号字符的特定宽度。这应该正是添加字符的宽度,以使文本正确排列。你想把它弄好,否则你的插入符号和文本选择定位将是错误的。另请注意,正确的值会随着您改变字体大小和选择的额外字符而改变。
硬编码边距的一种简单替代方法是将其设置为FontSize, FontFamily, FontWeight 上的多重绑定等,然后使用IMultiValueConverter 来计算给定这些值的适当边距。
注意:当涉及到文本选择的配色方案时,这个解决方案有点让人不满意。这可以修复,但需要更复杂的解决方案:后面的文本框是一样的,但它的文本不是不可见的。前面的文本框被替换为 RichTextBox(或 TextBlock),其内容被动态计算为带有额外字符的文本,但常规文本是透明的。因为它是一个 RichTextBox,所以多余的字符是可见的,而其他字符是透明的。