C#和WPF实现图形化编程

C#和WPF实现图形化编程

图形化编程在很多领域都应用很多,比如儿童编程、硬件控制,目前最火的两个引擎肯定应该是scratch和google blocky,很多图形化编程软件都是基于这两个引擎,比如国内编程猫使用了scratch,makecode使用了google blocky。

使用其他引擎必然会收到很大的限制,不如要实现比如局部变量等基本功能时候,使用scratch就没法实现了,而且scratch和blocky都是基于javascript,如果要控制某些硬件,就要做很多javascript和其他硬件的通信模块,不是很方便,所以我们也许要自己写一个图形化编程工具。我工作一直用图形化工具控制硬件,但是老外的软件做的很一般,有天突然想是否自己也能写一个图形化编程工具,就花了点时间,用C#和WPF写了一个图形化工具,代码已经放在github下开源

代码地址 https://github.com/weihuajiang/WPF-Blocky

也在代码中实现了一个代码编辑器,和scratch一样的编辑器
代码编辑器
Scratch编辑器

程序语法树AST的实现

图形化程序也是程序,要显示和运行,我们必须要知道程序语法树(abstract syntax tree)的概念。语法树可以分为表达式Expression、语句Statement和声明Declaration。比如简单的一个语句a+b,就是一个BinaryExpression,而a+b;虽然多了分号,不过就编程了一个Statement语句,这个是个ExpressionStatement,其中的表达式就是BinaryExpression。更多AST的信
息大家可以看esprima解释的很详细。在我们程序中,我们定义了AST基类Node和三个基本的语法树结构Expression、Statement和Decaration(主要就是函数定义),与之对应基本控件也就只有三个控件,Expression控件、Statement控件和Function控件。WPF的数据绑定,让控件编写简化了很多。

程序的运行

程序的运行,简单来说就是各个语法树节点的运行,比如a+5;可以转换成以下结构:

            "type": "ExpressionStatement",
            "expression": {
                "type": "BinaryExpression",
                "operator": "+",
                "left": {
                    "type": "Identifier",
                    "name": "a"
                },
                "right": {
                    "type": "Literal",
                    "value": 5,
                    "raw": "5"
                }
            }

执行顺序是ExpressionStatement->BinaryExpression->left, right,返回left+right,left是Identifier是变量,需要从运行环境获得变量的值,right是Literal是整数5,这样就能获得运行的结果。

程序跳转的实现

break、continue和return,还有异常都会导致程序运行顺序发生跳转,我们通过程序返回值类型来实现程序的跳转。返回值总共有五种类型,分别对应五种可能情况,如果执行过程中出现异常,则应返回Exception,如果正确执行返回Value类型,循环会处理continue和break,函数处理return,如果执行过程中返回非Value,则应该把改返回类型返回,这样就解决了程序跳转的问题。

    public enum CompletionType
    {
        Value,
        Continue,
        Break,
        Return,
        Exception
    }
    public class Completion
    {
        public static Completion Void = new Completion();
        public Node Location { get; internal set; } = null;
        public CompletionType Type { get; internal set; } = CompletionType.Value;
        public object ReturnValue { get; internal set; } = null;

        public bool IsValue
        {
            get
            {
                return Type == CompletionType.Value;
            }
        }
        public bool IsException
        {
            get
            {
                return Type == CompletionType.Exception;
            }
        }
    }

运行环境的实现

