C# 为什么TextBox.GetFirstCharIndexFromLine总是返回0?

C# 为什么TextBox.GetFirstCharIndexFromLine总是返回0?,c#,winforms,textbox,C#,Winforms,Textbox,我为TextBox创建了一个子类,并在一个单独的测试项目中测试了以下方法 internal static int NumberOfPhysicalLinesInTextBox(TextBox tb) { int lc = 0; while (tb.GetFirstCharIndexFromLine(lc) != -1) { ++lc; } return lc; } 代码运行得很好,但在我的子类中却不行。上述静态方法仅从方法UpdateVi

我为TextBox创建了一个子类,并在一个单独的测试项目中测试了以下方法

internal static int NumberOfPhysicalLinesInTextBox(TextBox tb)
{
    int lc = 0;
    while (tb.GetFirstCharIndexFromLine(lc) != -1)
    {
        ++lc;
    }
    return lc;
}
代码运行得很好,但在我的子类中却不行。上述静态方法仅从方法
UpdateVisibleScrollBars
调用,该方法仅在以下位置调用:

  • 来自子类“c-tor”
  • OnTextChanged
  • OnFontChanged
  • OnResize
这个子类的唯一特点是,当用户没有在文本框中输入任何内容时,它有一个占位符,并且这个
UpdateVisibleScrollBars
。在这个子类中,
numberOfPhysicalCallinesIntextBox
不返回,它无限循环,因为当文本是占位符时,
GetFirstCharIndexFromLine
总是返回0:“在此处输入文本…”

更新:我不使用行,因为我需要物理行(换行后产生的行),因此我可以知道是否需要显示或隐藏垂直滚动条。文本框设置为
WordWrap=true
。这是该方法的官方文档

更新2:所有课程代码如下(无非英语注释):


为什么需要
while(true)
循环?您不能改用
行[]
数组吗?它有一个
Length
属性…@Jimi我更新了这个问题。
while(true)
是无用的,我说了为什么我不使用
行[]
。然后,可能需要
GetPositionFromCharIndex
,它返回一个
点。字符可以是最后一个(
[TextBoxBase].Text.Length-1
Point.Y
可以是
[Control].Height
。你在找什么?你能发布所有的测试代码吗?这可能类似于调用该方法的递归问题,而不是将其卡在while循环中。您可以添加日志记录或使用中断来确保只调用一次而不返回吗?为什么需要
while(true)
循环?您不能改用
行[]
数组吗?它有一个
Length
属性…@Jimi我更新了这个问题。
while(true)
是无用的,我说了为什么我不使用
行[]
。然后,可能需要
GetPositionFromCharIndex
,它返回一个
点。字符可以是最后一个(
[TextBoxBase].Text.Length-1
Point.Y
可以是
[Control].Height
。你在找什么?你能发布所有的测试代码吗?这可能类似于调用该方法的递归问题,而不是将其卡在while循环中。您可以添加日志记录或使用中断来确保只调用一次而不返回吗?
class EnhancedTextBox : TextBox
{
    internal string PlaceholderText = "Enter text here...";

    internal string ActualText
    {
        get
        {
            return PlaceholderShown ? "" : Text;
        }
        set
        {
            if (value == "" || value == null)
            {
                if (Text == PlaceholderText)
                {
                    PlaceholderShown = true;

                    ActualTextChanged?.Invoke(this, EventArgs.Empty);
                }
                else
                {
                    if (!Focused)
                    {
                        BeforeActualTextChanged?.Invoke(this, EventArgs.Empty);

                        ProgrammaticTextChange = true;
                        Text = PlaceholderText;
                        ProgrammaticTextChange = false;

                        PlaceholderShown = true;

                        ActualTextChanged?.Invoke(this, EventArgs.Empty);
                    }
                    else
                    {
                        PlaceholderShown = false;

                        ActualTextChanged?.Invoke(this, EventArgs.Empty);
                    }
                }
            }
            else
            {
                if (Text != value)
                {
                    BeforeActualTextChanged?.Invoke(this, EventArgs.Empty);

                    ProgrammaticTextChange = true;
                    Text = value;
                    ProgrammaticTextChange = false;
                }

                PlaceholderShown = false;

                ActualTextChanged?.Invoke(this, EventArgs.Empty);
            }
        }
    }

    internal Color _PlaceholderForeColor = Utils.GrayByPercent(50);
    internal Color PlaceholderForeColor
    {
        get
        {
            return _PlaceholderForeColor;
        }
        set
        {
            if (_PlaceholderForeColor != value)
            {
                _PlaceholderForeColor = value;
                Invalidate();
            }
        }
    }

    internal Color _NormalForeColor = Color.Empty;
    internal Color NormalForeColor
    {
        get
        {
            return _NormalForeColor;
        }
        set
        {
            if (_NormalForeColor != value)
            {
                _NormalForeColor = value;
                Invalidate();
            }
        }
    }

    internal bool _PlaceholderShown = true;
    internal bool PlaceholderShown
    {
        get
        {
            return _PlaceholderShown;
        }
        set
        {
            if (_PlaceholderShown != value)
            {
                _PlaceholderShown = value;
                ForceUpdatePlaceholderShown(value);
            }
        }
    }

    internal void ForceUpdatePlaceholderShown(bool value)
    {
        ForeColor = value ? PlaceholderForeColor :
                    NormalForeColor;
        Invalidate();
    }

    public EnhancedTextBox() : base()
    {
        NormalForeColor = ForeColor;

        ProgrammaticTextChange = true;
        Text = PlaceholderText;
        ProgrammaticTextChange = false;

        PlaceholderShown = true;
        ForceUpdatePlaceholderShown(true);

        UpdateVisibleScrollBars();
    }

    protected override void OnEnter(EventArgs e)
    {
        HidePlaceholder();

        base.OnEnter(e);
    }

    private void HidePlaceholder()
    {
        if (PlaceholderShown)
        {
            ProgrammaticTextChange = true;
            Text = "";
            ProgrammaticTextChange = false;

            PlaceholderShown = false;
        }
    }

    protected override void OnLeave(EventArgs e)
    {
        ShowPlaceholder();

        base.OnLeave(e);
    }

    private void ShowPlaceholder()
    {
        if (Text == "")
        {
            ProgrammaticTextChange = true;
            Text = PlaceholderText;
            ProgrammaticTextChange = false;

            PlaceholderShown = true;
        }
    }

    internal static int NumberOfPhysicalLinesInTextBox(TextBox tb)
    {
        int lc = 0;
        while (tb.GetFirstCharIndexFromLine(lc) != -1)
        {
            ++lc;
        }
        return lc;
    }

    internal bool ProgrammaticTextChange = false;

    /// <summary>
    /// Do not use this event using handlers. Use ActualTextChanged instead.
    /// </summary>
    /// <param name="e"></param>
    protected override void OnTextChanged(EventArgs e)
    {
        if (ProgrammaticTextChange)
        {
            return;
        }

        ActualText = Text;

        base.OnTextChanged(e);

        UpdateVisibleScrollBars();
    }

    private bool busy = false;
    private void UpdateVisibleScrollBars()
    {
        if (busy) return;
        busy = true;

        bool c1 = false; // chars == Text.Length; // TODO: this not working for WordWrap = false
        bool c2 = NumberOfPhysicalLinesInTextBox(this) > 2;

        if (c1 && c2)
        {
            ScrollBars = ScrollBars.Both;
        }
        else if (c1)
        {
            ScrollBars = ScrollBars.Horizontal;
        }
        else if (c2)
        {
            ScrollBars = ScrollBars.Vertical;
        }
        else
        {
            ScrollBars = ScrollBars.None;
        }
        ScrollToCaret();

        busy = false;
    }

    protected override void OnFontChanged(EventArgs e)
    {
        base.OnFontChanged(e);

        UpdateVisibleScrollBars();
    }

    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);

        UpdateVisibleScrollBars();
    }

    public event EventHandler ActualTextChanged, BeforeActualTextChanged;
}
class EnhancedTextBox : TextBox
{
    internal string _PlaceholderText = "Enter text here...";
    internal string PlaceholderText
    {
        get
        {
            return _PlaceholderText;
        }
        set
        {
            _PlaceholderText = value;
            Invalidate();
        }
    }

