Bootstrap

C# winForm RichTextbox、ListBox 每行显示不同的颜色,自动换行等等-2023/02/02

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();
        }

小时候看迪迦,里面一段话特别印象深刻,“努力活完短暂的一生,将成果留给后代继承,人类就是如此反复,真的很了不起!”为往圣继绝学,为万世开太平。

;