创建高级记事本(Notepad)






4.91/5 (25投票s)
在本文中,我们将创建一个记事本,它具有多标签文档、文档选择器、文件关联、打开/保存多个文件、行号、运行外部程序、在浏览器中查看文件、全屏模式等功能。
引言
我们都知道记事本是什么。但是现在,单文档编辑器很少使用。如果您想开发一个可以使用 C# 处理多个文档或文件的编辑器或 IDE,那么本文适合您。
您需要 Visual Studio Professional 2013 或更高版本以及 .NET Framework 4.5 或更高版本。
下载简单、自定义高级记事本的源代码和演示(.exe 文件)
在本文中,我们将创建一个可以单独处理多个文件或文档的记事本。
记事本中要开发的主要功能有新建、打开、保存、另存为、关闭、打印、运行等。
在本文中,我们将不涉及语法高亮,但如果您想为您的项目添加语法高亮,那么您可以使用已开发的编辑器,如 ICSharp code、Avalon Edit 或 EditorControl,或者您可以开发自己的编辑器。
在本文中,我们将只介绍如何处理打开、保存、关闭等主要功能。我们还将为我们的 RichTextBox
添加行号。请访问以下链接以了解如何为 RichTextBox
创建行号。
您还可以添加自动完成功能、自定义 Windows 窗体、自动完成括号、自定义控件。请访问以下链接以创建所有这些功能
如果您想创建自己的集成开发环境 (IDE),请参阅以下文章
我们还将用 C# 创建深色自定义记事本,请参见上方或下方的黑色图片。
要自定义窗体,请参阅文章 使用面板在 C# 中创建自定义 Windows 窗体
代码相同,只需更改自定义代码和设计
下载简单和自定义记事本的源代码。
请记住,两者具有相同的项目名称。
在使用面板在 C# 中创建自定义 Windows 窗体一文中,我们创建了另一个名为 CustomForm 的窗体,我们正在设计它,在主窗体中,我们只是更改其超类。在这里,我们只需要一个要自定义的窗体,即 MainForm。所以在这个项目中,我没有为自定义创建任何其他窗体。我按照我想要的方式自定义了主窗体,并在 MainForm.cs 文件中添加了代码。
Using the Code
我创建了一个包含行号的类 MyRichTextBox
。对于创建多个选项卡式文档,当用户新建或打开文件时,您不能直接将 RichTextBox
添加到 TabControl
。
您必须创建一个类(我正在使用 MyTabPage
),其超类为 TabPage
,它接受 MainForm
的对象,然后将此类添加到 TabControl
。
请记住对象的修饰符。
下载源代码以更好地理解。
这是 MyTabPage
类的代码。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
namespace AdvancedNotepad_CSharp
{
public class MyTabPage : System.Windows.Forms.TabPage
{
public MainForm mainform;
public MyRichTextBox _myRichTextBox = new MyRichTextBox();
public MyTabPage(MainForm mf)
{
mainform = mf;
this._myRichTextBox.Dock = DockStyle.Fill;
this._myRichTextBox.richTextBox1.Text = "";
_myRichTextBox.richTextBox1.Font =
new System.Drawing.Font("Monospaced", 11, FontStyle.Regular);
this._myRichTextBox.richTextBox1.Select();
_myRichTextBox.richTextBox1.TextChanged +=
new EventHandler(this.richTextBox1_TextChanged);
_myRichTextBox.richTextBox1.SelectionChanged +=
new EventHandler(this.richTextBox1_SelectionChanged);
_myRichTextBox.richTextBox1.LinkClicked +=
new LinkClickedEventHandler(this.richTextBox1_LinkClicked);
this.Controls.Add(_myRichTextBox);
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
String str = this.Text;
if (str.Contains("*"))
{
}
else
{
this.Text = str + "*";
}
}
private void richTextBox1_SelectionChanged(object sender, EventArgs e)
{
int sel = _myRichTextBox.richTextBox1.SelectionStart;
int line = _myRichTextBox.richTextBox1.GetLineFromCharIndex(sel) + 1;
int col = sel - _myRichTextBox.richTextBox1.GetFirstCharIndexFromLine(line - 1) + 1;
mainform.LineToolStripLabel.Text = "Line : " + line.ToString();
mainform.ColumnToolStripLabel.Text = "Col : " + col.ToString();
}
private void richTextBox1_LinkClicked(object sender, LinkClickedEventArgs e)
{
Process.Start(e.LinkText);
}
}
}
从 TabControl 中的当前选项卡获取 RichTextBox 控件
一旦您将选项卡添加到 TabControl
,如果您想从选定的选项卡中获取添加的 RichTextBox
控件,请使用以下语法
var object=(object)TabControl.TabPages[tabcontrol selected index].Controls[0];
例如:
var _myRichTextBox = (MyRichTextBox)myTabControlZ.TabPages[myTabControlZ.SelectedIndex].Controls[0];
好的,让我们转到主窗体。
创建 Windows 窗体应用程序后,添加 MenuStrip
、SplitContainer
。将 TreeView
(treeView1
) 添加到 panel1
,将 TabControl
添加到 splitcontainer
的 panel2
。请记住,您不能直接将 ContextMenuStrip
添加到 RichTextBox
,相反,您必须将 ContextMenuStrip
添加到 TabControl
中的每个添加的 RichTextBox
。下载源代码以更好地理解。
现在让我们看一些重要的函数代码。
1) 打开
此函数将通过将 MultiSelect
属性设置为 true
,从 OpenFileDialog
中选择文件来打开多个文件;让我们看看创建选项卡、一次读取一个文件并将该选项卡添加到 tabcontrol
的代码。另外,将 ContextMenuStrip
添加到 RichTextBox
。
private void File_Open_MenuItem_Click(object sender, EventArgs e)
{
StreamReader strReader;
String str;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
String[] files = openFileDialog1.FileNames;
foreach (string filename in files)
{
MyTabPage tabpage = new MyTabPage(this);
strReader = new StreamReader(filename);
str = strReader.ReadToEnd();
strReader.Close();
String fname = filename.Substring(filename.LastIndexOf("\\") + 1);
tabpage.Text = fname;
//add contextmenustrip to richTextBox1
tabpage._myRichTextBox.richTextBox1.ContextMenuStrip = myContextMenuStrip;
tabpage._myRichTextBox.richTextBox1.Text = str;
myTabControlZ.TabPages.Add(tabpage);
myTabControlZ.SelectedTab = tabpage;
this.UpdateDocumentSelectorList();
/* check (*) is available on TabPage Text
adding filename to tab page by removing (*) */
fname = tabpage.Text;
if (fname.Contains("*"))
{
fname = fname.Remove(fname.Length - 1);
}
tabpage.Text = fname;
//adding filenames to OpenedFilesList list
OpenedFilesList.Add(filename);
FilenameToolStripLabel.Text = filename;
this.Text = "Advanced Notepad in C# [ "+fname+" ]";
}
if (myTabControlZ.SelectedIndex >= 0)
{
var _myRichTextBox =
(MyRichTextBox)myTabControlZ.TabPages[myTabControlZ.SelectedIndex].Controls[0];
_myRichTextBox.richTextBox1.Select();
}
UpdateWindowsList_WindowMenu();
}
}
2) 保存、另存为、全部保存
嗯,你知道如何保存文件。所以我们只看“全部保存”功能,一次性保存所有文件,而不会将一个文件的内容覆盖到另一个文件中。请看上面的图片。在状态栏中,您可以看到完整的文件名路径。这是逐个保存所有文件的代码。
private void File_SaveAll_MenuItem_Click(object sender, EventArgs e)
{
if (myTabControlZ.TabCount > 0)
{
OpenedFilesList.Reverse();
TabControl.TabPageCollection tabcoll = myTabControlZ.TabPages;
foreach(TabPage tabpage in tabcoll)
{
myTabControlZ.SelectedTab = tabpage;
myTabControlZ_SelectedIndexChanged(sender, e);
if( ! tabpage.Text.Contains("Untitled"))
{
try
{
var _myRichTextBox =
(MyRichTextBox)myTabControlZ.TabPages
[myTabControlZ.SelectedIndex].Controls[0];
File.WriteAllText(FilenameToolStripLabel.Text, "");
StreamWriter strwriter = System.IO.File.AppendText
(FilenameToolStripLabel.Text);
strwriter.Write(_myRichTextBox.richTextBox1.Text);
strwriter.Close();
strwriter.Dispose();
}
catch { }
}
}
System.Windows.Forms.TabControl.TabPageCollection tabcollection =
myTabControlZ.TabPages;
foreach (TabPage tabpage in tabcollection)
{
String str = tabpage.Text;
if (str.Contains("*")&& !str.Contains("Untitled"))
{
str = str.Remove(str.Length - 1);
}
tabpage.Text = str;
}
UpdateWindowsList_WindowMenu();
}
}
3) 关闭
如果您使用过像 Notepad++ 这样的编辑器,它提供了关闭文件的功能。在这里,我们也将创建该功能。此功能不过是在保存文件后从 TabControl
中删除选项卡并从 treeView1
中删除节点。这是关闭文档的代码。下载源代码以查看“全部关闭”功能的代码。
private void File_Close_MenuItem_Click(object sender, EventArgs e)
{
if (myTabControlZ.TabCount > 0)
{
TabPage tabpage = myTabControlZ.SelectedTab;
if (tabpage.Text.Contains("*"))
{
DialogResult dg = MessageBox.Show("Do you want to save " +
tabpage.Text + " file before close ?",
"Save before Close ?", MessageBoxButtons.YesNo);
if (dg == DialogResult.Yes)
{
//save file before close
File_Save_MenuItem_Click(sender, e);
//remove tab
myTabControlZ.TabPages.Remove(tabpage);
//RemoveFileNamesFromTreeView(tabpage.Text);
this.UpdateDocumentSelectorList();
UpdateWindowsList_WindowMenu();
myTabControlZ_SelectedIndexChanged(sender, e);
LineToolStripLabel.Text = "Line";
ColumnToolStripLabel.Text = "Col";
if (myTabControlZ.TabCount == 0)
{
FilenameToolStripLabel.Text = "Advanced Notepad in C#";
}
}
else
{
//remove tab
myTabControlZ.TabPages.Remove(tabpage);
UpdateDocumentSelectorList();
UpdateWindowsList_WindowMenu();
myTabControlZ_SelectedIndexChanged(sender, e);
LineToolStripLabel.Text = "Line";
ColumnToolStripLabel.Text = "Col";
if (myTabControlZ.TabCount == 0)
{
FilenameToolStripLabel.Text = "Advanced Notepad in C#";
}
}
}
else
{
//remove tab
myTabControlZ.TabPages.Remove(tabpage);
RemoveFileNamesFromTreeView(tabpage.Text);
UpdateDocumentSelectorList();
UpdateWindowsList_WindowMenu();
myTabControlZ_SelectedIndexChanged(sender, e);
LineToolStripLabel.Text = "Line";
ColumnToolStripLabel.Text = "Col";
if (myTabControlZ.TabCount == 0)
{
FilenameToolStripLabel.Text = "Advanced Notepad in C#";
}
}
if (myTabControlZ.SelectedIndex >= 0)
{
var _myRichTextBox =
(MyRichTextBox)myTabControlZ.TabPages[myTabControlZ.SelectedIndex].Controls[0];
_myRichTextBox.richTextBox1.Select();
}
}
else
{
FilenameToolStripLabel.Text = "Advanced Notepad in C#";
LineToolStripLabel.Text = "Line";
ColumnToolStripLabel.Text = "Col";
}
}
TabControl 上下文菜单
请看上图,它为选定的 tabpage
提供了保存、关闭、关闭除此以外的所有、打开文件文件夹等功能。您可以在几乎所有高级编辑器(如 Notepad++)或 IDE(如 Visual Studio)中看到此功能。
您只需要创建上下文菜单条,向其中添加菜单,然后将此菜单条添加到选项卡控件中。
1) 关闭除此以外的所有
此函数执行的操作是删除所有其他选项卡,而不删除选定或当前选项卡。这是代码
private void myTabControl_CloseAllButThis_MenuItem_Click(object sender, EventArgs e)
{
String tabtext = myTabControlZ.SelectedTab.Text;
if (myTabControlZ.TabCount > 1)
{
TabControl.TabPageCollection tabcoll = myTabControlZ.TabPages;
foreach (TabPage tabpage in tabcoll)
{
myTabControlZ.SelectedTab = tabpage;
if (myTabControlZ.SelectedTab.Text != tabtext)
{
File_Close_MenuItem_Click(sender, e);
}
}
}
else if (myTabControlZ.TabCount == 1)
{
File_Close_MenuItem_Click(sender, e);
}
}
2) 打开文件文件夹
此函数打开文件保存的文件夹。这是代码
private void myTabControl_OpenFileFolder_MenuItem_Click(object sender, EventArgs e)
{
if(myTabControlZ.TabCount>0)
{
if( ! myTabControlZ.SelectedTab.Text.Contains("Untitled"))
{
if(FilenameToolStripLabel.Text.Contains("\\"))
{
TabPage tabpage = myTabControlZ.SelectedTab;
String tabtext = tabpage.Text;
if(tabtext.Contains("*"))
{
tabtext = tabtext.Remove(tabtext.Length - 1);
}
String fname = FilenameToolStripLabel.Text;
String filename=fname.Remove(fname.Length-(tabtext.Length+1));
Process.Start(filename);
}
}
}
}
文件关联
Visual Studio 允许您为应用程序添加特定的文件扩展名。要访问此属性,.NET Framework 版本必须大于或等于 3.5。要添加文件扩展名,请转到您的项目属性。
选择发布选项卡。单击选项按钮。一旦出现“发布选项”对话框,然后选择“文件关联”项,并在此处添加您的文件扩展名和图标。
那么如何在双击文件时在应用程序中打开文件呢?
嗯,这很简单。当您双击文件或右键单击文件并选择程序时,该文件名将作为参数传递给应用程序。
所以您只需在程序的主函数中打开该文件。首先,在 MainForm
中定义一个打开文件并接受文件名作为参数的函数。您必须将此函数修饰符定义为 public
。
在这里,定义一个函数,其代码与“打开文件”代码相同,但不使用 OpenFileDialog
,而是使用数组的 string
作为函数的参数。我使用了 OpenAssociatedFiles_WhenApplicationStarts(String[] files)
函数。
您必须在程序的主函数中调用此函数,该函数位于 Programs.cs 文件中。
首先,检查参数是否为 null
,如果为 null
,则调用 Application.Run(new MainForm());
否则创建 MainForm
对象并调用已定义的函数以打开这些文件 (OpenAssociatedFiles_WhenApplicationStarts(args);
)。
这是我的应用程序或 Program.cs 文件的主代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.IO;
namespace AdvancedNotepad_CSharp
{
static class Program
{
[STAThread]
static void Main(String[] args)
{
if (args != null && args.Length > 0)
{
String[] files = args;
MainForm mf = new MainForm();
mf.IsArgumentNull = false;
mf.OpenAssociatedFiles_WhenApplicationStarts(files);
Application.EnableVisualStyles();
Application.Run(mf);
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
}
鼠标点击树节点时选择选项卡
如您所知,每个编辑器都有一个文档选择器,用户可以在其中单击文档列表并激活该文档。
在选项卡式文档中,当您双击树节点时,会在选项卡控件中搜索与树节点相同的文本,如果文本匹配,则选择该选项卡。
这是选择选项卡的代码。
private void treeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
{
String str = treeView1.SelectedNode.ToString();
String st = str.Substring(str.LastIndexOf(":") + 2);
int treenode_length = st.Length;
int tab_count = myTabControlZ.TabCount;
System.Windows.Forms.TabControl.TabPageCollection tb = myTabControlZ.TabPages;
foreach (TabPage tabpage in tb)
{
String tabstr = tabpage.Text;
int tab_length = tabstr.Length;
if (tabstr.Contains(st))
{
myTabControlZ.SelectedTab = tabpage;
}
}
if (myTabControlZ.SelectedIndex >= 0)
{
var _myRichTextBox =
(MyRichTextBox)myTabControlZ.TabPages[myTabControlZ.SelectedIndex].Controls[0];
_myRichTextBox.richTextBox1.Select();
}
this.UpdateWindowsList_WindowMenu();
}
下载源代码以更好地理解并查看所有功能。
在这里,我只使用了简单的 RichTextBox
,只是展示了所有功能的操作,如打开、全部保存、关闭等,但您可以使用任何语法高亮文本编辑器。