Ejemplo n.º 1
0
 public LadderGraph()
 {
     this.lchart = null;
     vertexs     = new List <LGVertex>();
     starts      = new List <LGVertex>();
     terminates  = new List <LGVertex>();
     edges       = new List <LGEdge>();
 }
Ejemplo n.º 2
0
        /// <summary>
        /// 初始化
        /// </summary>
        public LadderGraph(LadderChart lchart, int size)
        {
            this.lchart = lchart;
            vertexs     = new List <LGVertex>();
            starts      = new List <LGVertex>();
            terminates  = new List <LGVertex>();
            edges       = new List <LGEdge>();

            for (int i = 1; i <= size; i++)
            {
                vertexs.Add(new LGVertex(i));
            }
        }
Ejemplo n.º 3
0
        public void Setup(LadderChart lchart)
        {
            this._lchart = lchart;
            //RowCount = lchart.Heigh;

            /*
             * IntPoint pos = null;
             * SimuViewBaseModel svbmodel = null;
             * SimuViewVLineModel svvmodel = null;
             *
             * foreach (LCNode lcnode in lchart.Nodes)
             * {
             *  if (lcnode.Type == 0x00)
             *  {
             *      if (lcnode.HAccess)
             *      {
             *          pos = new IntPoint(lcnode.X, lcnode.Y);
             *          svbmodel = new SimuViewHLineModel(_root);
             *          _elements[pos] = svbmodel;
             *      }
             *  }
             *  else
             *  {
             *      pos = new IntPoint(lcnode.X, lcnode.Y);
             *      svbmodel = SimuViewBaseModel.Create(_root, lcnode.ToShowString());
             *      _elements[pos] = svbmodel;
             *  }
             *  if (lcnode.VAccess)
             *  {
             *      pos = new IntPoint(lcnode.X - 1, lcnode.Y);
             *      svvmodel = new SimuViewVLineModel(_root);
             *      _vlines[pos] = svvmodel;
             *  }
             * }
             */
        }
