如何从多行文本框中获取包装线?

时间:2016-04-22 18:31:36

标签: c# textbox multiline wrapping

在我的windows.forms c#应用程序中,我有一个WordWrap = true的多行文本框。在我将Text属性设置为长字符串后,我需要通过包装生成所有行。它与Lines []属性不同,因为我的文本不包含换行符。 我找到了使用图形MeasureString函数的解决方案,但考虑到文本框控件已经完成了包装,它似乎有点额外的工作 - 为什么我应该再做同样的工作? 有没有办法获得文本框包装文本的行?

谢谢

4 个答案:

答案 0 :(得分:5)

您可以查看以下解决方案,

public Form1()
{
    InitializeComponent();
    textBox1.Text = "This is my text where I want to check how I can get wrapped content as seperate lines automatically !! This is my text which I want to check how I can get wrapped content as seperate lines automatically !!";
}

private void button1_Click(object sender, EventArgs e)
{
    bool continueProcess = true;
    int i = 1; //Zero Based So Start from 1
    int j = 0;
    List<string> lines = new List<string>();
    while (continueProcess)
    {
        var index = textBox1.GetFirstCharIndexFromLine(i);
        if (index != -1)
        {
            lines.Add(textBox1.Text.Substring(j, index - j));
            j = index;
            i++;
        }
        else
        {
            lines.Add(textBox1.Text.Substring(j, textBox1.Text.Length - j));
            continueProcess = false;
        }
    }
    foreach(var item in lines)
    {
        MessageBox.Show(item);
    }
}

GetFirstCharIndexFromLine Reference

  

文本框中的行编号从零开始。如果是lineNumber   参数大于文本框中的最后一行,   GetFirstCharIndexFromLine返回-1。

     

GetFirstCharIndexFromLine返回a的第一个字符索引   物理线。物理线是显示的线,而不是   分配线。显示的行数可以大于   由于自动换行而分配的行数。例如,如果您指定   两个长行到RichTextBox控件并设置Multiline和WordWrap   为true,两个长指定的行导致四个物理(或   显示的行)。

答案 1 :(得分:1)

已修订的答案

我再次检查了Win32 API并意识到它可以轻松完成。我写了一个扩展方法,所以你可以更容易地做到:

using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    static class TextBoxExtensions
    {
        private const uint EM_FMTLINES = 0x00C8;
        private const uint WM_GETTEXT = 0x000D;
        private const uint WM_GETTEXTLENGTH = 0x000E;

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, IntPtr lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);

        public static string[] GetWrappedLines(this TextBox textBox)
        {
            var handle = textBox.Handle;
            SendMessage(handle, EM_FMTLINES, 1, IntPtr.Zero);

            var size = SendMessage(handle, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero).ToInt32();

            if (size > 0)
            {
                var builder = new StringBuilder(size + 1);
                SendMessage(handle, WM_GETTEXT, builder.Capacity, builder);
                return builder.ToString().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
            }

            return new string[0];
        }
    }
}

用法:

var lines = textBox1.GetWrappedLines();

原始答案

  

WinForm TextBox实际上是Windows GDI编辑控件的包装器,它本身处理文本换行。话虽这么说,即使TextBox保留了一系列包装线,它也不会被公共API暴露,甚至不会被带到托管环境(如果确实如此,它可以通过反射检索)。所以你最好的选择仍然是MeasureString。

答案 2 :(得分:1)

有点刺激可行:

private const UInt32 EM_GETLINECOUNT = 0xba;

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

private void button1_Click(object sender, EventArgs e) {
  int numLines = SendMessage(textBox1.Handle,
                             EM_GETLINECOUNT, IntPtr.Zero, IntPtr.Zero).ToInt32()
  MessageBox.Show(numLines.ToString());
}

答案 3 :(得分:0)

    To check if particular line is wrapped or not, here is the GDI Function you need to use:
    1. [DllImport("user32.dll")]
    static extern int DrawText(IntPtr hdc, string lpStr, int nCount, ref Dimension lpRect, int wFormat);

    Here are what you need to get things done:
      public enum DrawTextFlags
        {
            CalculateArea = 0x00000400,
            WordBreak = 0x00000010,
            TextBoxControl = 0x00002000,
            Top = 0x00000000,
            Left = 0x00000000,
            HorizontalCenter = 0x00000001,
            Right = 0x00000002,
            VerticalCenter = 0x00000004,
            Bottom = 0x00000008,
            SingleLine = 0x00000020,
            ExpandTabs = 0x00000040,
            TabStop = 0x00000080,
            NoClipping = 0x00000100,
            ExternalLeading = 0x00000200,
            NoPrefix = 0x00000800,
            Internal = 0x00001000,
            PathEllipsis = 0x00004000,
            EndEllipsis = 0x00008000,
            WordEllipsis = 0x00040000,
            ModifyString = 0x00010000,
            RightToLeft = 0x00020000,
            NoFullWidthCharacterBreak = 0x00080000,
            HidePrefix = 0x00100000,
            PrefixOnly = 0x00200000,
            NoPadding = 0x10000000,
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct Dimension
        {
            public int Left, Top, Right, Bottom;

            public Dimension(int left, int top, int right, int bottom)
            {
                this.Left = left;
                this.Right = right;
                this.Top = top;
                this.Bottom = bottom;
            }
            public Dimension(Rectangle r)
            {
                this.Left = r.Left;
                this.Top = r.Top;
                this.Bottom = r.Bottom;
                this.Right = r.Right;
            }
            public static implicit operator Rectangle(Dimension rc)
            {
                return Rectangle.FromLTRB(rc.Left, rc.Top, rc.Right, rc.Bottom);
            }
            public static implicit operator Dimension(Rectangle rc)
            {
                return new Dimension(rc);
            }

            public static Dimension Default
            {
                get { return new Dimension(0, 0, 1, 1); }
            }
        }

So to know whether a particular line is wrapped or not, you would call the function like this:
Dimension rc = new Dimension(0,0,2,2);
var flag =  DrawTextFlags.CalculateArea | DrawTextFlags.TextBoxControl | DrawTextFlags.WordBreak;
DrawText(hdc, line, line.length, ref rc, (int)flag);
Now if height of rc you get after executing this function is greater then your font height or tmHeight if you use TextMetric (that is what minimum required for a line to fit vertically) you can safely assume your line is wrapped.
Apart from this,
You can use the following function as an alternative approach:
static extern bool GetTextExtentExPoint(IntPtr hDc, string str, int nLength,
            int nMaxExtent, int[] lpnFit, int[] alpDx, ref Size size);