C#使用Winform实现简单的编辑器:编译、运行、关键字、注释高亮显示。

发布文章的目的即是学习也是分享保存。

1、简单的界面设计

程序分为脚本编辑框,操作、结果显示栏。 脚本编辑栏:可以编写自己想要的代码。
操作栏:其中脚本操作可以手动导入代码,也可以导出代码。预选为.txt文本格式,和支持其他格式。导出功能可以导出代码,保存格式只设置了.txt格式。
结果栏:点击编译显示编译结果,点击运行显示运行结果,填入命名空间、类名、方法可以简单显示指定的方法结果(建议是复制到文本框中,否则可能会报错)。

在这里插入图片描述

2、实现代码

(1)用到的变量字段

/// CSarpe关键字
private string[] _keyWords = { "abstract", "as", "base", "bool", 
"break", "byte", "case","catch","char", "checked", "class", "const", 
"continue", "decimal", "default","delegate", "do", "double", "else", 
"enum", "event", "explicit", "extern","false", "finally", "fixed", 
"float", "for", "foreach", "goto", "if", "implicit","in", "int", 
"interface", "internal", "is", "lock", "long", "namespace", "new",
"null", "object", "operator", "out", "override", "params", "partial", "private",
"protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short",
"sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw",
"true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using",
"virtual", "void", "volatile", "while","get","set" };

/// 选择文件
private OpenFileDialog openFileDialog1 = new OpenFileDialog();

/// 编译结果
CompilerResults Result = null;

/// 用于判断特殊按键不做rtb文本发生变化事件处理(如:Enter键、Table键)
bool isEnter = false;

(2)窗体初始化、加载

public Form1()
{
	InitializeComponent();
	this.MaximumSize = new System.Drawing.Size(760, 650);
	this.MinimumSize = new System.Drawing.Size(760, 650);
	this.rtbScriptCode.KeyDown += 
	new System.Windows.Forms.KeyEventHandler(this.rtbKeyDownEnter);
}
private void Form1_Load(object sender, EventArgs e)
{
	rtbScriptCode.Multiline = true;
	rtbScriptCode.WordWrap = true;
}

(3)执行操作:编译、运行

/// 点击编译按钮
private void btnCompile_Click(object sender, EventArgs e)
{
	if (rtbScriptCode.Text.ToString().Trim().Length < 1) 
	{ 
		AppendInfo("Code Null"); return; 
	}
	CompileCode();
	CompilerEffect();
}
/// 点击运行按钮
private void btn02Run_Click(object sender, EventArgs e)
{
 	if (rtbScriptCode.Text.ToString().Trim().Length < 1) 
 	{ AppendInfo("Code Null"); return; }
	CompileCode();
	// 通过反射,调用实例 
    if (Result != null)
	{
		if (tbNamespace.Text.Trim().Length < 1 || 
			tbMethodName.Text.Trim().Length < 1) return;
		try
		{
			 AppendInfo("开始运行.....");
			 Assembly objAssembly = Result.CompiledAssembly;
			 object objHelloWorld = 
			 objAssembly.CreateInstance($"{tbNamespace.Text}.{tbClassName.Text}");
			 MethodInfo objMI = objHelloWorld.GetType().GetMethod(tbMethodName.Text);
			 object ReValue = objMI.Invoke(objHelloWorld, null);
			 AppendInfo(ReValue);
		}
		catch (Exception ex)
		{
			AppendInfo("编译失败:");
			AppendInfo(ex.Message);
		}
	}
		else{	MessageBox.Show("执行未编译代码!");	}
}

(4)编译功能方法

public void CompileCode()
{
      AppendInfo("编译开始.....");
      //【1】新建C#代码生成器和代码编译器的实例
      CodeDomProvider Provider = CodeDomProvider.CreateProvider("CSharp");
      //【2】配置用于调用编译器的参数
      CompilerParameters Parameters = new CompilerParameters();
      Parameters.ReferencedAssemblies.Add("System.dll");
      Parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
      Parameters.ReferencedAssemblies.Add("System.Linq.dll");
      Parameters.GenerateExecutable = false;
      Parameters.GenerateInMemory = true;
      //【3】启动编译
      Result = Provider.CompileAssemblyFromSource(Parameters, rtbScriptCode.Text);
      if (Result.Errors.HasErrors)
      {
			AppendInfo("编译错误:");
            foreach (CompilerError err in Result.Errors)
            {
             	AppendInfo(err.ErrorText);
            }
	}
    else
    {
    	AppendInfo("编译成功。");
	}              
}

(5)高亮显示

/// 关键字高亮效果
private void HighlightKeywords()
{
	rtbScriptCode.Font = new Font("Consolas", 12);
	foreach (string keyword in _keyWords)
	{
		int startIndex = 0; //索引
		//小于文本长度
		while (startIndex < rtbScriptCode.TextLength)
		{
			//仅定位关键字全字的搜索索引
			int index = rtbScriptCode.Find(keyword, startIndex, RichTextBoxFinds.WholeWord);
			if (index == -1) break;
			rtbScriptCode.SelectionStart = index;
			rtbScriptCode.SelectionLength = keyword.Length;
			rtbScriptCode.SelectionColor = Color.Blue;
			startIndex = index + keyword.Length;
		}
	}
}
 /// 注释高亮效果
