我在.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基于我从dstran在Expandable WinForms TextBox中的响应中看到的示例代码。这似乎是建议的最完整实施,被标记为该帖子的答案。这是代码:
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增大时,我需要以某种方式向下推第二个TextBox。面板有什么好方法可以知道ExpandoField何时获得OnSizeChanged事件吗?这样看来,该面板的增长将需要使其余面板列表在较低位置重新绘制。我不确定如何使这种级联效果正常工作...
我也认为使用计时器似乎效率很低……
我仍在学习WinForms。是否有一些精心设计的方法可以使我获得想要的行为?发生自动换行时(或文本超出TextBox的大小时),我是否可以捕获某些事件?那将允许我调整TextBox的大小。以及TextBox如何使面板知道它已更改?它是否需要为其父面板调用OnSizeChanged处理函数?面板是否需要为其父列表调用OnSizeChanged处理函数?
有什么建议吗?
我相信,经过3或4次失败尝试后,我有了答案...
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等修改它应该很简单。)请提出改进建议。