Winform自定义控件之二叉树控件(2)
1. 二叉树控件
(1) 二叉树节点控件一个连接节点只能引出一条连接线
(2) 二叉树节点控件可以自由拖动并且自动绘制连接线
废话不说了实现效果如下:
2. 实现效果如下:
图-1添加节点
图-2 插入节点
图-3 连接节点
图-4 拖动节点调整节点位置
3. 类关系图
4. 实现思路
连接矩形:就是指连接节点上的小方块。
连接矩形名称 |
枚举对应值 |
上 |
TopRectangle |
下左 |
BottomLeftRectangle |
下右 |
BottomRightRectangle |
BNode节点类中存储与它连接的各节点字符串,格式如下:
<当前节点名称-连接矩形类型名称,连接节点-连接矩形类型名称>。
当鼠标点击连接矩形是开始记录当前连接矩形,当鼠标释放时记录鼠标释放位置。
鼠标释放时引发连接事件,在外部连接事件中判断鼠标的释放位置是否在进入另一节点的连接矩形中。如果进入了另一节点的连接矩形,就在当前节点中记录当前节点的字符串形式的连接点信息。然后调用绘图方法枚举每个二叉树节点并且根据字符串形式的连接点信息连接每个二叉树节点。
为什么使用字符串格式的连接点信息?
因为字符串格式的存储的只是相对连接信息,当节点移动时更容易计算节点到容器的客户端坐标。如果存储是节点到容器的客户端Point结构的连接点信息,节点移动时需要大量的坐标转化。
5. 重要的算法
(1) 连接点处理
代码
/// <summary>
/// 节点的连接点坐标处理
/// </summary>
private void AdjNodePointProcess(BNode currentNode)
{
List<BNode> allList = this.Controls.OfType<BNode>().ToList();
List<BNode> otherList = allList.Where(p => p.Name != currentNode.Name).ToList();
for (int j = 0; j < currentNode.EndPointList.Count; j++)
{
KeyValuePair<string, Point> currentPair = currentNode.EndPointList.ToArray()[j];
foreach (var otherBNode in otherList)
{
KeyValuePair<string, string> keyPair = HitBNode(currentPair, currentNode, otherBNode);
if (keyPair.Key != string.Empty)
{
currentNode.AddAdjNode(keyPair.Key, keyPair.Value);
}
}
}
}
/// 节点的连接点坐标处理
/// </summary>
private void AdjNodePointProcess(BNode currentNode)
{
List<BNode> allList = this.Controls.OfType<BNode>().ToList();
List<BNode> otherList = allList.Where(p => p.Name != currentNode.Name).ToList();
for (int j = 0; j < currentNode.EndPointList.Count; j++)
{
KeyValuePair<string, Point> currentPair = currentNode.EndPointList.ToArray()[j];
foreach (var otherBNode in otherList)
{
KeyValuePair<string, string> keyPair = HitBNode(currentPair, currentNode, otherBNode);
if (keyPair.Key != string.Empty)
{
currentNode.AddAdjNode(keyPair.Key, keyPair.Value);
}
}
}
}
(2) 连接矩形只能有一条连接线限制
代码
/// <summary>
/// 添加连接节点
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddAdjNode(string key,string value)
{
//允许a-->b修改为a---->c
if (adjNodes.ContainsKey(key))
{
adjNodes.Remove(key);
Debug.WriteLine("add line");
}
//找出当前节点的父容器
Control container = this.Parent;
List<BNode> list = container.Controls.OfType<BNode>().ToList();
//(1)防止集合已有了
//a节点 有了 a-->b,b节点有 b-->a
//(2)防止集合已有了(同一节点只能有一条连接线)
//bNode3|BottomLeftRectangle-bNode4|TopRectangle
//bNode1|BottomLeftRectangle-bNode4|TopRectangle
if (
!list.Exists(
delegate(BNode b) {
if(b.adjNodes.Count(p=>p.Value==key && p.Key==value)>0)
{
return true ;
}
return false;
}
)
&&
!list.Exists(
delegate(BNode b) {
if(b.adjNodes.Count(p=>p.Value==value )>0 || b.adjNodes.Count(p=>p.Key==value)>0
||
b.adjNodes.Count(p=>p.Value ==key )>0 || b.adjNodes.Count(p=>p.Key==key )>0
)
{
return true ;
}
return false;
}
)
)
{
adjNodes.Add(key, value);
}
}
/// 添加连接节点
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddAdjNode(string key,string value)
{
//允许a-->b修改为a---->c
if (adjNodes.ContainsKey(key))
{
adjNodes.Remove(key);
Debug.WriteLine("add line");
}
//找出当前节点的父容器
Control container = this.Parent;
List<BNode> list = container.Controls.OfType<BNode>().ToList();
//(1)防止集合已有了
//a节点 有了 a-->b,b节点有 b-->a
//(2)防止集合已有了(同一节点只能有一条连接线)
//bNode3|BottomLeftRectangle-bNode4|TopRectangle
//bNode1|BottomLeftRectangle-bNode4|TopRectangle
if (
!list.Exists(
delegate(BNode b) {
if(b.adjNodes.Count(p=>p.Value==key && p.Key==value)>0)
{
return true ;
}
return false;
}
)
&&
!list.Exists(
delegate(BNode b) {
if(b.adjNodes.Count(p=>p.Value==value )>0 || b.adjNodes.Count(p=>p.Key==value)>0
||
b.adjNodes.Count(p=>p.Value ==key )>0 || b.adjNodes.Count(p=>p.Key==key )>0
)
{
return true ;
}
return false;
}
)
)
{
adjNodes.Add(key, value);
}
}