Introduction
TabText
is a text-editor with tab-pages, like Excel.
The program has the capabilities of the following extensions, which have been published on The Code Project:
namespace Khendys.Controls
{
public class ExRichTextBox : RichTextBox
{
}
}
using Khendys.Controls;
namespace Nik.UserControls
{
public class RicherTextBox2 : ExRichTextBox
{
}
}
using Nik.UserControls;
namespace MicrosoftMSDN
{
public class RichTextBoxExt : RicherTextBox2
{
}
}
using MicrosoftMSDN;
namespace Compenkie
{
public class RichTextBoxExtend : RichTextBoxExt
{
}
}
TabText
is extended with:
- tab-pages, so you can easily switch between documents.
- added page setting of the
PageSetupDialog
to every page.
- serializes all the pages in one file.
- recent-file-list through an array of
ToolStripMenuItem
s.
- added a simple table. The table use the functionality of the
RichTextBox
to add more rows in the table, by using the Enter-key.
- adds functions for Find & Replace as described in the MSDN Library.
- inserts emoticons through a picture panel.
- inserts text fragments through a context menu.
- help pages.
- possibility to change the language.
- keyboard state.
- store the settings of the program in the registry.
TabText
makes a folder, My Documents\\Compenkie\\TabText1, to store and load data files. TabText
makes a Registry key, CurrentUser\\Compenkie\Tabext1, to store settings.
The construction of the program is as follows:
Black = Form = TabText
Red = SplitContainer = splitContaimer
Bleu = TabControl = tabControl
Green = RichTextBoxExtended (left) = richTextBox
Green = RichTextBox (right) = richTextBoxPreview
The richTextBoxPreview
is only to preview the text fragments.
Tab-Pages
Pages are made dynamic in the same way as the designer does. At every page is a rich-text box added, of type RichTextBoxExtended
.
public RichTextBoxExtend[] richTextBox = new RichTextBoxExtend[17];
for (int teller = 0; teller < 17; teller++)
{
richTextBox[teller] = new RichTextBoxExtend();
richTextBox[teller].AcceptsTab = true;
richTextBox[teller].Dock = System.Windows.Forms.DockStyle.Fill;
richTextBox[teller].Location = new System.Drawing.Point(3, 3);
richTextBox[teller].Name = "richTextBox" + teller.ToString();
richTextBox[teller].Size = new System.Drawing.Size(397, 380);
richTextBox[teller].TabIndex = teller;
richTextBox[teller].TabStop = true;
richTextBox[teller].Text = "";
richTextBox[teller].SelectionChanged +=
new EventHandler(TabText_SelectionChanged);
richTextBox[teller].CursorPositionChanged +=
new EventHandler(TabText_CursorPositionChanged);
richTextBox[teller].AllowDrop = false;
richTextBox[teller].DragDrop += new DragEventHandler(TabText_DragDrop);
richTextBox[teller].DragEnter += new DragEventHandler(TabText_DragEnter);
}
TabPage[] tabPages = new TabPage[17];
public void AddPage(int pagenummer)
{
tabPages[pagenummer] = new TabPage();
tabPages[pagenummer].Location = new System.Drawing.Point(4, 22);
tabPages[pagenummer].Name = "tabPage" + pagenummer.ToString();
tabPages[pagenummer].Padding = new System.Windows.Forms.Padding(3);
tabPages[pagenummer].Size = new System.Drawing.Size(403, 386);
tabPages[pagenummer].TabIndex = pagenummer;
tabPages[pagenummer].MouseHover += new EventHandler(TabText_MouseHover);
tabPages[pagenummer].MouseLeave += new EventHandler(TabText_MouseLeave);
tabPages[pagenummer].ToolTipText = "Dubbelklikken voor wijzigen naam tab";
tabPages[pagenummer].Text = "tabPage" + pagenummer.ToString();
tabPages[pagenummer].Tag = documentDir + "\\" +
tabPages[pagenummer].Text + ".rtf";
tabPages[pagenummer].UseVisualStyleBackColor = true;
tabPages[pagenummer].Controls.Add(richTextBox[pagenummer]);
tabControl.Controls.Add(tabPages[pagenummer]);
tabPages[pagenummer].SuspendLayout();
tabPages[pagenummer].ResumeLayout(false);
int aantal = tabControl.Controls.Count;
tabControl.SelectedIndex = aantal - 1;
this.Text = "TabText - " + tabPages[pagenummer].Text;
}
The pages are named "TapPage0", "Tabpage1", ... In the Text
property of the tap-page is the filename. In the Tag
property of the tap-page is the full path of the file. With a double-click on the tab, appears a textbox where you can change the filename. The rich-text box turns off DockStyle.Fill
so you can make space on the top of the tab control to show a TextBox
.
private void tabControl_DoubleClick(object sender, EventArgs e)
{
TabControl conTrol = (TabControl)sender;
index = conTrol.SelectedIndex;
richTextBox[index].Dock = DockStyle.None;
richTextBox[index].Location = new Point(0, 25);
richTextBox[index].Size = new Size(this.Size.Width, Size.Height - 25);
box = new TextBox();
box.Leave += new System.EventHandler(this.box_Leave);
box.Text = tabPages[index].Text.ToString();
tabPages[index].Controls.Add(box);
box.Show();
box.Focus();
}
If the focus leaves the textbox then the name will add to the text of the tab-page. If you do not give an extension then the standard extension rtf will be added. The full-name of the file will be added to the Tag
property of the tab-control.
private void box_Leave(object sender, EventArgs e)
{
TextBox box = (TextBox)sender;
tabPages[index].Text = box.Text.ToString();
if (Path.GetExtension(tabPages[index].Text.ToString()) == "")
tabPages[index].Tag = documentDir + "\\" +
tabPages[index].Text + ".rtf";
else
tabPages[index].Tag = documentDir + "\\" + tabPages[index].Text;
box.Hide();
richTextBox[index].Dock = DockStyle.Fill;
}
Load File
LoadFile
will be accessed by:
- the menu-item Load.
- the recent-file menu-item Load.
The Modified
property will be initially set to false
. By editing the rich-text box, the property becomes true
. This will be used in the Save routine. The full name of the file will be stored in the Tag
property, and the file will be add to the recent file list. If present, then first remove and then add. If the the extension is RTF, then load as an RTF-file, in all other cases, as a text-file.
private void loadFile(string fileName)
{
TabPage tab = tabControl.SelectedTab;
try
{
int index = ZoekTab();
string extentie = Path.GetExtension(fileName);
if (extentie == ".rtf")
richTextBox[index].LoadFile(fileName,
RichTextBoxStreamType.RichText);
else
richTextBox[index].LoadFile(fileName,
RichTextBoxStreamType.PlainText);
richTextBox[index].Modified = false;
tab.Tag = fileName; ;
tab.Text = Path.GetFileName(fileName);
this.Text = "TabText - " + tab.Text;
removeRecentFile(fileName);
addRecentFile(fileName);
}
catch (IOException ex)
{
removeRecentFile(fileName);
Trace.WriteLine(ex.Message, "Error loading from file");
}
}
Save File
The file is normally saved as TXT or RTF, depending on the extension of the filename. The default is "rtf". If a file is edited, you will be asked if you want to save it.
private void saveFile(TabPage tab)
{
int index = ZoekTab();
if (richTextBox[index].Modified == true)
{
string messageString = "Save file " + tab.Text.ToString() + "?";
if (MessageBox.Show(messageString, "TabText1",
MessageBoxButtons.YesNo) == DialogResult.Yes)
{
string directorie = Path.GetDirectoryName(tab.Tag.ToString());
if (Directory.Exists(directorie) == false)
Directory.CreateDirectory(directorie);
string extentie = Path.GetExtension(tab.Tag.ToString());
if (extentie == ".rtf")
richTextBox[index].SaveFile(tab.Tag.ToString(),
RichTextBoxStreamType.RichText);
else
richTextBox[index].SaveFile(tab.Tag.ToString(),
RichTextBoxStreamType.PlainText);
richTextBox[index].Modified = false;
}
}
addRecentFile(tab.Tag.ToString());
}
Recent File List
The recent file-list is made by an array of ToolStripMenuItem
s.
private ToolStripMenuItem[] recentFiles = new ToolStripMenuItem[11];
int maxRecent;
In the variable maxRecent
is the actual number of recent files stored.
In the Load
event of the form, the array will be filled. In the property ToolTipText
is the path of the file stored, so we will have a nice tooltip.
RegistryKey key = Registry.CurrentUser.OpenSubKey(strRegKey +
"Recent File List\\");
if (key != null)
{
try
{
for (maxRecent = 0; maxRecent < 10; maxRecent++)
{
string sKey = "file" + maxRecent.ToString();
string longfileNaam = (string)key.GetValue(sKey, "");
if (longfileNaam.Length == 0)
break;
recentFiles[maxRecent].ToolTipText =
longfileNaam.ToString();
recentFiles[maxRecent].Text =
GetShortDisplayName(longfileNaam, 40);
}
}
catch (Exception ex)
{
Trace.WriteLine("Loading Recent Files" +
" from Registry failed: " + ex.Message);
}
key.Close();
}
if (recentFiles[0].Text != "")
loadFile(recentFiles[0].ToolTipText.ToString());
If a file is present initially, it will be opened and a tap-page will be made.
Serialization
With serialization, all the tab-pages are stored in one file. A BinaryFormatter
will be used to store data-types. For every page, we store the file-name and the full path-name of the file. Also will be saved the page settings of the PageSetupDialog
for every TabPage
. After selecting the complete content of the rich-text box, save the content. With a for
-loop, we will then save all the pages.
private void serializeFileDialog_FileOk(object sender, CancelEventArgs e)
{
sbpMenu.Text = "Serialize alle pagina's";
Stream stream = File.Open(serializeFileDialog.FileName, FileMode.Create);
BinaryFormatter bformatter = new BinaryFormatter();
bformatter.Serialize(stream, maxTab);
for (int teller = 0; teller < maxTab; teller++)
{
bformatter.Serialize(stream, richTextBox[teller].printLandScape);
bformatter.Serialize(stream, richTextBox[teller].printMarginTop);
bformatter.Serialize(stream, richTextBox[teller].printMarginLeft);
bformatter.Serialize(stream, richTextBox[teller].printMarginRight);
bformatter.Serialize(stream, richTextBox[teller].printMarginBottom);
bformatter.Serialize(stream, tabPages[teller].Text.ToString());
bformatter.Serialize(stream, tabPages[teller].Tag.ToString());
richTextBox[teller].SelectAll();
bformatter.Serialize(stream, richTextBox[teller].SelectedRtf);
richTextBox[teller].DeselectAll();
}
stream.Close();
sbpMenu.Text = "Gereed";
}
To get back the pages from a serialized file, you must work in the same order as the tap-pages are serialized. We start to save the existed tab pages and clear the tab control. After loading the number of tab pages, we add the number of tab pages and clear the old content of the rich-text box. Of course, we load the page settings of the PageSetupDialog
and the properties text and the tag, before the count of the rich-text box is loaded.
private void deserializeFileDialog_FileOk(object sender, CancelEventArgs e)
{
sbpMenu.Text = "Deserialize alle pagina's";
saveAll();
tabControl.TabPages.Clear();
Stream stream = File.Open(deserializeFileDialog.FileName, FileMode.Open);
BinaryFormatter bformatter = new BinaryFormatter();
maxTab = (int)bformatter.Deserialize(stream);
for (int teller = 0; teller < maxTab; teller++)
{
AddPage(teller);
richTextBox[teller].Clear();
richTextBox[teller].printLandScape =
(bool)bformatter.Deserialize(stream);
richTextBox[teller].printMarginTop =
(int)bformatter.Deserialize(stream);
richTextBox[teller].printMarginLeft =
(int)bformatter.Deserialize(stream);
richTextBox[teller].printMarginRight =
(int)bformatter.Deserialize(stream);
richTextBox[teller].printMarginBottom =
(int)bformatter.Deserialize(stream);
tabPages[teller].Text = bformatter.Deserialize(stream).ToString();
tabPages[teller].Tag = bformatter.Deserialize(stream).ToString();
richTextBox[teller].SelectedRtf =
bformatter.Deserialize(stream).ToString();
}
stream.Close();
sbpMenu.Text = "Gereed";
}
Emoticons
We can add little pictures, which can be chosen from a palette.
The palette is a Panel
with picture boxes. The Panel
is created in the Load
-event of the form. The pictures-file is made in Emotion_collector
. The serialized file is embedded as a resource file. The file will loaded in a MemoryStream
. In the Options box, you can choose to load a picture file from disk.
images = new ImageInfo[maxImage];
for (int teller = 0; teller < maxImage; teller++)
images[teller] = new ImageInfo();
Loading a a serialized file from the resource will be done with a MemoryStream
, the same way as loading a serialized file in the tab pages.
try
{
MemoryStream stream = new MemoryStream(Resources._default);
BinaryFormatter bformatter = new BinaryFormatter();
imageCounter = (int)bformatter.Deserialize(stream);
for (int teller = 0; teller < imageCounter; teller++)
{
string dummy = bformatter.Deserialize(stream).ToString();
dummy = bformatter.Deserialize(stream).ToString();
images[teller].img = (Image)bformatter.Deserialize(stream);
}
stream.Close();
}
catch (ArgumentException ex)
{
MessageBox.Show(ex.Message.ToString());
}
createImagePanel(images, imageCounter);
The image panel will be shown by clicking on the menu item. The palette must first be hidden, because there is an error if you click twice on the menu-item.
private void contextmenuemoticonsToolStripMenuItem_Click(object sender,
EventArgs e)
{
ToolStripMenuItem item = (ToolStripMenuItem)sender;
EventArgs args = (EventArgs)e;
if (showForm != null)
{
showForm.Hide();
showForm.Location = new Point(rectNormal.X + 200,
rectNormal.Y + 200);
showForm.Show();
}
else
MessageBox.Show("No Icons selectede");
}
A picture will be added by clicking on a PictureBox
. The object sender contains the PictureBox
.
void box_MouseClick(object sender, MouseEventArgs e)
{
PictureBox box = (PictureBox)sender;
showForm.Hide();
int index = ZoekTab();
try
{
richTextBox[index].InsertImage(box.Image);
}
catch (Exception _e)
{
MessageBox.Show("Rtf Image Insert Error\n\n" +
_e.ToString());
}
}
Text-Fragments
Using the same way, you can insert text-fragments. Standard text fragments are stored in an array of ToolStripMenuItem
s. I have four date-items made, and these items get added to a context menu. The context menu will be shown by clicking on the menu item:
private void contextMenuToolStripMenuItem_Click(object sender, EventArgs e)
{
contextMenu_Text.Show(this, new Point(200, 200));
}
The text-fragments are loaded in the Tag
-property of the menu-item. The bool
-variable standaard
is true
if the text is plain text. If the the text is in RTF-format, then standaard
is false
.
DateTime time = DateTime.Now;
toolStripMenuDatumKort.Tag = time.ToShortDateString() + "\n";
toolStripMenuDatumLang.Tag = time.ToLongDateString() + "\n";
toolStripMenuTijdKort.Tag = time.ToShortTimeString() + "\n";
toolStripMenuTijdLang.Tag = time.ToLongTimeString() + "\n";
toolStripMenuDatumKort.ToolTipText =
time.ToShortDateString() + "\n";
toolStripMenuDatumLang.ToolTipText =
time.ToLongDateString() + "\n";
toolStripMenuTijdKort.ToolTipText =
time.ToShortTimeString() + "\n";
toolStripMenuTijdLang.ToolTipText =
time.ToLongTimeString() + "\n";
standaard = true;
When you move the mouse over the context menu items, a preview window will show the items. In the constructor of TabText
is set the SplitterDistance
equal to the splitter width.
splitContainer.SplitterDistance = splitContainer.Size.Width;
In the MouseHover
event, the SplitterDistance
will be set on two-thirds of the width and the preview-window will be shown.
private void cmenu_Teksten_MouseHover(object sender, EventArgs e)
{
ToolStripMenuItem item = (ToolStripMenuItem)sender;
splitContainer.SplitterDistance = (splitContainer.Size.Width / 3) * 2;
richTextBoxPreview.Clear();
if (standaard)
richTextBoxPreview.AppendText(item.Tag.ToString());
else
richTextBoxPreview.AppendRtf(item.Tag.ToString());
}
When the context-menu is closed, the preview-window will disappear with the event Closed
of the context menu.
private void cmenu_Teksten_Closed(object sender,
ToolStripDropDownClosedEventArgs e)
{
splitContainer.SplitterDistance = splitContainer.Size.Width;
}
Text-fragments can be made with serialization of the tab-pages. In the Options box, you can choose and load a text-fragment-file from disk.
Help
There is help for the user available in three ways:
- the usual tooltips.
- extra information on the status bar.
- tap-pages with user-information.
Information for the status bar is stored in the Tag
property of a menu item. When you move with the mouse over a menu item, a MouseHover
event takes place and shows the info on the status bar.
private void toolStripMenuItem_MouseHover(object sender, EventArgs e)
{
ToolStripMenuItem menu = (ToolStripMenuItem)sender;
sbpMenu.Text = (string)menu.Tag;
}
If the mouse leaves a menu-item, then the MouseLeave
event occurs and resets the text.
private void toolStripMenuItem_MouseLeave(object sender, EventArgs e)
{
sbpMenu.Text = "Ready";
}
In the Help menu, there is help available for the user. Click on Help in the Help menu and the split container changes for a tab control with tab-pages. The most important thing here is the order of adding of the help control and the menu control.
private void helpHelpToolStripMenuItem_Click(object sender, EventArgs e)
{
Controls.Remove(this.splitContainer);
Controls.Remove(this.comboStrip);
Controls.Remove(this.buttonStrip);
Controls.Remove(this.menuStrip);
Controls.Remove(this.statusStrip);
Controls.Add(helpControl);
Controls.Add(helpMenu);
}
Clicking on the back-button of the help menu will change the help menu with the split container.
public void backToolStripMenuItem_Click(object sender, EventArgs e)
{
Controls.Add(splitContainer);
Controls.Add(comboStrip);
Controls.Add(buttonStrip);
Controls.Add(menuStrip);
Controls.Add(statusStrip);
Controls.Remove(helpControl);
Controls.Remove(helpMenu);
}
The help menu control is declared as a variable. You can see that, it is exactly the same as deserialized tabpages, except that there is a memory-stream used.
this.TabPages.Clear();
MemoryStream stream =new MemoryStream(Resources.HelpFile);
BinaryFormatter bformatter = new BinaryFormatter();
int maxTab = (int)bformatter.Deserialize(stream);
for (int teller = 0; teller < maxTab; teller++)
{
AddPage(teller);
richTextBox[teller].Clear();
tabPage[teller].Text = bformatter.Deserialize(stream).ToString();
tabPage[teller].Tag = bformatter.Deserialize(stream).ToString();
richTextBox[teller].SelectedRtf = bformatter.Deserialize(stream).ToString();
}
stream.Close();
Changing languages
Because the information is stored in three properties of a ToolStripMenuItem
, you can easily switch the language option;
private void languagetoolStripComboBox_SelectedIndexChanged(object sender,
EventArgs e)
{
ToolStripComboBox box = (ToolStripComboBox)sender;
language = box.Text.ToString();
switch (box.Text)
{
case "Nederlands":
{
Nederlands();
break;
}
case "English":
{
English();
break;
}
}
}
A part of the function English
looks like:
void English()
{
fileToolStripMenuItem.Text = "&File";
newToolStripMenuItem.Tag = "Save the ecxist files, close " +
"all the tab-pages and" +
" make a new tab-page";
newToolStripMenuItem.Text = "&New";
newToolStripMenuItem.ToolTipText = "Make a new document";
....
newToolStripButton.Tag = newToolStripMenuItem.Tag.ToString();
newToolStripButton.ToolTipText =
newToolStripMenuItem.ToolTipText.ToString();
}
Embedded resources
Here, in short, you will see how you can add an embedded resource to your project:
- Make tab-pages as needed, enter the text, and format it.
- Serialize the pages under the name HelpFile.srl.
- Add the file to the project by clicking wit the right mouse button on
TabText
in the Solution Explorer. Add as an existing item, the file to the project.
- Set the property Build Action of the file HelpFile.srl to Embedded Resource.
- Open in the Solution Explorer, Resources.resx.
- Add resources -> Add Existing File, the file HelpFile.srl.
- Now can you use
Resources.HelpFile
to access the file.
Keyboard-state
To get the state of the keys Caps, Insert, ScrollLock and Numlock, we need the class Keyboard
. These class is available in the namespace Microsoft.VisualBasic.Devices
. To use this namespace, you must the required reference.
- Go to the Solution Explorer.
- Right click with the mouse on Reference.
- Add Reference.
- Choose in the tab .NET the item "Microsoft.VisualBasic".
- Then click OK.
So now, you can:
using Microsoft.VisualBasic.Devices;
and declare in the constructor of TabText
, the class:
public Keyboard board = new Keyboard();
In the TimerTick
event, the status can be read:
private void TabText_TimerTick(object sender, EventArgs e)
{
DateTime dt = DateTime.Now;
sbpDate.Text = dt.ToShortDateString();
sbpTime.Text = dt.ToShortTimeString();
if (board.CapsLock)
sbpCapsKey.Text = "Caps on";
else
sbpCapsKey.Text = "Caps off";
if (board.NumLock)
sbpNumberKey.Text = "Num on";
else
sbpNumberKey.Text = "Num off";
if (board.ScrollLock)
sbpScrollLock.Text = "Scroll on";
else
sbpScrollLock.Text = "Scroll off";
int index = ZoekTab();
if (richTextBox[index].GetKeyStateInsert() == 1)
sbpInsert.Text = "Insert";
else
sbpInsert.Text = "Overwrite";
}
sbpDate
, sbpTime
, sbpCapsKey
, sbpNumberKey
, sbpScrollLock
, and sbpInsert
are labels on the status bar.
But the class Keyboard
has no property for the Insert-key. So we need an API-function, which I have placed in the RichTextBox
.
[DllImportAttribute("user32.dll")]
private static extern uint GetKeyState(int keystate);
public uint GetKeyStateInsert()
{
return GetKeyState(45);
}
Register the program
In the Load
function of the form, the settings of the program will be loaded from the registry. In the Form Closed
event, the settings will be stored in the registry.
Further information