代码之家  ›  专栏  ›  技术社区  ›  Yousef Golnahali

如何绘制一个网络模型来表示项目的时间导向模型[[关闭]

  •  -1
  • Yousef Golnahali  · 技术社区  · 7 年前

    netwok example

    我有桌子,想画模型。你建议用什么方法来做这个问题?

    编辑:

    当我向这个程序添加一个节点数据(一个DataTable包含超过100行的Activities和precedents列)并将其用作节点资源时,得到

    (根据@TaW的回答),

    在layoutNodeY()部分

    生产线: nodes.Values.ElementAt(i)[j].VY = 1f * j - c / 2

    NodeChart NC = new NodeChart();
    
    private void Form1_Load(object sender, EventArgs e)
       {
    
         for (int i = 0; i < sortedtable.Rows.Count - 1; i++)
            {  List<string> pred = sortedtable.Rows[i][2].ToString().Split(',').ToList();
               for (int j = 0; j < sortedtable.Rows.Count - 1; j++)
                {
                    foreach (var item in pred)
                    {
                        if (item == sortedtable.Rows[j][0].ToString() + "." + sortedtable.Rows[j][1].ToString())
                        {
                            NC.theNodes.Add(new NetNode(sortedtable.Rows[i][0].ToString() + "." + sortedtable.Rows[i][1].ToString(), item));
                        }
                    }
                } 
            }
       }
    

    Datatable的部分屏幕截图:

    enter image description here

    1 回复  |  直到 6 年前
        1
  •  1
  •   TaW    7 年前

    我建议将尽可能多的复杂性放在数据结构中。

    我经常利用 List<T> Dictionary<float, List<NetNode>>

    注意这篇文章比通常的答案要长得多;我希望它有启发性。。

    • 它应该知道自己的
    • 节点 允许转换为 二者都
    • …一份 节点
    • 虚拟位置 在布局上;绘制时必须按比例缩放以适合给定区域。。
    • 以及与邻国的联系。

    以下是使用数据和一个额外节点得到的结果:

    enter image description here

    现在让我们更仔细地研究一下代码。

    class NetNode
    {
        public string Text { get; set; }
        public List<NetNode> prevNodes { get; set; }
        public List<NetNode> nextNodes { get; set; }
        public float VX { get; set; }
        public float VY { get; set; }
    
        public string prevNodeNames;
    
        public NetNode(string text, string prevNodeNames)
        {
            this.prevNodeNames = prevNodeNames;
            prevNodes = new List<NetNode>();
            nextNodes = new List<NetNode>();
            Text = text; 
            VX = -1;
            VY = -1;
        }
        ...
    }
    

    列表<T> string 后来 NodeChart

    DrawCurves 用几个额外的点或构造所需的贝塞尔控制点。

    箭头也是便宜的;不幸的是,内置端盖不是很好。为了提高性能,您可以创建一个自定义的,也许可以使用一个graphicspath。。

    public void draw(Graphics g, float scale, float size)
    {
        RectangleF r = new RectangleF(VX * scale, VY * scale, size, size);
        g.FillEllipse(Brushes.Beige, r);
        g.DrawEllipse(Pens.Black, r);
        using (StringFormat fmt = new StringFormat()
        { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center})
        using (Font f = new Font("Consolas", 20f))
            g.DrawString(Text, f, Brushes.Blue, r, fmt);
    
        foreach(var nn in nextNodes)
        {
            using (Pen pen = new Pen(Color.Green, 1f)
            { EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor })
                g.DrawLine(pen, getConnector(this, scale, false, size),
                                getConnector(nn, scale, true, size));
        }
    }
    
    PointF getConnector(NetNode n, float scale, bool left, float size)
    {
        RectangleF r = new RectangleF(n.VX * scale, n.VY * scale, size, size);
        float x = left ? r.Left : r.Right;
        float y = r.Top + r.Height / 2;
        return new PointF(x, y);
    }
    

    您需要扩展node类以包含更多的文本、颜色、字体等。。

    上面的draw方法是最长的代码之一。让我们看看 现在上课。

    它包含..:

    • 节点和..的列表。。
    • 开始节点的列表。真的应该只有 '开始节点不唯一' ..
    • 方法列表 节点数据。

    class NodeChart
    {
        public List<NetNode> theNodes { get; set; }
        public List<NetNode> startNodes { get; set; }
    
        public NodeChart()
        {
            theNodes = new List<NetNode>();
            startNodes = new List<NetNode>();
        }
        ..
    
    }
    

    第一个方法使用 以前的 节点:

    public void fillPrevNodes()
    {
        foreach (var n in theNodes)
        {
            var pn = n.prevNodeNames.Split(',');
            foreach (var p in pn)
            {
                var hit = theNodes.Where(x => x.Text == p);
                if (hit.Count() == 1) n.prevNodes.Add(hit.First());
                else if (hit.Count() == 0) startNodes.Add(n);
                else Console.WriteLine(n.Text + ": prevNodeName '" + p + 
                                                "' not found or not unique!" );
            }
        }
    }
    

    nextNodes 列表:

    public void fillNextNodes()
    {
        foreach (var n in theNodes)
        {
            foreach (var pn in n.prevNodes) pn.nextNodes.Add(n); 
        }
    }
    

    现在我们有了数据,需要布置节点。这个 布局很简单,但与分支数据一样,需要递归:

    public void layoutNodeX()
    {
        foreach (NetNode n in startNodes) layoutNodeX(n, n.VX + 1);
    }
    
    public void layoutNodeX(NetNode n, float vx)
    {
        n.VX = vx;
        foreach (NetNode nn in n.nextNodes)   layoutNodeX(nn, vx + 1);
    }
    

    垂直的 计数 每个x位置的节点,并将它们平均展开。A Dictionary 弗斯特 我们把它填好, 然后 最后 居中 他们..:

        public void layoutNodeY()
        {
            NetNode n1 = startNodes.First();
            n1.VY = 0;
            Dictionary<float, List<NetNode>> nodes = 
                              new Dictionary<float, List<NetNode>>();
    
            foreach (var n in theNodes)
            {
                if (nodes.Keys.Contains(n.VX)) nodes[n.VX].Add(n);
                else nodes.Add(n.VX, new List<NetNode>() { n });
            }
    
            for (int i = 0; i < nodes.Count; i++)
            {
                int c = nodes[i].Count;
                for (int j = 0; j < c; j++)
                {
                    nodes.Values.ElementAt(i)[j].VY = 1f * j - c / 2;
                }
            }
    
            float min = theNodes.Select(x => x.VY).Min();
            foreach (var n in theNodes) n.VY -= min;
        }
    

    Form 用一个 PictureBox :

    NodeChart NC = new NodeChart();
    
    private void Form1_Load(object sender, EventArgs e)
    {
        NC.theNodes.Add(new NetNode("A",""));
        NC.theNodes.Add(new NetNode("B","A"));
        NC.theNodes.Add(new NetNode("C","B"));
        NC.theNodes.Add(new NetNode("D","B"));
        NC.theNodes.Add(new NetNode("T","B"));
        NC.theNodes.Add(new NetNode("E","C"));
        NC.theNodes.Add(new NetNode("F","D,T"));
        NC.theNodes.Add(new NetNode("G","E,F"));
    
        NC.fillPrevNodes();
        NC.fillNextNodes();
        NC.layoutNodeX();
        NC.layoutNodeY();
    
        pictureBox1.Invalidate();
    }
    
    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        if (NC.theNodes.Count <= 0) return;
        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        foreach (var n in NC.theNodes) n.draw(e.Graphics, 100, 33);
    }
    

    除了前面提到的内容之外,您可能还需要添加一个y缩放或“leading”参数来垂直分布节点,以便为额外的文本腾出更多空间。。

    更新:

    enter image description here

    我做了一些改变:

    • 我把第二个“35.2”改成了“35.3”。你的意思可能是“25.2”,但这会带来更多的数据错误;你应该照顾好他们!一定要检查输出窗格!!
    • 我把天平换成了 n.draw(e.Graphics, 50, 30); Paint
    • 最后我把字号改成 Font("Consolas", 10f)

    您还必须确保pbox足够大和/或停靠/锚定以允许使用窗体调整大小。

    推荐文章