一、效果示意
今天设计了一个自定义控件,用于满足下面的场景:
有一句话,有多个子句组成,每一个子句都要设置对应的超链接。举个例子,比如现有要素:姓名、国籍、生年、卒年。可以组成这么一句话:
“【理查德·尼克松】,国籍【美国】,生于【1913年1月9日】,卒于【1994年4月22日】。”
但是,这四个要素并不是每条数据都有的,除了姓名是必须的,后面三点要素都是可选的。比如只有姓名、生年、卒年,没有国籍时,拼成的话就是:
“【霍去病】,生于【前140年】,卒于【前117年】。”
超链接点击后,根据点击的文字,可以触发对应的事件。
二、控件设计
建立一个继承UserControl类的自定义控件,名为MyLinkLabelPanel,里面有五个Panel,最中间的Panel中添加一个Dock为Fill的LinkLabel。
控件代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace LinkLabelTest
{
/// <summary>
/// 自制超链接控件
/// </summary>
public partial class MyLinkLabelPanel : UserControl
{
public MyLinkLabelPanel()
{
InitializeComponent();
}
/// <summary>
/// 链接详细信息
/// </summary>
private class SituationDetail
{
/// <summary>
/// 链接起始点
/// </summary>
public int Start;
/// <summary>
/// 链接长度
/// </summary>
public int Length;
/// <summary>
/// 链接数据
/// </summary>
public string Data;
/// <summary>
/// 子句内容
/// </summary>
public string Description;
/// <summary>
/// 链接详细信息
/// </summary>
/// <param name="data">链接数据</param>
/// <param name="description">子句内容</param>
public SituationDetail(string data, string description)
{
this.Data = data;
this.Description = description;
}
/// <summary>
/// 重载:ToString函数,返回子句内容Description
/// </summary>
/// <returns></returns>
public override string ToString()
{
return Description;
}
};
/// <summary>
/// 根据控件属性设置,生成链接文本
/// </summary>
public void RefreshData()
{
lnkContent.Text = "";
lnkContent.Links.Clear();
LinkedList<SituationDetail> linkedList = new LinkedList<SituationDetail>();
if (!string.IsNullOrWhiteSpace(Name))
{
linkedList.AddLast(new SituationDetail(
Name, "【" + Name + "】"));
}
else
{
lnkContent.Text = "人物姓名不能为空";
return;
}
if (!string.IsNullOrWhiteSpace(Nationality))
{
linkedList.AddLast(new SituationDetail(
Nationality, "国籍【" + Nationality + "】"));
}
if (!string.IsNullOrWhiteSpace(BornDate))
{
linkedList.AddLast(new SituationDetail(
BornDate, "生于【" + BornDate + "】"));
}
if (!string.IsNullOrWhiteSpace(DeadDate))
{
linkedList.AddLast(new SituationDetail(
DeadDate, "卒于【" + DeadDate + "】"));
}
if (linkedList.Count != 0)
{
string result = String.Join(",", linkedList) + "。";
lnkContent.Text = result;
LinkedListNode<SituationDetail> node = linkedList.First;
for (int i = 0; i < result.Length; i++)
{
if (result[i] == '【')
{
node.Value.Start = i;
}
else if (result[i] == '】')
{
node.Value.Length = i - node.Value.Start + 1;
lnkContent.Links.Add(node.Value.Start, node.Value.Length, node.Value.Data);
if (node.Next == null)
{
break;
}
else
{
node = node.Next;
}
}
}
}
else
{
lnkContent.Text = "数据缺失";
}
}
#region 对外开放的可设置属性
/// <summary>
/// 姓名
/// </summary>
private string _name;
///<summary>
/// 姓名
///</summary>
[System.ComponentModel.Description("姓名")]
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
/// <summary>
/// 国籍
/// </summary>
private string _nationality;
///<summary>
/// 国籍
///</summary>
[System.ComponentModel.Description("国籍")]
public string Nationality
{
get
{
return _nationality;
}
set
{
_nationality = value;
}
}
/// <summary>
/// 出生日期
/// </summary>
private string _bornDate;
///<summary>
/// 出生日期
///</summary>
[System.ComponentModel.Description("出生日期")]
public string BornDate
{
get
{
return _bornDate;
}
set
{
_bornDate = value;
}
}
/// <summary>
/// 逝世日期
/// </summary>
private string _deadDate;
///<summary>
/// 逝世日期
///</summary>
[System.ComponentModel.Description("逝世日期")]
public string DeadDate
{
get
{
return _deadDate;
}
set
{
_deadDate = value;
}
}
#endregion
/// <summary>
/// 单击链接时触发
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void lnkContent_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
lnkContent.Links[lnkContent.Links.IndexOf(e.Link)].Visited = true;
string selectedItem = e.Link.LinkData.ToString();
MessageBox.Show("当前点击条目为:" + selectedItem.ToString());
}
}
}
注:根据要生成的语句内容,可以灵活将一句话拆分成多个子句。每个子句可以单独写一个属性存放其中的值,将每个子句都存放在类SituationDetail中,在函数RefreshData中将这些子句要素拼成完整的一句话。
三、控件使用
建立一个C#窗体应用程序,主窗体命名为FormMain,放置如下控件:
窗体代码如下,这段代码给出了自定义控件MyLinkLabelPanel的使用示例:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LinkLabelTest
{
public partial class FormMain : Form
{
public FormMain()
{
InitializeComponent();
}
private void FormMain_Load(object sender, EventArgs e)
{
btnRefreshData_Click(null, null);
}
private void btnRefreshData_Click(object sender, EventArgs e)
{
//测试数据
pnlMain.Name = txtName.Text;
pnlMain.Nationality = txtNationality.Text;
pnlMain.BornDate = txtBornDate.Text;
pnlMain.DeadDate = txtDeadDate.Text;
pnlMain.RefreshData();
}
}
}
END