第三天 -- 《2014-07-18 三层架构》1 -- 递归调用、根据实体集合构建树、将树解析实体集合。方法的传递。

一、上午《01、回顾》--《03图解递归》--递归,根据实体集合构建树

1、数据库表结构、树结构

                    

 

2、表转树(递归添加节点)

递归调用:方法体内调用方法本身,要求:一不能是死循环,即存在结束递归调用的条件。二方法调用方法本身。递归调用的图解(见下图)
注意这么几个关键点:
<1>第1层循环创建的是第1层的节点,后面每层循环都以上一层某节点为父节点,创建该层节点。
<2>调用方法相当于在方法的调用处创建方法的代码。同理递归调用,也是在每次调用处创建方法的代码(见下图)。值得注意的是,每次创建的是相同的方法代码,只是传入参数在变化。
<3>方法执行完毕会回到其主调方法的下一句继续执行。递归调用中这意味着当边界条件满足时,递归返回(见下图)。

 1 //UI层主要代码
 2 ...
 3 BLL.AreasManager _am = new BLL.AreasManager();
 4 private List<Areas> _list = null;//实体集合
 5 
 6 private void FrmAreas_Load(object sender, EventArgs e)
 7 {//窗体加载时,通过三层获取实体集合。再构建树
 8      _list = _am.GetAllAreasList(false);
 9 
10      BuildTreeList(0, null);//第一次去找id为0的
11 }
12 
13 
14 private void BuildTreeList(int parentId, TreeNode parentNode)
15 {//递归构建树
16     foreach (Areas item in _list)
17     {
18         if (item.APid == parentId)
19         {//如果节点的父节点id与指定id相等.==这是结束递归的条件:集合中不再有哪个元素的父id等于指定Id(最叶子层了)
20             TreeNode node = new TreeNode(item.AName);
21             node.Tag = item;//将实体对象绑到节点Tag上,便于以后获取实体
22 
23             if (parentNode == null)
24             {//如果节点本身就是根节点,也就是说没有父节点
25                 tvlist.Nodes.Add(node);//加到TreeView控件根节点
26             }
27             else
28             {
29                 parentNode.Nodes.Add(node);
30             }
31             //现在已经加好了一个节点,下面还要加其他节点
32             BuildTreeList(item.AID, node);//递归调用
33         }
34 
35     }
36 }

 

3、获取树节点上的实体对象

如果之前构建树时,已经将实体绑到对应节点的Tag上,可以通过Tag(见下面代码)获取节点上的实体对象

 1 if (tvlist.SelectedNode != null)
 2 {//如果选中节点
 3     Areas  ar = tvlist.SelectedNode.Tag as Areas;
 4     if(ar != null)
 5     {//如果已经获得实体对象
 6         
 7     }
 8 }
 9 else
10 {
11     MessageBox.Show("请先选择节点");
12 }

 

 

4、树转表(递归获取子节点Tag,转为实体对象)

从根节点开始循环遍历所有节点,将节点的Tag(前提是构建树的时候已经把每个节点Tag绑上对应实体对象)转为实体对象,添加到集合。

 1 //树转表主要代码
 2 private List<Areas> _listAreas;//待填充的实体集合
 3 ...
 4 private void btn_ToList_Click(object sender, EventArgs e)
 5 {//点击按钮,将树解析到实体集合
 6     _listAreas = new List<Areas>();
 7     ParseTreeList(this.tvlist.Nodes);
 8 }
 9 
10 private void ParseTreeList(TreeNodeCollection nodes)
11 {//递归解析树
12     foreach (TreeNode node in nodes)
13     {
14         Areas a = node.Tag as Areas;//将节点Tag转为实体对象,添加到集合
15         if (a != null)
16         {
17             _listAreas.Add(a);
18         }
19         if (node.Nodes.Count != 0)
20         {//如果存在子节点,递归下一层。==“无子节点”是结束递归的条件
21             ParseTreeList(node.Nodes);
22         }
23         
24     }
25 }

 

 

二、上午《04、将对象存储到节点的Tag值中》--《05、使用递归的方式获取地区信息》--递归,获取节点路径

1、获取选中节点的全路径(每个节点的Name)

 1 tvlist.PathSeparator = "|";//指定节点路径的间隔符
 2 ...
 3 
 4 if (tvlist.SelectedNode != null)
 5 {//如果选中节点
 6     string path = tvlist.SelectedNode.FullPath;//节点全路径
 7 }
 8 else
 9 {
10     MessageBox.Show("请先选择节点");
11 }

 