private void HighlightComments()
{
	// 使用正则表达式查找所有注释,并将其文本颜色设置为灰色
	Regex regex = new Regex(@"(?://.*?$|/\*.*?\*/)",
					  RegexOptions.Multiline | RegexOptions.Singleline);
	MatchCollection matches = regex.Matches(rtbScriptCode.Text);
	int startIndex = rtbScriptCode.SelectionStart;
	int length = rtbScriptCode.SelectionLength;
	foreach (Match match in matches)
	{
		rtbScriptCode.Select(match.Index, match.Length);
		rtbScriptCode.SelectionColor = Color.Green;
	}
	rtbScriptCode.Select(startIndex, length);
}
/// 调用方法高亮显示
public void CompilerEffect()
{
	HighlightKeywords();
	HighlightComments();
}
/// 文本发生改变
private void rtbScriptCode_TextChanged(object sender, EventArgs e)
{
	int index = rtbScriptCode.SelectionStart; //插入位置
	if (!isEnter)
	{
		CompilerEffect();
		if (index != -1)
		{
			rtbScriptCode.SelectionStart = index;
			rtbScriptCode.SelectionLength = 0;
		}
		return;
	}
}

(6)其它

/// Table键效果
private void rtbScriptCode_KeyDown(object sender, KeyEventArgs e)
{
	if (e.KeyCode == Keys.Tab && !(rtbScriptCode.ReadOnly) && !(rtbScriptCode.Enabled))
	{
		int selectionStart = rtbScriptCode.SelectionStart;
		rtbScriptCode.SelectedText = "\t";
		rtbScriptCode.SelectionStart = selectionStart + 1;
		e.SuppressKeyPress = true;
	}
}
/// 文本框中指定按下指定键,不进行关键字高亮操作(高亮操作导致重画会)
private void rtbKeyDownEnter(object sender, KeyEventArgs e)
{
	if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Tab || e.KeyCode == Keys.Space) 
	isEnter = true;
	else isEnter = false;
}

/// 文本域鼠标右键
private void rtbScriptCode_MouseDown(object sender, MouseEventArgs e)
{
	if (e.Button.ToString().Equals("Right")) rtbScriptCode.SelectAll();
}

/// 追加字符
private void AppendInfo(object Info)
{
	tb01CompileResult.Text = Info + "\n\r";
}

/// 导出代码
private void btn03Save_Click(object sender, EventArgs e)
{
	if ((rtbScriptCode.Text.Trim().Length) < 1) return;
	string fileName = tbFileName.Text;
	string path = @"C:\Users\qin\Desktop\TeseCompile\" + $"{fileName}{labelFilePostfix.Text}";
	//确认保存,保存方式
	bool saveMethod = false;
	//保存文件
	if (DialogResult.Yes == MessageBox.Show(path, "保存", MessageBoxButtons.YesNo))
	{
		if (File.Exists(path))
		{
			//是否替换文件
			if (DialogResult.Yes == 
				MessageBox.Show("是否替换文件", "替换", MessageBoxButtons.YesNo))
				saveMethod = false;
			else
			{
				int i = 1;
				while (File.Exists(path))
				{
					tbFileName.Text = fileName + i.ToString();
					i++;
					path = @"C:\Users\qin\Desktop\TeseCompile\" + 
						   $"{tbFileName.Text}{labelFilePostfix.Text}";
					saveMethod = true;
				}
			}
		}
		//保存
		using (StreamWriter writer = new StreamWriter(path, saveMethod))
		{
		writer.Write(rtbScriptCode.Text);
		MessageBox.Show($"文件:{tbFileName.Text}{labelFilePostfix.Text}保存成功!");
		}
	}
}

/// 导入文件
private void btnImportCode_Click(object sender, EventArgs e)
{
	OpenFileDialog fileDialog = new OpenFileDialog();
	fileDialog.Multiselect = true;
	fileDialog.Title = "请选择文件";
	fileDialog.Filter = "文本文件(*.txt)|*.txt; | 所有文件(*.*)|*.*";
	if (fileDialog.ShowDialog() == DialogResult.OK)
	{
		rtbScriptCode.Text = "";
		string file = fileDialog.FileName;
		string fileName = fileDialog.SafeFileName;
		tbFileName.Text = fileName.Substring(0, fileName.Length - ".txt".Length);
		using (StreamReader read = new StreamReader(file))
		{
			string line = "";
			while ((line = read.ReadLine()) != null)
			{
				rtbScriptCode.AppendText(line + "\r\n");
			}
		}
	}
	rtbScriptCode.SelectionLength = rtbScriptCode.Text.Length;
	CompilerEffect();
}

3、运行效果

代码导入
运行效果
在这里插入图片描述

4、说明

2023-06-04
程序的不完善:

1、文本发生变化的重绘滚动(闪烁)问题。

程序中,使用RichTextBox控件编辑代码,每键入一个字符,都会触发文本变化事件,而关键字高亮显示是在文本发生变化后重绘的,所以会出现编写代码时存在闪烁问题。
测试发现如果不是导入的代码,不会发生闪烁问题(测试输入十行)。 测试编译时文本高亮显示无问题。

2、光标位置显示问题。

程序中,在导入代码后开始编辑,会发生类似滚动的现象(跟鼠标滚动一样)。 光标(焦点)跑位已解决。

3、关键字高亮显示后,直接在后面输入文字格式问题。

关键字后面的文本格式会跟关键字一样。

结语

既是分享,也是备份。

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。
如果有任何疑问或需要进一步的帮助,欢迎随时留言。
也可以加入微信公众号 [编程笔记in] 社区,与其他热爱技术的同行一起交流心得,共同成长!欢迎大家留言学习!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程笔记in

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值