    internal Color _PlaceholderForeColor = SystemColors.GrayText;
    public Color PlaceholderForeColor
    {
        get
        {
            return _PlaceholderForeColor;
        }
        set
        {
            _PlaceholderForeColor = value;
            Invalidate();
        }
    }

    [Obsolete]
    internal string ActualText
    {
        get
        {
            return Text;
        }
        set
        {
            if (Text != value)
            {
                Text = value;
            }
        }
    }

    internal Color _NormalForeColor = Color.Empty;
    internal Color NormalForeColor
    {
        get
        {
            return _NormalForeColor;
        }
        set
        {
            if (_NormalForeColor != value)
            {
                _NormalForeColor = value;
                ForeColor = value;
            }
        }
    }

    public EnhancedTextBox() : base()
    {
        NormalForeColor = ForeColor;

        WordWrap = true;
    }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);

        if (m.Msg == 0xf)
        {
            if (!this.Focused && string.IsNullOrEmpty(this.Text)
                && !string.IsNullOrEmpty(this.PlaceholderText))
            {
                using (var g = this.CreateGraphics())
                {
                    TextRenderer.DrawText(g, this.PlaceholderText, this.Font,
                        this.ClientRectangle, this.PlaceholderForeColor, this.BackColor,
                         TextFormatFlags.Top | TextFormatFlags.Left);
                }
            }
        }
    }

    internal static int NumberOfPhysicalLinesInTextBox(TextBox tb)
    {
        int lc = 0;
        while (tb.GetFirstCharIndexFromLine(lc) != -1)
        {
            ++lc;
        }
        return lc;
    }

    internal bool ProgrammaticTextChange = false;

    protected override void OnTextChanged(EventArgs e)
    {
        base.OnTextChanged(e);

        ActualTextChanged?.Invoke(this, e);

        UpdateVisibleScrollBars();
    }

    private bool busy = false;
    private Size textSize = Size.Empty;
    private void UpdateVisibleScrollBars()
    {
        if (busy) return;
        busy = true;

        bool c1 = false; // chars == Text.Length; // TODO: this not working for WordWrap = false
        bool c2 = NumberOfPhysicalLinesInTextBox(this) > 2;

        if (c1 && c2)
        {
            ScrollBars = ScrollBars.Both;
        }
        else if (c1)
        {
            ScrollBars = ScrollBars.Horizontal;
        }
        else if (c2)
        {
            ScrollBars = ScrollBars.Vertical;
        }
        else
        {
            ScrollBars = ScrollBars.None;
        }
        ScrollToCaret();

        busy = false;
    }

    protected override void OnFontChanged(EventArgs e)
    {
        base.OnFontChanged(e);

        UpdateVisibleScrollBars();
    }

    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);

        //UpdateVisibleScrollBars();
    }

    [Obsolete]
    public event EventHandler ActualTextChanged;
}