Ejemplo n.º 4
0
        /// <summary>
        /// 内部的表达式转逻辑图方法
        /// </summary>
        /// <param name="expr"></param>
        /// <param name="lgvstart"></param>
        /// <param name="lgvend"></param>
        /// <param name="flag"></param>
        /// <returns></returns>
        static private LadderGraph _GenLadderGraph(string expr, LGVertex lgvstart = null, LGVertex lgvend = null, int flag = 0)
        {
            int         estart = 0, eend = expr.Length - 1;
            LadderChart lchart = new LadderChart();
            LadderGraph lgraph = new LadderGraph();

            lgraph.LChart = lchart;
            // 若起始点未给定则添加
            if (lgvstart == null)
            {
                lgvstart = new LGVertex(++lgvtop);
            }
            // 若结束点未给定则添加
            if (lgvend == null)
            {
                lgvend = new LGVertex(++lgvtop);
            }
            //Console.WriteLine(expr.Substring(start, end - start + 1));
            // 当前表达式被括号包围时拆掉括号
            int bracket = 0;

            while (expr[estart] == '(' && expr[eend] == ')')
            {
                estart++;
                eend--;
            }
            // 当前单元的结尾
            int uend = estart;

            // CASE 1:查询并处理表达式中的或运算符(优先级最高)
            if ((flag & FLAG_HASOR) == 0)
            {
                bracket = 0;
                while (uend <= eend && (bracket > 0 || expr[uend] != '|'))
                {
                    if (expr[uend] == '(')
                    {
                        bracket++;
                    }
                    if (expr[uend] == ')')
                    {
                        bracket--;
                    }
                    uend++;
                }
                if (uend <= eend && expr[uend] == '|')
                {
                    // 先生成或运算符前的部分的逻辑图
                    LadderGraph lg1 = _GenLadderGraph(expr.Substring(0, uend), lgvstart, lgvend, flag | FLAG_HASOR);
                    // 再生成或运算符后的部分的逻辑图
                    LadderGraph lg2 = _GenLadderGraph(expr.Substring(uend + 2), lgvstart, lgvend, flag);
                    // 合并两个逻辑图
                    lgraph.Vertexs    = lg1.Vertexs.Union(lg2.Vertexs).ToList();
                    lgraph.Starts     = lg1.Starts;
                    lgraph.Terminates = lg1.Terminates;
                    lgraph.Edges      = lg1.Edges.Concat(lg2.Edges).ToList();
                    // 合并两个梯形图

                    /*
                     * 因为两个字表达式属于或的关系,所以要在梯形图上体现出来并联的结构
                     * 合并梯形图时,要注意以下几点
                     * 1. 将对应的梯形图上下对接,并且首尾相连
                     * 2. 如果两个梯形图的宽度不一致,另一个要额外向前设立线路,然后再对接
                     */
                    // 两个相邻的梯形图元件
                    LCNode lcn1 = null, lcn2 = null;
                    foreach (LCNode lcn3 in lg2.LChart.Nodes)
                    {
                        // 编号不能相同,所以要加上梯形图1的元件数量,建立新的编号域
                        lcn3.Id += lg1.LChart.Nodes.Count();
                        // 梯形图2放在梯形图1的下面,所以Y坐标要加上梯形图1的高度
                        lcn3.Y += lg1.LChart.Heigh;
                    }
                    // 建立合并后的新的梯形图,先将元件集合并
                    lchart.Nodes = lg1.LChart.Nodes.Union(lg2.LChart.Nodes).ToList();
                    // 宽度取两者最大,高度取两者相加
                    lchart.Width = Math.Max(lg1.LChart.Width, lg2.LChart.Width);
                    lchart.Heigh = lg1.LChart.Heigh + lg2.LChart.Heigh;
                    // 找到梯形图1的左端的最下元件
                    lcn1 = lg1.LChart.LeUpNode;
                    while (lcn1.Down != null)
                    {
                        lcn1 = lcn1.Down;
                    }

                    /*
                     * 将两个梯形图的左端对接
                     * 需要将上面的部分向下设立新的路线,并到达下面的左端
                     */
                    // 从该元件开始向下铺路
                    lcn1.VAccess = true;
                    for (int y = lcn1.Y + 1; y <= lg1.LChart.Heigh; y++)
                    {
                        // 新建下面的空元件
                        lcn2         = new LCNode(lchart.Nodes.Count() + 1);
                        lcn2.X       = lcn1.X;
                        lcn2.Y       = y;
                        lcn2.HAccess = false;
                        // 将向下联通设为true,能通到下面
                        lcn2.VAccess = true;
                        lcn1.Down    = lcn2;
                        lcn2.Up      = lcn1;
                        lchart.Nodes.Add(lcn2);
                        // 完成连接后,当前元件移动到下面
                        lcn1 = lcn2;
                    }
                    // 将铺路后最下面的元件和梯形图2的左上角对接
                    lcn2      = lg2.LChart.LeUpNode;
                    lcn1.Down = lcn2;
                    lcn2.Up   = lcn1;

                    /*
                     * 将两个梯形图的右端对接
                     * 需要将上面的部分向下设立新的路线,并到达下面的右端
                     */
                    // 如果梯形图1的宽度小于梯形图2

                    /*
                     * 先将图1的右上角拓宽,延伸到图2一样的宽度
                     * 然后再向下连接,和图2的右上角相连
                     */
                    if (lg1.LChart.Width < lg2.LChart.Width)
                    {
                        lcn1 = lg1.LChart.RiUpNode;
                        // 向右连接
                        for (int x = lg1.LChart.Width + 1; x <= lg2.LChart.Width; x++)
                        {
                            lcn2         = new LCNode(lchart.Nodes.Count() + 1);
                            lcn2.X       = x;
                            lcn2.Y       = lcn1.Y;
                            lcn2.HAccess = true;
                            lcn2.VAccess = false;
                            lcn1.Right   = lcn2;
                            lcn2.Left    = lcn1;
                            lchart.Nodes.Add(lcn2);
                            lcn1 = lcn2;
                        }
                        // 设置图1新的右上角
                        lg1.LChart.RiUpNode = lcn1;
                    }
                    else
                    // 如果梯形图1的宽度大于梯形图2

                    /*
                     * 将图2的右上角进行扩展,与图1的宽度一致
                     * 然后图1向下连接,直到到达图2新扩展的右下角
                     */
                    if (lg1.LChart.Width > lg2.LChart.Width)
                    {
                        lcn1 = lg2.LChart.RiUpNode;
                        // 向右连接
                        for (int x = lg2.LChart.Width + 1; x <= lg1.LChart.Width; x++)
                        {
                            lcn2         = new LCNode(lchart.Nodes.Count() + 1);
                            lcn2.X       = x;
                            lcn2.Y       = lcn1.Y;
                            lcn2.HAccess = true;
                            lcn2.VAccess = false;
                            lcn1.Right   = lcn2;
                            lcn2.Left    = lcn1;
                            lchart.Nodes.Add(lcn2);
                            lcn1 = lcn2;
                        }
                        // 设置图2新的右上角
                        lg2.LChart.RiUpNode = lcn1;
                    }

                    /*
                     * 经过上面的调整,这个时候图1和图2的宽度一致
                     * 但还是要考虑两个梯形图最右端的元件,
                     * 要注意元件是左端向下连接的,这样连接的可能会是右端的前一个线路
                     * --[]-----[]-----[]--
                     *              |
                     *              |
                     * --[]-----[]-----[]--
                     * 像这种右端存在非空元件时,会连接前面的部分
                     * 所以要将梯形图1再向右扩展一格,右上角向右再连一条向下的线路
                     * 这样保证连接的是最右端
                     * --[]-----[]-----[]--- [right]
                     *                     |
                     *                     |
                     * --[]-----[]-----[]---
                     */
                    // 如果两个梯形图其中之一的右端元件非空
                    if (!lg1.LChart.RiUpNode.Type.Equals(String.Empty) &&
                        !lg2.LChart.RiUpNode.Type.Equals(String.Empty))
                    {
                        // 向右添加线路
                        lcn1         = lg1.LChart.RiUpNode;
                        lcn2         = new LCNode(lchart.Nodes.Count() + 1);
                        lcn2.X       = lcn1.X + 1;
                        lcn2.Y       = lcn1.Y;
                        lcn2.HAccess = false;
                        lcn2.VAccess = true;
                        lcn1.Right   = lcn2;
                        lcn2.Left    = lcn1;
                        lchart.Nodes.Add(lcn2);
                        lg1.LChart.RiUpNode = lcn2;
                    }
                    lcn1 = lg1.LChart.RiUpNode;
                    while (lcn1.Down != null)
                    {
                        lcn1 = lcn1.Down;
                    }
                    lcn1.VAccess = true;
                    // 向下连接
                    for (int y = lcn1.Y + 1; y <= lg1.LChart.Heigh; y++)
                    {
                        lcn2         = new LCNode(lchart.Nodes.Count() + 1);
                        lcn2.X       = lcn1.X;
                        lcn2.Y       = y;
                        lcn2.HAccess = false;
                        lcn2.VAccess = true;
                        lcn1.Down    = lcn2;
                        lcn2.Up      = lcn1;
                        lchart.Nodes.Add(lcn2);
                        lcn1 = lcn2;
                    }
                    // 最下端和图2的右上角连接
                    lcn2      = lg2.LChart.RiUpNode;
                    lcn1.Down = lcn2;
                    lcn2.Up   = lcn1;
                    // 设置左上角和右上角
                    lchart.LeUpNode = lg1.LChart.LeUpNode;
                    lchart.RiUpNode = lg1.LChart.RiUpNode;

                    return(lgraph);
                }
            }
            // CASE 2:查询并处理表达式中的与运算符(优先级其次)
            if ((flag & FLAG_HASAND) == 0)
            {
                bracket = 0;
                uend    = estart;
                while (uend <= eend && (bracket > 0 || expr[uend] != '&'))
                {
                    if (expr[uend] == '(')
                    {
                        bracket++;
                    }
                    if (expr[uend] == ')')
                    {
                        bracket--;
                    }
                    uend++;
                }
                if (uend <= eend && expr[uend] == '&')
                {
                    // 添加中间点
                    LGVertex lgvmidium = new LGVertex(++lgvtop);
                    // 先生成或运算符前的部分的逻辑图
                    LadderGraph lg1 = _GenLadderGraph(expr.Substring(0, uend), lgvstart, lgvmidium, flag | FLAG_HASOR);
                    // 再生成或运算符后的部分的逻辑图
                    LadderGraph lg2 = _GenLadderGraph(expr.Substring(uend + 2), lgvmidium, lgvend, flag);
                    // 合并两个逻辑图
                    lgraph.Vertexs    = lg1.Vertexs.Union(lg2.Vertexs).ToList();
                    lgraph.Starts     = lg1.Starts;
                    lgraph.Terminates = lg2.Terminates;
                    lgraph.Edges      = lg1.Edges.Concat(lg2.Edges).ToList();
                    // 图2移动到图1的右端并合并,所以编号加上图1的总数,X坐标加上图1的宽度
                    foreach (LCNode lcn3 in lg2.LChart.Nodes)
                    {
                        lcn3.Id += lg1.LChart.Nodes.Count();
                        lcn3.X  += lg1.LChart.Width;
                    }
                    // 合并梯度图
                    lchart.Nodes    = lg1.LChart.Nodes.Union(lg2.LChart.Nodes).ToList();
                    lchart.Width    = lg1.LChart.Width + lg2.LChart.Width;
                    lchart.Heigh    = Math.Max(lg1.LChart.Width, lg2.LChart.Width);
                    lchart.LeUpNode = lg1.LChart.LeUpNode;
                    lchart.RiUpNode = lg2.LChart.RiUpNode;
                    // 图1和图2首尾相接
                    lg1.LChart.RiUpNode.Right = lg2.LChart.LeUpNode;
                    lg2.LChart.LeUpNode.Left  = lg1.LChart.RiUpNode;
                    // 图1需要向右连接
                    lg1.LChart.RiUpNode.HAccess = true;
                    return(lgraph);
                }
            }
            // CASE 3:当前为单一单元
            // 新建一个新的逻辑图
            lgraph.Starts.Add(lgvstart);
            lgraph.Terminates.Add(lgvend);
            lgraph.Vertexs.Add(lgvstart);
            lgraph.Vertexs.Add(lgvend);
            LCNode lcn = new LCNode(0);
            LGEdge lge = new LGEdge(lcn, lgvstart, lgvend);

            lgvstart.Edges.Add(lge);
            lgvend.BackEdges.Add(lge);
            lgraph.Edges.Add(lge);
            // 新建一个梯度图
            lchart.Width = lchart.Heigh = 1;
            lchart.Insert(lcn);
            // 对表达式进行解析
            // 函数调用形式表示的功能指令(func(a,b,c))
            if (expr[eend] == ')')
            {
                // 找到括号的起始位置和终止位置
                int ebstart = estart;
                int ebend   = eend;
                while (expr[ebstart] != '(')
                {
                    ebstart++;
                }
                // 得到函数名称和参数集合
                string   fname = expr.Substring(estart, ebstart - estart);
                string[] fargs = expr.Substring(ebstart + 1, ebend - ebstart - 1).Split(',');
                // 转换为LadderChart模式
                lcn.Type = fname;
                if (fargs.Length > 1)
                {
                    lcn[1] = fargs[0];
                }
                if (fargs.Length > 2)
                {
                    lcn[2] = fargs[1];
                }
                if (fargs.Length > 3)
                {
                    lcn[3] = fargs[2];
                }
                if (fargs.Length > 4)
                {
                    lcn[4] = fargs[3];
                }
                if (fargs.Length > 5)
                {
                    lcn[5] = fargs[4];
                }
                return(lgraph);
            }
            // 识别开始的非符号
            if (expr[estart] == '!')
            {
                // 识别非符号后面的立即符号( !imM0 )
                if (expr[estart + 1] == 'i' && expr[estart + 2] == 'm')
                {
                    lcn.Type = "LDIIM";
                    lcn[1]   = expr.Substring(estart + 3, eend - estart - 2);
                    return(lgraph);
                }
                // 一般的非符号( !M0 )
                lcn.Type = "LDI";
                lcn[1]   = expr.Substring(estart + 1, eend - estart);
                return(lgraph);
            }
            // 识别上升沿符号(ueM0)
            if (expr[estart] == 'u' && expr[estart + 1] == 'e')
            {
                lcn.Type = "LDP";
                lcn[1]   = expr.Substring(estart + 2, eend - estart - 1);
                return(lgraph);
            }
            // 识别下降沿符号(deM0)
            if (expr[estart] == 'd' && expr[estart + 1] == 'e')
            {
                lcn.Type = "LDF";
                lcn[1]   = expr.Substring(estart + 2, eend - estart - 1);
                return(lgraph);
            }
            // 识别立即符号(imM0)
            if (expr[estart] == 'i' && expr[estart + 1] == 'm')
            {
                lcn.Type = "LDIM";
                lcn[1]   = expr.Substring(estart + 2, eend - estart - 1);
                return(lgraph);
            }
            // 比较表达式的长度都不小于6
            if (eend - estart > 4)
            {
                // 找到比较符的位置
                int op = estart + 2;
                while (expr[op] != '=' && expr[op] != '<' && expr[op] != '>')
                {
                    op++;
                }
                // 识别比较符前的数据类型

                /*
                 * int datatype = 0;
                 * switch (expr[op - 1])
                 * {
                 *  case 'w': datatype = 1; break;
                 *  case 'd': datatype = 2; break;
                 *  case 'f': datatype = 3; break;
                 * }
                 */
                // 等比较(M0w=M1)
                if (expr[op] == '=')
                {
                    switch (expr[op - 1])
                    {
                    case 'w': lcn.Type = "LDWEQ"; break;

                    case 'd': lcn.Type = "LDDEQ"; break;

                    case 'f': lcn.Type = "LDFEQ"; break;
                    }
                    lcn[1] = expr.Substring(estart, op - 1 - estart);
                    lcn[2] = expr.Substring(op + 1, eend - op);
                    return(lgraph);
                }
                // 不等比较(M0w<>M1)
                if (expr[op] == '<' && expr[op + 1] == '>')
                {
                    switch (expr[op - 1])
                    {
                    case 'w': lcn.Type = "LDWNE"; break;

                    case 'd': lcn.Type = "LDDNE"; break;

                    case 'f': lcn.Type = "LDFNE"; break;
                    }
                    lcn[1] = expr.Substring(estart, op - 1 - estart);
                    lcn[2] = expr.Substring(op + 2, eend - op - 1);
                    return(lgraph);
                }
                // 小等比较(M0w<=M1)
                if (expr[op] == '<' && expr[op + 1] == '=')
                {
                    if (expr[op] == '<' && expr[op + 1] == '>')
                    {
                        switch (expr[op - 1])
                        {
                        case 'w': lcn.Type = "LDWLE"; break;

                        case 'd': lcn.Type = "LDDLE"; break;

                        case 'f': lcn.Type = "LDFLE"; break;
                        }
                        lcn[1] = expr.Substring(estart, op - 1 - estart);
                        lcn[2] = expr.Substring(op + 2, eend - op - 1);
                        return(lgraph);
                    }
                    return(lgraph);
                }
                // 大等比较(M0w>=M1)
                if (expr[op] == '>' && expr[op + 1] == '=')
                {
                    if (expr[op] == '<' && expr[op + 1] == '>')
                    {
                        switch (expr[op - 1])
                        {
                        case 'w': lcn.Type = "LDWGE"; break;

                        case 'd': lcn.Type = "LDDGE"; break;

                        case 'f': lcn.Type = "LDFGE"; break;
                        }
                        lcn[1] = expr.Substring(estart, op - 1 - estart);
                        lcn[2] = expr.Substring(op + 2, eend - op - 1);
                        return(lgraph);
                    }
                    return(lgraph);
                }
                // 小于比较(M0w<M1)
                if (expr[op] == '<')
                {
                    switch (expr[op - 1])
                    {
                    case 'w': lcn.Type = "LDWL"; break;

                    case 'd': lcn.Type = "LDDL"; break;

                    case 'f': lcn.Type = "LDFL"; break;
                    }
                    lcn[1] = expr.Substring(estart, op - 1 - estart);
                    lcn[2] = expr.Substring(op + 1, eend - op);
                    return(lgraph);
                }
                // 大于比较(M0w>M1)
                if (expr[op] == '>')
                {
                    switch (expr[op - 1])
                    {
                    case 'w': lcn.Type = "LDWG"; break;

                    case 'd': lcn.Type = "LDDG"; break;

                    case 'f': lcn.Type = "LDFG"; break;
                    }
                    lcn[1] = expr.Substring(estart, op - 1 - estart);
                    lcn[2] = expr.Substring(op + 1, eend - op);
                    return(lgraph);
                }
            }
            // 读取位(M0)
            lcn.Type = "LD";
            lcn[1]   = expr.Substring(estart, eend - estart + 1);
            return(lgraph);
        }
        public void Update()
        {
            int           rowid = 0;
            RowDefinition rdef;

            NetworkHeader.Text = String.Format("Network {0:d}", lnvmodel?.NetworkNumber);
            insts = new List <PLCOriginInst>();
            G_Inst.RowDefinitions.Clear();
            G_Inst.Children.Clear();
            rdef        = new RowDefinition();
            rdef.Height = new GridLength(20);
            TextBlock tberr = new TextBlock();

            tberr.Background = Brushes.Red;
            Grid.SetRow(tberr, 0);
            Grid.SetColumn(tberr, 0);
            Grid.SetColumnSpan(tberr, 6);
            G_Inst.RowDefinitions.Add(rdef);
            G_Inst.Children.Add(tberr);
            if (lnvmodel == null)
            {
                Status     = STATUS_ERROR;
                tberr.Text = App.CultureIsZH_CN()
                    ? "找不到 Network。"
                    : "Cannot found network.";
                return;
            }
            if (IsMasked)
            {
                tberr.Background = Brushes.Gray;
                tberr.Text       = String.Format(
                    App.CultureIsZH_CN() ? "Network {0:d} 已被屏蔽!" : "Network {0:d} has been masked!",
                    lnvmodel.NetworkNumber);
                return;
            }
            ismodified  = false;
            this.lchart = GenerateHelper.CreateLadderChart(lnvmodel.GetElements().Union(lnvmodel.GetVerticalLines()));
            if (lchart.checkOpenCircuit())
            {
                Status     = STATUS_OPEN;
                tberr.Text = String.Format(
                    App.CultureIsZH_CN() ? "Network {0:d} 的梯形图存在断路错误!" : "There have broken circuit in ladder of Network {0:d}.",
                    lnvmodel.NetworkNumber);
                return;
            }
            this.lgraph = lchart.Generate();
            if (lgraph.checkShortCircuit())
            {
                Status     = STATUS_SHORT;
                tberr.Text = String.Format(
                    App.CultureIsZH_CN() ? "Network {0:d} 的梯形图存在短路错误!" : "There have short circuit in ladder of Network {0:d}.",
                    lnvmodel.NetworkNumber);
                return;
            }
            if (lgraph.CheckFusionCircuit())
            {
                Status     = STATUS_FUSION;
                tberr.Text = String.Format(
                    App.CultureIsZH_CN() ? "Network {0:d} 的梯形图存在混连错误!" : "There have fusion circuit in ladder of Network {0:d}.",
                    lnvmodel.NetworkNumber);
                return;
            }
            Status = STATUS_ACCEPT;
            List <PLCInstruction> _insts       = lgraph.GenInst();
            SortedSet <int>       prototypeids = new SortedSet <int>();

            foreach (PLCInstruction inst in _insts)
            {
                insts.Add(inst.ToOrigin());
                if (inst.PrototypeID != -1)
                {
                    if (prototypeids.Contains(inst.PrototypeID))
                    {
                        Status     = STATUS_FUSION;
                        tberr.Text = String.Format(
                            App.CultureIsZH_CN() ? "Network {0:d} 的梯形图存在混连错误!" : "There have fusion circuit in ladder of Network {0:d}.",
                            lnvmodel.NetworkNumber);
                        return;
                    }
                    prototypeids.Add(inst.PrototypeID);
                }
            }
            G_Inst.RowDefinitions.Clear();
            G_Inst.Children.Clear();
            foreach (PLCOriginInst inst in insts)
            {
                rdef        = new RowDefinition();
                rdef.Height = new GridLength(20);
                G_Inst.RowDefinitions.Add(rdef);
                TextBlock tb = new TextBlock();
                tb.Text       = rowid.ToString();
                tb.Foreground = inst.ProtoType != null ? Brushes.Black : Brushes.Gray;
                tb.Background = (rowid & 1) == 0 ? Brushes.AliceBlue : Brushes.LightCyan;
                Grid.SetRow(tb, rowid);
                Grid.SetColumn(tb, 0);
                G_Inst.Children.Add(tb);
                for (int colid = 1; colid <= 6; colid++)
                {
                    tb            = new TextBlock();
                    tb.Text       = inst[colid - 1];
                    tb.Foreground = inst.ProtoType != null ? Brushes.Black : Brushes.Gray;
                    tb.Background = (rowid & 1) == 0 ? Brushes.AliceBlue : Brushes.LightCyan;
                    Grid.SetRow(tb, rowid);
                    Grid.SetColumn(tb, colid);
                    G_Inst.Children.Add(tb);
                }
                rowid++;
            }
            rdef        = new RowDefinition();
            rdef.Height = new GridLength(1, GridUnitType.Star);
            G_Inst.RowDefinitions.Add(rdef);
            G_Inst.Children.Add(Cursor);
            UpdateComment();
        }