我们使用Dictionary来保存运行运行环境的变量值,当前变量就保存在当前的Dictionary中,运行到一个位置,比如需要一个新的运行环境时,就生成一个新的环境,这样在这个环境中定义的变量在parent环境就无法访问,也就实现了局部变量的操作,同时当前环境可以访问前边所有parent环境的数据,也就是访问其他作用域的变量。

    public class ExecutionEnvironment
    {
        ExecutionEnvironment _parent=null;
        Dictionary<string, object> currentVariables = new Dictionary<string, object>();
        public ExecutionEnvironment()
        {
            _parent = null;
        }
        public ExecutionEnvironment(ExecutionEnvironment parent)
        {
            _parent = parent;
        }
        public bool HasValue(string variable)
        {
            if (currentVariables.ContainsKey(variable))
                return true;
            if (_parent != null)
                return _parent.HasValue(variable);
            return false;
        }
        public void RegisterValue(string variable, object value)
        {
            if (currentVariables.ContainsKey(variable))
                throw new Exception(Variable was defined already");
            currentVariables.Add(variable, value);
        }
        public object GetValue(string variable)
        {
            if (currentVariables.ContainsKey(variable))
            {
                return currentVariables[variable];
            }
            if (_parent != null)
                return _parent.GetValue(variable);
            throw new KeyNotFoundException();
        }
        public void SetValue(string variable, object value)
        {
            if (currentVariables.ContainsKey(variable))
            {
                currentVariables[variable] = value;
                return;
            }
            else if (_parent != null)
            {
                _parent.SetValue(variable, value);
                return;
            }
            else
                throw new KeyNotFoundException();
        }
    }

具体实现的例子

我们下边以if语句为例,看看具体如何实现语句的运行,if语句包含三部分,条件判断表达式Test,true时候运行的Consequent语句,false时候运行的Alternate语句,运行时候,我们先执行Test.ExecuteImpl语句,如果为true,则运行Consequent,false时候运行Alternate,不过在Consequent和Alternate运行时候,都产生了一个新的变量作用域,我们需要新建一个运行环境。


    public class IfStatement : Statement
    {
        public IfStatement()
        {
            Consequent = new BlockStatement();
        }
        public Expression Test { get; set; }
        public BlockStatement Consequent {get; set;}
        public BlockStatement Alternate {get; set;}
        protected override Completion ExecuteImpl(ExecutionEnvironment enviroment)
        {
            if (Test == null)
                return Completion.Void;
            var t = Test.Execute(enviroment);
            if (!t.Type.IsValue)
                return t;
            if (t.ReturnValue is bool)
            {
                if ((bool)t.ReturnValue)
                {
                    if (Consequent == null)
                        return Completion.Void;
                    ExecutionEnvironment current = new ExecutionEnvironment(enviroment);
                    return Consequent.Execute(current);
                }
                else
                {
                    if (Alternate == null)
                        return Completion.Void;
                    ExecutionEnvironment current = new ExecutionEnvironment(enviroment);
                    return Alternate.Execute(current);
                }
            }
            else
                return Completion.Exception("Not a boolean value", Test);
        }
    }

结语

githup上开源了编辑器的代码,大家可以下载看看,同时这个编辑器放在了微软商店里,链接是Visual Code Editor,如果想用微软商店直接安装,可以点击下边连接,
ms-windows-store://pdp/?productid=9NG2QVSXT34H
里边除了基本的语法功能,增加了更多类库和多语言等功能,比如python turtle一样的画板、翻译、语音、数学、数据结构等
在这里插入图片描述

This Wrox Blox teaches you how to add graphics to C# 2008 applications, explaining fundamental graphics techniques such as: drawing shapes with different colors and line styles; filling areas with colors, gradients, and patterns; drawing text that is properly aligned, sized, and clipped exactly where you want it; manipulating images and saving results in bitmap, JPEG, and other types of files. Also covered are instructions for how to greatly increase your graphics capabilities using transformations. Transformations allow you to move, stretch, or rotate graphics. They also let you work in coordinate systems that make sense for your application. You will also learn how to use all of these techniques in printouts. The author describes the sequence of events that produce a printout and shows how to generate and preview printouts. The final sections describe two powerful new graphic tools that were introduced with .NET Framework 3.0: WPF graphics and FlowDocuments. WPF applications can use XAML graphic commands to declaratively draw and fill the same kinds of shapes that a program can draw by using graphics objects. Finally, a discussion on the FlowDocument object shows you how to define items that should be flowed across multiple pages as space permits. This lets you display text, graphics, controls, and other items that automatically flow across page breaks. FlowDocument viewers make displaying these documents easy for you, and simplifies the user's reading of the documents. This Wrox Blox also contains 35 example programs written in C# 2008, although most of the code works in previous versions of C# as well. The most notable exceptions are WPF graphics and FlowDocuments, both of which require WPF provided in .NET Framework 3.0 and later.
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值