【问题标题】:How to create multiline textbox which shows apostrophe before each line如何创建在每行之前显示撇号的多行文本框
【发布时间】:2009-11-25 14:40:07
【问题描述】:

我想要一个多行文本框,在每行之前和之后显示撇号。 使文本框看起来像:

" Hello this  "
" is a funny  "
" string test "

或者例如:

// This is
// a muliline
// comment.

编辑:这些特殊字符必须是只读的,如果用户从文本框中复制文本,则应该包含这些字符。

【问题讨论】:

    标签: c# .net wpf xaml wpf-controls


    【解决方案1】:

    使用两个直接重叠的文本框非常容易。后面的是一个普通的文本框,带有额外的填充和透明文本。前面的有你的额外字符,但它的边框隐藏了,是IsHitTestVisible=FalseFocusable=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,所以多余的字符是可见的,而其他字符是透明的。

    【讨论】:

      【解决方案2】:

      你不能简单地创建一个用户定义的文本框,使用继承并将多行属性设置为 true,并在文本更改事件上使用一些特殊代码来检查每行的第一个和最后一个索引,以确保它有一个撇号吗?或者如果没有办法迭代行,你可以只看每个 chr(11) 或 chr(13) 之前和之后的字符吗?

      这对于 winforms 来说并不难,但是我不确定 WPF。

      ~~~~~~~~~~~~~~~~~~~~~~~~~编辑 1: 11/25/2009 9:12 AM CT~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~

      如果您希望用户无法编辑或弄乱引号,那么您可以在 KeyPressed 事件处理程序中编写特定代码(或者如果您正在执行继承控件,则覆盖 OnKeyPressed 函数)以防止它们添加chr(11) 或 chr(13) 之前或之后的字符,如果他们试图删除它,则立即将其添加回来,并取消无效的击键。

      我在 VB .NET 中为货币文本框字段编写了与此类似的代码。它可以帮助你理解我在说什么,也可以帮助你做你正在做的事情:

      Protected Overrides Sub OnMouseClick(ByVal e As System.Windows.Forms.MouseEventArgs)
          If Me.SelectionStart = 0 Then
              Me.SelectionStart = Me.Text.Length
          End If
      End Sub
      
      Protected Overrides Sub OnTextChanged(ByVal e As System.EventArgs)
          If Not Me.Text.IndexOf("$") = 0 Then
              Me.Text = "$" + Me.Text.Replace("$", "")
          End If
          If Me.SelectionStart = 0 Then
              Me.SelectionStart = Me.Text.Length
          End If
      End Sub
      
      Protected Overrides Sub OnKeyPress(ByVal e As System.Windows.Forms.KeyPressEventArgs)
          If NOT ((Char.IsDigit(e.KeyChar) Or (e.KeyChar = CChar(".") And Not Me.Text.Contains(".")) Or Char.IsControl(e.KeyChar)) And (Not Char.IsDigit(e.KeyChar) Or (Me.SelectionStart <= Me.Text.IndexOf(".") + 2 Or Me.Text.IndexOf(".") = -1))) Then
              e.Handled = True
          End If
      End Sub
      

      【讨论】:

      • 用户不能操作撇号,它们必须是只读的。
      • 啊!这让它变得更加困难!
      • 我进行了编辑并添加了一些与您要求的类似的代码。可能应该有一种更简单的方法来做到这一点,但总而言之,这可能是变通代码。
      【解决方案3】:

      我想说你最好的办法是实现你自己的文本框(从文本框本身继承作为基础,所以你可以免费获得所有功能)并覆盖绘图代码并自己写出每一行(前/后- 在写出每一行时添加撇号)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-29
        • 1970-01-01
        • 2023-03-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-02
        相关资源
        最近更新 更多