2、递归遍历选中节点的父节点,获取全路径(获取节点绑定的实体对象Id)

private void btnOk_Click(object sender, EventArgs e)
{//点击按钮,构建选中节点的全路径和对应实体的全Id路径
    if (tvlist.SelectedNode != null)
    {
        StringBuilder sb = new StringBuilder();
        BuildIdPath(sb, tvlist.SelectedNode);//递归遍历父节点,构建对应实体的全Id路径
        _idPath = sb.ToString();//Id路径
       
        _address = tvlist.SelectedNode.FullPath;//节点的全路径
        
    }
    else
    {
        MessageBox.Show("请先选择地点");
    }

}

//递归构建对应实体的全Id路径
private void BuildIdPath(StringBuilder sb, TreeNode node)
{
    if (node.Parent != null)
    {//先递归遍历父节点
        BuildIdPath(sb, node.Parent);
    }
    //最后从根节点开始,回递构建Id路径
    Areas area = node.Tag as Areas;//获取实体(前提是当初构建树的时候,每个节点Tag绑上实体对象)
    if (area != null)
    {
        if (node.Parent != null)
        {//如果节点不是根节点
            sb.Append("|");
        }

        sb.Append(area.AID);
    }
}

 

 

三、上午《06、使用接口实现窗体值的回传》--《08、使用委托做为方法的参数》--递归,获取节点路径

有时希望子窗体能够访问父窗体的方法、属性时,可以用以下几种方式。推荐使用最后一种。

1、构建子窗体时(构造器),传入父窗体对象。--(违背封装原则)

 1 //FrmPerson.cs
 2 private void btnChoice_Click(object sender, EventArgs e)
 3 {//点击按钮,创建打开FrmAreas子窗体
 4     FrmAreas frm = new FrmAreas(this);//将父窗体对象传入子窗体
 5    
 6     frm.ShowDialog();
 7 }
 8 
 9 //FrmAreas.cs
10 //FrmAreas窗体重载构造器。接受一个父窗体FrmPerson对象
11 public FrmAreas(FrmPerson frm)
12 {
13     InitializeComponent();
14     this.frm = frm;//赋值给一个父窗体FrmPerson字段
15 }

 

2、构建子窗体时(构造器),传入父窗体的接口引用。--(挺好,但是要定义接口)

<1>父窗体需要实现某个接口,将父窗体引用转为接口类型引用,传入子窗体构造器(实例化子窗体)。

<2>子窗体重载构造器,接受一个接口类型引用。

//定义一个接口
public interface IFillData
{
    void FillAreas(string text, string aid);//将传入字符串显示、绑定在主窗体某个控件上
}


//FrmPerson.cs 父窗体FrmPerson定义
public partial class FrmPerson : Form,  IFillData
{  
    private void btnChoice_Click(object sender, EventArgs e)
    {//点击按钮,创建打开FrmAreas子窗体
        FrmAreas frm = new FrmAreas((IFillData)this);//父窗体引用转为接口类型引用,传入子窗体构造器
   
        frm.ShowDialog();
    }

    public void FillAreas(string text, string aid)
    {//这是实现接口的方法
        txtDistrict.Text = text;
        txtDistrict.Tag = aid;
    } 
}

//FrmAreas.cs  
//FrmAreas窗体重载构造器。接受一个IFillData接口类型引用
private IFillData Ifrm;//接口类型字段
...
public FrmAreas(IFillData Ifrm)
{
    InitializeComponent();
    this.Ifrm = Ifrm;//赋值给一个IFillData接口类型字段
}

 

3、传递委托对象。--(方便、安全)

 1 //FrmPerson.cs
 2 private void btnChoice_Click(object sender, EventArgs e)
 3 {//使用Action泛型委托,为FrmAreas构造器传入委托对象。
 4     //实例化一个委托对象。它有一个方法:作用是将输入字符串显示、绑定到文本控件。
 5     Action<string, string> fillAreas = (text,aid) => { txtDistrict.Text = text; txtDistrict.Tag = aid; } ;
 6     FrmAreas frm = new FrmAreas(fillAreas);
 7    
 8     frm.ShowDialog();
 9 }
10 
11 //FrmAreas.cs
12 //FrmAreas窗体重载构造器。接受一个委托对象
13 private Action<string, string> _fillArea;//委托对象
14 ...
15 public FrmAreas(Action<string, string> fillArea)
16 {
17     InitializeComponent();
18     this._fillArea = fillArea;
19 }

 

posted on 2017-08-27 10:37  困兽斗  阅读(268)  评论(0)    收藏  举报

导航