我在我的.NET WinForms应用程序中有记录,当记录可编辑时,我在面板上设置了增强的TextBox控件,但当记录不可编辑时,我将TextBoxes设置为ReadOnly。单击可编辑记录上的“保存”按钮将文本保存到数据库,然后将其显示为不可编辑的记录(直到单击“编辑”按钮)。请查看以下屏幕抓取:

正如您希望看到的,第一条记录是不可编辑的,但第二条是可编辑的。我的问题是,如果文本太多,我希望TextBox在高度上增长。看起来TextBox正在执行WordWrap,但是它要么只显示文本的一行,要么只显示前两行。有些东西总是在底部被切断。
我看过这个网站上的其他几篇文章,特别是Expandable WinForms TextBox。
下面是面板的一些示例代码:
AutoSize = true;
AutoSizeMode = AutoSizeMode.GrowAndShrink;
...
Field1 = new ExpandoField { Multiline = true, WordWrap = true };
Field1.Location = new System.Drawing.Point(42, 3);
if (CanEdit)
{
Field1.BackColor = System.Drawing.Color.White;
Field1.TabIndex = 20;
}
else
{
((ExpandoField) Field1).ReadOnly = true;
Field1.ForeColor = System.Drawing.Color.FromArgb(0, 50, 0);
Field1.BackColor = System.Drawing.Color.Snow;
Field1.TabIndex = 0;
Field1.TabStop = false;
}
Field1.Text = Text1;
Field1.Dock = DockStyle.None;
Field1.Size = new System.Drawing.Size(538 - 25, 34);
Field1.MinimumSize = Field1.Size;
Field1.AutoSize = true;
Controls.Add(Field1);如您所见,对于面板,我已经将AutoSize设置为true。Field2的代码类似于Field1。
ExpandoField基于我从Expandable WinForms TextBox中的dstran的响应中看到的示例代码。这似乎是对这一职位的答案的建议的最全面的执行。下面是代码:
class ExpandoField : TextBox
{
private double m_growIndex = 0.0;
private Timer m_timer;
public ExpandoField()
{
AutoSize = false;
this.Height = 20;
// Without the timer, I got a lot of AccessViolationException in the System.Windows.Forms.dll.
m_timer = new Timer();
m_timer.Interval = 1;
m_timer.Enabled = false;
m_timer.Tick += new EventHandler(m_timer_Tick);
this.KeyDown += new KeyEventHandler(ExpandoField_KeyDown);
}
void ExpandoField_KeyDown(object sender, KeyEventArgs e)
{
if (e.Modifiers == Keys.Control && e.KeyCode == Keys.A)
this.SelectAll();
}
void m_timer_Tick(object sender, EventArgs e)
{
var sz = new System.Drawing.Size(Width, Int32.MaxValue);
sz = TextRenderer.MeasureText(Text, Font, sz, TextFormatFlags.TextBoxControl);
m_growIndex = (double)(sz.Width / (double)Width);
if (m_growIndex > 0)
Multiline = true;
else
Multiline = false;
int tempHeight = (int) (20 * m_growIndex);
if (tempHeight <= 20)
Height = 20;
else
Height = tempHeight;
m_timer.Enabled = false;
}
public override sealed bool AutoSize
{
get { return base.AutoSize; }
set { base.AutoSize = value; }
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
m_timer.Enabled = true;
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
m_timer.Enabled = true;
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
m_timer.Enabled = true;
}
}这显然不是很有效。我已经将面板设置为AutoSize,但它并不能容纳第二个TextBox。另外,当第一个TextBox增长时,我需要以某种方式降低第二个。有什么好方法让面板知道ExpandoField什么时候得到OnSizeChanged事件?看来,该面板的增长将需要导致面板列表的其余部分被重新绘制在较低的位置。我不知道怎样才能让这个级联效应正常运作.
我还认为计时器的使用似乎是低效的kluge.
我还在学习WinForms。有什么精心设计的方法能让我得到我想要的行为吗?当WordWrap发生时(或者当文本超过TextBox的大小时),我能捕捉到什么事件吗?这将允许我调整TextBox的大小。TextBox是如何让面板知道它已经改变的呢?它是否需要为它的父面板调用OnSizeChanged处理程序?该面板是否需要为其父列表调用OnSizeChanged处理程序?
有什么建议吗?
发布于 2020-01-23 04:04:33
我相信我有答案了,在三、四次失败之后.
class ExpandoField : TextBox
{
private bool UpdateInProgress = false;
private static System.Text.RegularExpressions.Regex rgx = new System.Text.RegularExpressions.Regex(@"\r\n");
public delegate void CallbackFn();
CallbackFn VSizeChangedCallback;
public ExpandoField(CallbackFn VSizeChanged)
{
AutoSize = false;
VSizeChangedCallback = VSizeChanged;
this.KeyDown += new KeyEventHandler(ExpandoField_KeyDown);
}
public void ExpandoField_KeyDown(object sender, KeyEventArgs e)
{
if (e.Modifiers == Keys.Control && e.KeyCode == Keys.A)
this.SelectAll();
}
public void UpdateSize()
{
if (UpdateInProgress == false && Text.Length > 0)
{
UpdateInProgress = true;
int numLines = 0;
System.Drawing.Size baseSize = new System.Drawing.Size(Width, Int32.MaxValue);
System.Drawing.Size lineSize = baseSize; // compiler thinks we need something here...
// replace CR/LF with single character (paragraph mark '¶')
string tmpText = rgx.Replace(Text, "\u00B6");
// split text at paragraph marks
string[] parts = tmpText.Split(new char[1] { '\u00B6' });
numLines = parts.Count();
foreach (string part in parts)
{
// if the width of this line is greater than the width of the text box, add needed lines
lineSize = TextRenderer.MeasureText(part, Font, baseSize, TextFormatFlags.TextBoxControl);
numLines += (int) Math.Floor(((double) lineSize.Width / (double) Width));
}
if (numLines > 1)
Multiline = true;
else
Multiline = false;
int tempHeight = Margin.Top + (lineSize.Height * numLines) + Margin.Bottom;
if (tempHeight > Height || // need to grow...
Height - tempHeight > lineSize.Height) // need to shrink...
{
Height = tempHeight;
VSizeChangedCallback();
}
UpdateInProgress = false;
}
}
public override sealed bool AutoSize
{
get { return base.AutoSize; }
set { base.AutoSize = value; }
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
UpdateSize();
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
UpdateSize();
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
UpdateSize();
}
}注意,在构造函数上,TextBox的这个子类现在接受一个委托回调,以让父类知道TextBox已经改变了它的大小。(我想我应该在这里处理空值的可能性.)
谢天谢地,这个解决方案不再需要计时器。
我已经很好地测试了这段代码,并且看到了它的成长和收缩。它尊重MaximumSize,甚至处理运输-返回/行-馈送对的存在。(这段代码假设Windows;为Linux等修改它应该是很简单的。)请随意提出改进建议。
https://stackoverflow.com/questions/59828499
复制相似问题