Introduction
Well, we all know what Notepad is. But nowadays, a single document editor is rarely used. Consider you want to develop an editor or IDE that can handle multiple documents or files using C#, so this article is for you.
You require Visual Studio Professional 2013 or higher version and .NET framework 4.5 or higher.
Download the source code for Simple,Custom advanced notepad & also demo(.exe files)
In this article, we will create a Notepad that can handle multiple files or documents separately.
The kind of main functions that are there to develop in a Notepad are New, Open, Save, Save As, Close, Print, Run, etc.
In this article, we will not see Syntax Highlighting, but if you want to add syntax highlighting to your project, then you can use developed editors like ICSharp code, Avalon Edit or EditorControl, or you can develop your own editor.
In this article, we will see only how to handle main functions like Open, Save, Close, etc. We will also add Line Numbers to our RichTextBox
. Go to the following link to see how to create line numbers for RichTextBox
.
You can also add Auto Completion function, Custom Windows Form, Auto Complete Brackets, Custom Controls. Go to the following links to create all these functions:
If you want to create your own Integrated Development Environment(IDE) then see following article
We will also create dark custom notepad in C#,see above or following black image.
To customize a form see article Creating Custom Windows Forms in C# using Panels
The code is same only just change in customization code & in design
Download the source code for both simple & customize notepads.
Remeber both have same project names.
In Creating Custom Windows Forms In C# using Panels article, we created another form named CustomForm which we are designing & in mainform we just changing its super class. Here we need only one form to be customize which is a MainForm. so in this i didnt created any another form for customize.i have customized the main form in the way what i wanted & also adding codes in MainForm.cs file.
Using the Code
I have created a class with line numbers MyRichTextBox
. For creating multiple tabbed documents, you cannot directly add RichTextBox
to TabControl
when user creates a New or Open a file.
You have to create a class (I am using MyTabPage
) with its super class TabPage
that takes a object of MainForm
and then add this class to TabControl
.
Remember about Modifiers of objects.
Download the source code for better understanding.
Here's the code of MyTabPage
class.
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);
}
}
}
Getting RichTextBox Control from Current Tab in TabControl
Once you added a Tab to TabControl
and if you want to get the added RichTextBox
control from the selected tab, then use the following syntax:
var object=(object)TabControl.TabPages[tabcontrol selected index].Controls[0];
e.g.:
var _myRichTextBox = (MyRichTextBox)myTabControlZ.TabPages[myTabControlZ.SelectedIndex].Controls[0];
All right, let's move to the main form.
Once you created the Windows Forms Application, then add MenuStrip
, SplitContainer
. Add TreeView
(treeView1
) to panel1
and TabControl
to panel2
of splitcontainer
respectively. Remember you directly cannot add ContextMenuStrip
to RichTextBox
, rather than that, you must add ContextMenuStrip
to every added RichTextBox
to a TabControl
. Download the source code for better understanding.
Now let's see some important functions code.
1) Open
This function will open multiple files from selecting files from OpenFileDialog
by setting MultiSelect
property to true
; Let's see the code that creates tab, read each file at a time and add that tab to tabcontrol
. Also, add ContextMenuStrip
to 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;
tabpage._myRichTextBox.richTextBox1.ContextMenuStrip = myContextMenuStrip;
tabpage._myRichTextBox.richTextBox1.Text = str;
myTabControlZ.TabPages.Add(tabpage);
myTabControlZ.SelectedTab = tabpage;
this.UpdateDocumentSelectorList();
fname = tabpage.Text;
if (fname.Contains("*"))
{
fname = fname.Remove(fname.Length - 1);
}
tabpage.Text = fname;
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) Save, Save As, Save All
Well, you know how to save a file. So we will see only Save All function for saving all files at once without overwriting one file contents to another file contents. See the above image. In status strip, you can see full filename path. Here's the code to save all files one by one.
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) Close
If you have used any editor like Notepad++, it provides a function to close a file. Here, we will also create that function. This function is nothing but to remove tab from TabControl
and remove node from treeView1
after saving a file. Here's the code to close a document. Download the source code to view Close All function code.
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)
{
File_Save_MenuItem_Click(sender, e);
myTabControlZ.TabPages.Remove(tabpage);
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
{
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
{
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 ContextMenuStrip
See the above image that provides functions to selected tabpage
like Save, Close, Close All But This, Open File Folder, etc. You can see this function in almost all advanced editors like Notepad++ or IDEs like Visual Studio.
You just need to create context menu strip, adding menus to it and adding this menu strip to tabcontrol.
1) Close All But This
This function performs action like to remove all other tabs without removing selected or current tab. Here's the code:
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) Open File Folder
This function opens the folder where the file is saved. Here's the code:
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);
}
}
}
}
File Association
Visual Studio allows you to add specific file extension to the application. To access this property, the .NET Framework version must be greater than or equal to 3.5. For adding file extension, go to your Project Properties.
Select Publish tab. Click on Options button. Once "Publish Options" Dialog box appeared, then select "File Associations" item and add your file extension with icon here.
So how to open a file in the application when double clicking on file?
Well, it's so simple. When you double click on a file or right click on a file & select a program, that file name is passed as an argument to the application.
So you just have to open this file in the application in the main function of the program. First, define the function in the MainForm
that opens a file and takes a filename as an argument. You must define this function modifier to public
.
Here, define a function with code is same as Open file code without using OpenFileDialog
, instead that use string
of array as argument to function. I used OpenAssociatedFiles_WhenApplicationStarts(String[] files)
function.
You have to call this function in the main function of program which is in Programs.cs file.
First, check that the arguments are not null
, if it is null
, then call Application.Run(new MainForm());
otherwise create object of MainForm
and call the defined function that open these files(OpenAssociatedFiles_WhenApplicationStarts(args);
).
Here's the main code of my application or Program.cs file code:
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());
}
}
}
}
Select Tab when Mouse Click on Tree Node
As you know, every editor has a document selector where a user can click on list of document and that document is activated.
In Tabbed document,when you double click on tree node, then the same text of tree node is searched in tabcontrol, if that text is matched, then that tab is selected.
Here's the code to select the tab.
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();
}
Download the source code for better understanding and to view all functions.
Here, I just used only simple RichTextBox
, just showing about actions all the functions like Open, Save All, Close, etc., but you can use any Syntax Highlighting text editor.