C# WinForm Label 控件拓展—变色字体、超链接
来源:互联网 发布:全球软件行业市场规模 编辑:程序博客网 时间:2024/06/07 17:32
前言:
以前做项目需要实现在文本类控件中加入超链接文字段的功能,在网上查了不少资料基本没有找到比较理想的实现方法,最后无奈只好自己实现了...
需求:
拓展Label控件,使其文本中的文字可以有多种颜色,添加下划线、做成超链、点击变色。变色文字的格局(间距、字体大小、换行)必须与原来一致。(注意做出改变的是部分文字,非整个文本)效果图如下:
图1
图2
图3
图1为WinForm原生Label的预览效果,图2、图3为拓展后的预览效果。
实现原理:
(1)准确测量文字区域
需要准确测量出文字段的大小才能保证文字格局的不变,GDI+已经自带了这种功能,可以得到指定文字段的System.Drawing.Region对象。但直接通过System.Drawing.Region对象获取的数据进行绘制的效果不太理想(未深入尝试),所以才有了(2)。
(2)异或刷前景
改变局部文字颜色的一种方法:①计算异或相关的颜色值,对局部区域做一次异或填充②绘制所有文字 ③使用与第一步同样的颜色对局部区域再做一次异或填充。
实现上述方法的条件:已知填充的区域、能控制整体文字绘制。
(3)开窗
底部加一层Label,在底部Label做变色,添加下划线效果,上层Label对变色部分开窗。--不算是个好方法,下滑线只能全局设置。
源码:
namespace MultiColorLabel{ //超链接、变色文字Label拓展控件 //Function: //(1)Label控件中改变颜色的部分,在这里命名为“分段”。 //(2)一个Label可以有设置多个分段,每个分段可以相互重叠,重叠部分会做颜色融合。 //(3)分段可以改变颜色设置下划线、中划线和响应部分鼠标消息。 //Limitation:不支持Label的Padding属性改变 //Written by xin 2017.3.28 //Reviewed by xin 2017.9.4 This is a test edition, probably has some potential problem. //Multi Color Label public class ColorLabel : Label { public ColorLabelExtension ColorEx { get { return m_colorEx; } } ColorLabelExtension m_colorEx; public ColorLabel() { m_colorEx = new ColorLabelExtension(this); } } //main collaboration class public class ColorLabelExtension { public RangeEvents Events { get { return m_rangeEvents; } }//事件集合 public IEnumerableRanges { get { return m_ranges; } }//分段集合 public bool UnderLine { set { FontStyle = value ? FontStyle | FontStyle.Underline : FontStyle & ~FontStyle.Underline; } get { return ((int)m_rangeStyle & (int)FontStyle.Underline) != 0; } }//下划线 public bool Strikeout { set { FontStyle = value ? FontStyle | FontStyle.Strikeout : FontStyle & ~FontStyle.Strikeout; } get { return ((int)m_rangeStyle & (int)FontStyle.Strikeout) != 0; } }//中划线 FontStyle FontStyle//设置分段的字体风格,其中粗体、斜体、常规对此属性无效。 --取消开放 { set { m_rangeStyle = value & ~FontStyle.Italic & ~FontStyle.Bold & ~FontStyle.Regular; OnRangeFontStyleChanged(); } get { return m_rangeStyle; } } Label m_parent; SubLabel m_subLabel;//显示分段的label List m_ranges = new List ();//分段对象 RangeEvents m_rangeEvents; FontStyle m_rangeStyle; int m_lastUpdateRangeCount = 0;//上次更新界面时分段的数量 public ColorLabelExtension(Label parent) { m_parent = parent; //注册事件 m_parent.FontChanged += OnFontChanged; m_parent.TextChanged += OnTextChanged; m_parent.SizeChanged += OnSizeChanged; m_parent.Paint += OnPaint; //初始化分段控件 m_rangeEvents = new RangeEvents(m_parent); m_subLabel = new SubLabel(m_ranges, m_rangeEvents); m_parent.Controls.Add(m_subLabel); //同步分段控件的设置 m_subLabel.SuspendLayout(); // PropertiesCopy(m_subLabel,m_parent); m_subLabel.Cursor = Cursors.Hand; // this.UnderLine = true; m_subLabel.Size = m_parent.Size; m_subLabel.Region = new Region(new Rectangle(new Point(0, 0), new Size(0, 0))); m_subLabel.ResumeLayout(); } //创建分段并添加,返回创建的对象,下标和长度可以超过文本长度,清除文本分段依然保留,清除分段调用Clear即可。 public Range Add(int index, int length) { if (index < 0 || length < 0) throw new IndexOutOfRangeException("下标或长度不能为负!"); Range textRange = new Range(index, length, m_parent.Text); textRange.ForeColor = m_parent.ForeColor; textRange.PressedColor = Color.Red; m_ranges.Add(textRange); //回调事件 OnRangeCountChanged(); return textRange; } //删除分段 public void Remove(Range r) { if (m_ranges.Remove(r)) { //回调事件 OnRangeCountChanged(); } } //清除分段 public void Clear() { int lastCount = m_ranges.Count; m_ranges.Clear(); if (lastCount > 0) //回调事件 OnRangeCountChanged(); } //字体格式改变时 void OnRangeFontStyleChanged() { m_subLabel.Font = new Font(m_parent.Font, m_parent.Font.Style| m_rangeStyle); } //父控件字体改变时 void OnFontChanged(object sender, EventArgs e) { OnRangeFontStyleChanged(); } //父控件文本改变时 void OnTextChanged(object sender, EventArgs e) { //获取分段文本信息 for (int i = 0; i < m_ranges.Count; i++) m_ranges[i].ParentText = m_parent.Text; m_subLabel.Text = m_parent.Text; } //尺寸改变时 void OnSizeChanged(object sender, EventArgs e) { m_subLabel.Size = m_parent.Size; } //绘制控件 void OnPaint(object sender, PaintEventArgs e) { //更新分段的位置 updateRangeData(e.Graphics); } //当分段数量改变时 void OnRangeCountChanged() { //如果控件为可视状态 if (isParentVisualable()) { //更新分段数据 updateRangeData(); } } //控件是否可视 bool isParentVisualable() { return m_parent.IsHandleCreated && m_parent.Visible; } //计算分段显示的区域 void updateRangeData() { Graphics g = m_parent.CreateGraphics(); updateRangeData(g); g.Dispose(); } //计算分段显示的区域 void updateRangeData(Graphics g) { if (m_ranges.Count < 1) { if (m_lastUpdateRangeCount > 0) { //区域置0 m_subLabel.Region = new Region(new Rectangle(new Point(0, 0), new Size(0, 0))); m_lastUpdateRangeCount = 0; } return; } RectangleF layoutRect = m_parent.ClientRectangle; CharacterRange[] charRanges; StringFormat strFormat = new StringFormat(); Region[] rangeRegions; Region lblRegion; int count = m_ranges.Count; //获取分段文本信息 charRanges = m_ranges.ConvertAll(o => o.CRange).ToArray(); //测量 strFormat.SetMeasurableCharacterRanges(charRanges); rangeRegions = g.MeasureCharacterRanges(m_parent.Text, m_parent.Font, layoutRect, strFormat); for (int i = 0; i < count; i++) { m_ranges[i].Region = rangeRegions[i]; } //开窗 lblRegion = new Region(new Rectangle(new Point(0, 0), new Size(0, 0))); for (int i = 0; i < count; i++) { lblRegion.Union(rangeRegions[i]); } m_subLabel.Region = lblRegion; m_lastUpdateRangeCount = m_ranges.Count;//记录当前的分段数 } } //显示分段的label,用于绘制分段和捕获事件 class SubLabel : Label { List m_ranges; RangeEvents m_textRangeEvents; Range depressRange = null;//鼠标按下的分段 public SubLabel(List ranges, RangeEvents textRangeEvents) { m_ranges = ranges; m_textRangeEvents = textRangeEvents; } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); Range range = null; if (tryHitRange(e.Location, out range)) { //触发事件 var tme = new RangeMouseEventArgs(); tme.Range = range; tme.MouseEventArgs = e; callEvent(m_textRangeEvents, "MouseDown", m_textRangeEvents.Sender, tme); if (e.Button != MouseButtons.Left) return; //按下变色 //判断边界:可视,是否被释放,鼠标位置 if (IsRangePressable(range)) { //颜色交换 Color temp = range.PressedColor; range.PressedColor = range.ForeColor; range.ForeColor = temp; depressRange = range; Invalidate(); } } } bool IsRangePressable(Range range) { return range.Pressabled&&this.Visible && this.IsHandleCreated && range.Region.IsVisible(this.PointToClient(Form.MousePosition)); } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); Range range = null; //还原按下的分段 if (depressRange != null) { //颜色交换 Color temp = depressRange.PressedColor; depressRange.PressedColor = depressRange.ForeColor; depressRange.ForeColor = temp; depressRange = null; Invalidate(); } //触发事件 if (tryHitRange(e.Location, out range)) { var tme = new RangeMouseEventArgs(); tme.Range = range; tme.MouseEventArgs = e; callEvent(m_textRangeEvents, "MouseUp", m_textRangeEvents.Sender, tme); } } protected override void OnMouseClick(MouseEventArgs e) { base.OnMouseClick(e); Range range = null; if (tryHitRange(e.Location, out range)) { //触发事件 var tme = new RangeMouseEventArgs(); tme.Range = range; tme.MouseEventArgs = e; callEvent(m_textRangeEvents, "MouseClick", m_textRangeEvents.Sender, tme); } } protected override void OnClick(EventArgs e) { base.OnClick(e); Range range = null; if (tryHitRange(this.PointToClient(Form.MousePosition), out range)) { //触发事件 var tme = new RangeMouseEventArgs(); tme.Range = range; callEvent(m_textRangeEvents, "Click", m_textRangeEvents.Sender, tme); } } //外部触发事件 void callEvent(object p_Object, string p_EventName, params object[] args) { System.Reflection.FieldInfo _Field = p_Object.GetType().GetField(p_EventName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static); if (_Field == null) { return; } object _FieldValue = _Field.GetValue(p_Object); if (_FieldValue != null && _FieldValue is Delegate) { Delegate _ObjectDelegate = (Delegate)_FieldValue; _ObjectDelegate.DynamicInvoke(args); } } //绘制分段(做变色处理部分) protected override void OnPaint(PaintEventArgs e) { if (m_ranges.Count < 1) { return; } Graphics g = e.Graphics; int count = m_ranges.Count; var hrgns = new IntPtr[count]; long[] colors = new long[count]; for (int i = 0; i < count; i++) { //调配颜色 colors[i] = m_ranges[i].ForeColor.ToCOLORREF() ^ this.ForeColor.ToCOLORREF(); //将Region转换为Hrgn hrgns[i] = m_ranges[i].Region.GetHrgn(g); } //使用gdi画异或背景 drawXORRegions(g, hrgns, colors); g.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor), this.ClientRectangle); drawXORRegions(g, hrgns, colors); //释放资源 for (int i = 0; i < count; i++) { m_ranges[i].Region.ReleaseHrgn(hrgns[i]); } } [DllImport("gdi32.dll")] public static extern int SetROP2(IntPtr hdc, int rop2); [DllImport("gdi32.dll")] public static extern int DeleteObject(IntPtr ho); [DllImport("gdi32.dll")] public static extern IntPtr CreateSolidBrush(int color); [DllImport("gdi32.dll ")] public static extern int FillRgn(IntPtr hdc, IntPtr hrgn, IntPtr hbr); //使用异或的方式绘制Region void drawXORRegions(Graphics grp, IntPtr[] region, long[] color) { //取得GDI+的设备上下文 IntPtr hdc = grp.GetHdc(); // 设置光栅模式为异或 var oldRop2 = SetROP2(hdc, 7); int count = region.Length; IntPtr[] brushes = new IntPtr[region.Length]; //生成各个颜色的画刷 for (int i = 0; i < count; i++) { brushes[i] = CreateSolidBrush((int)color[i]); } //开始画区域 for (int i = 0; i < count; i++) { FillRgn(hdc, region[i], brushes[i]); } //释放资源 for (int i = 0; i < count; i++) { DeleteObject(brushes[i]); } // 释放hdc grp.ReleaseHdc(hdc); } //查找点所在的分段 bool tryHitRange(Point p, out Range range) { range = null; for (int i = 0; i < m_ranges.Count; i++) { if (m_ranges[i].Region.IsVisible(p)) { range = m_ranges[i]; return true; } } return false; } } //文本分段实体 public class Range { public string Text//返回分段的文本 { get { if (text == null && parentText != null) { text = parentText.Substring(cRange.First, cRange.Length); } return text; } } public int Index { get { return index; } }//开始的位置 public int Length { get { return length; } }//长度 public Color ForeColor { set; get; }//颜色 public Color PressedColor { set; get; }//按下后的颜色 默认为红色 public bool Pressabled { set; get; }//是否可被按下 public object Tag { set; get; }//参见Control的Tag string parentText; string text; int index; int length; CharacterRange cRange; internal string ParentText //父文本 { set { parentText = value; OnParentTextChanged(); } get { return parentText; } } internal Region Region { set; get; }//本段的区域 internal CharacterRange CRange//字符的范围 { get { return cRange; } } //构造函数 internal Range(int index, int length, string parentText) { this.cRange = new CharacterRange(); this.index = index; this.length = length; this.ParentText = parentText; } //更新子文本内容及CharacterRange对象,主要做边界判断 void OnParentTextChanged() { if (parentText == null) //父文本为空 { text = null; cRange.First = 0; cRange.Length = 0; return; } if (index >= parentText.Length) //下标超过 { text = ""; cRange.First = 0; cRange.Length = 0; } else { int newLen = length; int lastLen = parentText.Length - index; if (length > lastLen) //长度超过 newLen = lastLen; cRange.First = index; cRange.Length = newLen; } } } //分段的事件实体 public class RangeEvents { object m_sender; //事件的sender internal object Sender { get { return m_sender; } } public RangeEvents(object sender) { m_sender = sender; } public event Action
其他:
限定:不兼容Label的Padding属性。
阅读全文
0 0
- C# WinForm Label 控件拓展—变色字体、超链接
- c# WinForm中如何在代码中设置控件的padding 设置Label的字体
- c# WinForm中如何在代码中设置控件的padding 设置Label的字体
- c# winform 超链接
- C# winform自定义Label控件使其能设置行距
- C# winform自定义Label控件使其能设置行距
- Android自定义字体变色控件
- C# 训练场(三)WinForm 练习:字体选择器,文本框,按钮,超链接
- C# Winform 实现透明label
- c# 超链接控件:HyperLink
- javaScript精华(1)--超链接变色字体预览图片技术
- C# WinForm 中控件(Label,Button等) 文本自动换行 解决方法
- C# WinForm 中控件(Label,Button等) 文本自动换行 解决方法
- C# WinForm 中控件(Label,Button等) 文本自动换行 解决方法
- 使用不同颜色的Label控件(WinForm)
- WinForm中Label控件的换行显示
- winform 可拖动的自定义Label控件
- WinForm中Label控件的换行显示
- cs231n笔记--到底什么是梯度消散
- 在windows上安装mongodb(二)
- 怎样读一本书V5.0 ?(译)
- Android注解使用之使用Support Annotations注解优化代码
- codility ChocolatesByNumbers
- C# WinForm Label 控件拓展—变色字体、超链接
- 人工智能 -- NLP : 零基础学习深度学习系列文章
- [初学笔记] 文件目录路径的操作,创建,更改等等
- Linux常用命令学习
- SQL Server存储过程创建和修改
- docker在阿里获取加速器图例
- Nao机器人高尔夫(golf)小结
- 设计师到底能不能自己进行可用性测试(下)
- 1005 计算字符串累加和并输出