如何从多行文本框获取换行



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

谢谢

你能检查下面的解决方案吗?

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

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

GetFirstCharIndexFromLine 返回 物理线。物理线是显示的线,而不是 分配的行。显示的行数可以大于 由于自动换行而分配的行数。例如,如果您分配 两行长行到富文本框控件,并设置多行和自动换行 如果为 true,则两条长分配的行会产生四个物理(或 显示的行)。

稍微调听一下就可以了:

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());
}

修订后的答案

我再次检查了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。

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

最新更新