当前位置:   article > 正文

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

c# 脚本编辑器

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

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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

(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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

(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("执行未编译代码!");	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

(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("编译成功。");
	}              
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

(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;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

(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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95

3、运行效果

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

4、说明

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

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

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

2、光标位置显示问题。

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

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/230372
推荐阅读
相关标签
  

闽ICP备14008679号