C# winForm RichTextbox、ListBox 每行显示不同的颜色,自动换行等等
在WindowsForm项目开发中,我们有时候会遇到需求,将接收到的信息按照标志位,标记成不同的颜色。这时,普通的TextBox就满足不了需求。RichTextbox和Listox则可以满足需求,两者各有好处。选择哪一种,见仁见智。
RichTextbox 实现每行不同颜色,自动换行等
实现不同的文字,不同的颜色
//文字的起始位置,这里以添加到文本末尾为例
richTextBox_MesInfo.SelectionStart = richTextBox_MesInfo.TextLength;
richTextBox_MesInfo.SelectionColor = Color.Red;
richTextBox_MesInfo.AppendText("红色");
richTextBox_MesInfo.SelectionStart = richTextBox_MesInfo.TextLength;
richTextBox_MesInfo.SelectionColor = Color.Black;
richTextBox_MesInfo.AppendText("黑色");
自动定位到最新的信息
//将滚动条定位到最下方
richTextBox_MesInfo.ScrollToCaret();
自动换行
richTextBox_MesInfo.WordWrap = false;
//wordwrap 属性:指示多行文本控件是否在必要时自动换到下一行的开始处;
设置行间距(博众家之所长)
需要写一个帮助类
class CSetLineSpace
{
public const int WM_USER = 0x0400;
public const int EM_GETPARAFORMAT = WM_USER + 61;
public const int EM_SETPARAFORMAT = WM_USER + 71;
public const long MAX_TAB_STOPS = 32;
public const uint PFM_LINESPACING = 0x00000100;
[StructLayout(LayoutKind.Sequential)]
private struct PARAFORMAT2
{
public int cbSize;
public uint dwMask;
public short wNumbering;
public short wReserved;
public int dxStartIndent;
public int dxRightIndent;
public int dxOffset;
public short wAlignment;
public short cTabCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public int[] rgxTabs;
public int dySpaceBefore;
public int dySpaceAfter;
public int dyLineSpacing;
public short sStyle;
public byte bLineSpacingRule;
public byte bOutlineLevel;
public short wShadingWeight;
public short wShadingStyle;
public short wNumberingStart;
public short wNumberingStyle;
public short wNumberingTab;
public short wBorderSpace;
public short wBorderWidth;
public short wBorders;
}
[DllImport("user32", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, ref PARAFORMAT2 lParam);
/// <summary>
/// 设置行距
/// </summary>
/// <param name="ctl">控件</param>
/// <param name="dyLineSpacing">间距</param>
public static void SetLineSpace(Control ctl, int dyLineSpacing)
{
PARAFORMAT2 fmt = new PARAFORMAT2();
fmt.cbSize = Marshal.SizeOf(fmt);
fmt.bLineSpacingRule = 4;// bLineSpacingRule;
fmt.dyLineSpacing = dyLineSpacing;
fmt.dwMask = PFM_LINESPACING;
try
{
SendMessage(new HandleRef(ctl, ctl.Handle), EM_SETPARAFORMAT, 0, ref fmt);
}
catch
{
}
}
}
外部调用,则是 SetLineSpace(textBox_MesInfo, 300);
去除多余的消息
后来实际使用中,发现现场电脑从不关机,所以又额外增加了去除多余消息的功能。RichTextbox用正常逻辑去处理不行,字的颜色会改变。需要用它特有的方法。
//这里SelectionStart 可变,0表示从头开始删除
richTextBox_MesInfo.SelectionStart = 0;
//这里的2可变,数字越大删除越多
richTextBox_MesInfo.SelectionLength = richTextBox_MesInfo.GetFirstCharIndexFromLine(2);
//richtextbox的Text不可以直接赋值,字的颜色会变,这个原理是将选中的某一段将其变成"",达到类似删除的效果
richTextBox_MesInfo.SelectedText = "";
ListBox 实现每行不同颜色,自动换行等
ListBox实现不同颜色很容易,因为控制每个item很容易,同理去除多余信息也很容易,都是操作item就可以了
ListBox定位到最新的信息
listBox_MES.TopIndex = listBox_MES.Items.Count - 1;
ListBox自动换行,设置间距,复制等
listbox简单好用,也有很大的局限性,最显而易见的就是,一条数据只能显示在一行,如果内容很长,体验就非常差。第二个明显的就是,listbox里面的item信息只可读,不可复制。
先说ListBox的复制
没有什么好的方法,可以使用添加右键菜单的形式,利用系统的剪贴板达到效果。
// 支持右键拷贝
ContextMenuStrip listboxMenu = new ContextMenuStrip();
ToolStripMenuItem rightMenu = new ToolStripMenuItem("复制");
rightMenu.Click += new EventHandler(Copy_Click);
listBox_MES.Items.AddRange(new ToolStripItem[] { rightMenu });
this.listBox.ContextMenuStrip = listboxMenu;
private void Copy_Click(object sender, EventArgs e)
{
Clipboard.SetText(listBox_MES.Items[listBox.SelectedIndex].ToString());
}
弊端显而易见,需要鼠标右键额外点击一次,而且只能复制整个item的信息,(非要特殊处理也行,但是太麻烦了,得不偿失),不如RichTextbox可以复制任意长度的字符。
ListBox自动换行,设置间距
这个没有便捷的方法,只能去重绘,(有利有弊,弊端就是麻烦,利则是可以按照自己所想,重绘出更加好看的效果)重写listbox 的 MeasureItem 和 DrawItem方法,一定需要将 listBox_MES.DrawMode设置为 DrawMode.OwnerDrawVariable,案例中只写了上间距、下间距,包括字体的设置,为了更加绚丽还可以添加更多。
/// <summary>
/// 根据MES信息的长度定义item的高度
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void listBox_MES_MeasureItem(object sender, MeasureItemEventArgs e)
{
Graphics gfx = e.Graphics;
string dtext = listBox_MES.Items[e.Index].ToString();
StringFormat stf = new StringFormat();
stf.FormatFlags = StringFormatFlags.FitBlackBox;
SizeF f2 = gfx.MeasureString(dtext, tfont, this.listBox_MES.ClientSize.Width - leftMargines, stf);
e.ItemHeight = topMargines + (int)f2.Height + 8 + 8;//上下高度
}
//可以写在Load里面也可以写在其他地方
private void panel_Load(object sender, EventArgs e)
{
listBox_MES.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
tfont = new Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
leftMargines = 8;
topMargines = 10;
}
/// <summary>
/// 左间距
/// </summary>
int leftMargines;
/// <summary>
/// 上间距
/// </summary>
int topMargines;
/// <summary>
/// 显示字体
/// </summary>
Font tfont;
/// <summary>
/// MES信息重绘
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void listBox_MES_DrawItem(object sender, DrawItemEventArgs e)
{
string dtext = listBox_MES.Items[e.Index].ToString();
Graphics gfx = e.Graphics;
Rectangle rect = new Rectangle(e.Bounds.X + leftMargines, e.Bounds.Y + topMargines, this.listBox_MES.ClientSize.Width - leftMargines, this.ClientSize.Height);
StringFormat stf = new StringFormat();
stf.FormatFlags = StringFormatFlags.FitBlackBox;
gfx.DrawString(dtext, tfont, dtext.Contains("失败") ? Brushes.Red : Brushes.Black, rect, stf);
e.DrawFocusRectangle();
}
小时候看迪迦,里面一段话特别印象深刻,“努力活完短暂的一生,将成果留给后代继承,人类就是如此反复,真的很了不起!”为往圣继绝学,为万世开太平。