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

C# 如何从多行文本框中获取包装行?,c#,textbox,multiline,word-wrap,C#,Textbox,Multiline,Word Wrap,在我的windows.forms c#应用程序中,我有一个多行文本框,其中WordWrap=true。将Text属性设置为长字符串后,需要获得通过换行生成的所有行。它与Lines[]属性不同,因为我的文本不包含新行字符。 我已经找到了使用graphics MeasureString函数的解决方案,但考虑到textbox控件已经完成了包装,这似乎有点额外的工作-为什么我还要再做同样的工作? 有没有办法得到文本框包装文本的行 谢谢修改后的答案 我再次检查了Win32 API,意识到这很容易做到。我编

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

谢谢

修改后的答案 我再次检查了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();
原始答案 WinFormTextBox实际上是Windows GDI编辑控件的包装器,它以本机方式处理文本包装。这就是说,即使TextBox保留了一个包装行数组,它也不会被公共API公开,甚至不会被带到托管环境中(如果它这样做了,可以通过反射来检索)。所以你的最佳选择仍然是有限的


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

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);
    }
}
public Form1()
{
初始化组件();
textBox1.Text=“这是我的文本,我想检查如何自动将包装内容作为分隔行!!这是我的文本,我想检查如何自动将包装内容作为分隔行!!”;
}
私有无效按钮1\u单击(对象发送者,事件参数e)
{
bool continueProcess=true;
int i=1;//从零开始,从1开始
int j=0;
列表行=新列表();
while(持续过程)
{
var index=textBox1.GetFirstCharIndexFromLine(i);
如果(索引!=-1)
{
添加(textBox1.Text.Substring(j,index-j));
j=指数;
i++;
}
其他的
{
添加(textBox1.Text.Substring(j,textBox1.Text.Length-j));
持续过程=假;
}
}
foreach(行中的var项目)
{
MessageBox.Show(项目);
}
}

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

GetFirstCharIndexFromLine返回 物理线路。物理行是显示的行,而不是 指定行。显示的行数可以大于 由于换行而指定的行数。例如,如果您指定 两条长线连接到RichTextBox控件,并设置多行和换行 若为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());
}

只是假设,但我认为测量字符串可能是最简单的方法:/。。。特别是当您已经有了解决方案时。文本框的用途是显示文本并允许文本输入,但不处理文本。TextBox无法提供您所需的信息Olivier:但我不想处理文本。我想知道这个框是如何显示文本的,即第一行是什么,第二行是什么,等等。明白了,谢谢。是否有可能至少得到确切的行数?我再次查看了编辑控制文档,我可能错了。实际上,您应该能够向编辑控件发送EM_FMTLINES消息,然后发送WM_GETTEXT消息来检索包装的文本。当我有时间的时候,我会做一些实验并修改我的答案。嗨,请查看我修改后的答案谢谢你hillin,我测试了它,它工作得非常好!我将在我的项目中使用它。@torno我相信它是可行的,尽管要复杂得多。读:好极了!谢谢!完美的比使用API调用或测量宽度要好得多。谢谢
    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);