65.9K
CodeProject 正在变化。 阅读更多。
Home

TabText

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.60/5 (8投票s)

2006年7月10日

7分钟阅读

viewsIcon

84692

downloadIcon

1515

TabText 是一款带有标签页的文本编辑器,类似于 Excel。

引言

TabText 是一款带有标签页的文本编辑器,类似于 Excel。

该程序具有以下已在 Code Project 上发布的扩展功能:

namespace Khendys.Controls
{
    public class ExRichTextBox : RichTextBox
    {
        //code of Khendis Gordon
    }
}

using Khendys.Controls;
namespace Nik.UserControls
{
    public class RicherTextBox2 : ExRichTextBox
    {
        //code of Nikola Stepan
    }
}

using Nik.UserControls;
namespace MicrosoftMSDN
{
    public class RichTextBoxExt : RicherTextBox2
    {
        //Code of MSND
    }
}

using MicrosoftMSDN;
namespace Compenkie
{
    public class RichTextBoxExtend : RichTextBoxExt
    {
        //code of myself
    }
}

TabText 扩展了以下功能:

  • 标签页,方便您在文档之间切换。
  • 为每个页面添加了 PageSetupDialog 的页面设置。
  • 将所有页面序列化到一个文件中。
  • 通过 ToolStripMenuItem 数组实现最近文件列表。
  • 添加了一个简单的表格。该表格利用 RichTextBox 的功能,通过按 Enter 键来添加更多行。
  • 添加了查找和替换功能,如 MSDN 库中所述。
  • 通过图片面板插入表情符号。
  • 通过上下文菜单插入文本片段。
  • 帮助页面。
  • 可更改语言的选项。
  • 键盘状态。
  • 将程序的设置保存在注册表中。

TabText 会创建一个文件夹,我的文档\\Compenkie\\TabText1,用于存储和加载数据文件。TabText 会创建一个注册表项,CurrentUser\\Compenkie\Tabext1,用于存储设置。

程序的构建结构如下:

Black    = Form                 = TabText
Red    = SplitContainer              = splitContaimer
Bleu    = TabControl             = tabControl
Green    = RichTextBoxExtended (left)    = richTextBox
Green    = RichTextBox (right)        = richTextBoxPreview

richTextBoxPreview 仅用于预览文本片段。

标签页

页面以与设计器相同的方式动态创建。每个页面都添加了一个 RichTextBox,类型为 RichTextBoxExtended

public RichTextBoxExtend[] richTextBox = new RichTextBoxExtend[17];
for (int teller = 0; teller < 17; teller++)
{
    richTextBox[teller] = new RichTextBoxExtend();
    // 
    // richTextBox1
    // 
    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)
{
    // tabPage
    //

    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;
}

页面命名为“TapPage0”、“Tabpage1”等。标签页的 Text 属性中包含文件名。标签页的 Tag 属性中包含文件的完整路径。双击标签页会弹出一个文本框,您可以在其中更改文件名。RichTextBox 关闭 DockStyle.Fill,以便您可以在选项卡控件顶部留出空间来显示 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();
}

当焦点离开文本框时,名称将添加到标签页的文本中。如果不提供扩展名,将自动添加标准扩展名 rtf。文件的完整名称将添加到选项卡控件的 Tag 属性中。

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;
}

加载文件

LoadFile 将通过以下方式访问:

  • 菜单项 加载
  • 最近文件菜单项 加载

Modified 属性最初设置为 false。通过编辑 RichTextBox,该属性变为 true。这将在保存例程中使用。文件的完整名称将存储在 Tag 属性中,并且文件将被添加到最近文件列表中。如果已存在,则先移除再添加。如果扩展名为 RTF,则按 RTF 文件加载,否则按文本文件加载。

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;
         // add successfully opened file to recent file list
         removeRecentFile(fileName);
         addRecentFile(fileName);
    }
    catch (IOException ex)
    {
         removeRecentFile(fileName);
                Trace.WriteLine(ex.Message, "Error loading from file");
    }
}

保存文件

文件通常保存为 TXT 或 RTF,具体取决于文件名扩展名。默认是“rtf”。如果文件被修改,系统会询问您是否要保存。

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());
}

最近文件列表

最近文件列表是通过 ToolStripMenuItem 数组创建的。

private ToolStripMenuItem[] recentFiles = new ToolStripMenuItem[11];
int maxRecent;

变量 maxRecent 存储实际的最近文件数量。

在窗体的 Load 事件中,将填充数组。在 ToolTipText 属性中存储文件的路径,以便显示漂亮的工具提示。

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());

如果文件最初存在,它将被打开并创建一个标签页。

序列化

通过序列化,所有标签页都存储在一个文件中。将使用 BinaryFormatter 来存储数据类型。对于每个页面,我们将存储文件名和文件的完整路径名。还将为每个 TabPage 保存 PageSetupDialog 的页面设置。选中 RichTextBox 的全部内容后,保存内容。通过一个 for 循环,我们将保存所有页面。

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";
}

要从序列化文件中恢复页面,您必须按照与标签页序列化相同的顺序进行操作。我们首先保存现有的标签页,然后清空选项卡控件。加载标签页数量后,我们添加标签页数量,并清空 RichTextBox 的旧内容。当然,在加载 RichTextBox 计数之前,我们加载 PageSetupDialog 的页面设置以及文本和标签属性。

private void deserializeFileDialog_FileOk(object sender, CancelEventArgs e)
{
    sbpMenu.Text = "Deserialize alle pagina's";
    //Open the file written above and read values from it.
    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";
}

表情符号

我们可以添加小图片,这些图片可以从调色板中选择。

调色板是一个带有 PictureBox 的 Panel。该 Panel 在窗体的 Load 事件中创建。图片文件在 Emotion_collector 中创建。序列化文件作为资源文件嵌入。文件将被加载到 MemoryStream 中。在“选项”框中,您可以选择从磁盘加载图片文件。

images = new ImageInfo[maxImage];
for (int teller = 0; teller < maxImage; teller++)
     images[teller] = new ImageInfo();

使用 MemoryStream 从资源加载序列化文件,方式与在标签页中加载序列化文件相同。

// Load Emoticon Images
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);

通过点击菜单项来显示图片面板。必须先隐藏调色板,因为如果双击菜单项会出现错误。

private void contextmenuemoticonsToolStripMenuItem_Click(object sender, 
                                                         EventArgs e)
{
    ToolStripMenuItem item = (ToolStripMenuItem)sender;
    EventArgs args = (EventArgs)e;
            
    if (showForm != null)
    {
        // first hide, you can not twice Show a form.
        showForm.Hide();
        showForm.Location = new Point(rectNormal.X + 200, 
                                      rectNormal.Y + 200);
        showForm.Show();
    }
    else
        MessageBox.Show("No Icons selectede");
}

通过点击 PictureBox 来添加图片。sender 对象包含 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());
    }
}

文本片段

使用相同的方式,您可以插入文本片段。标准文本片段存储在 ToolStripMenuItem 数组中。我创建了四个日期项,并将这些项添加到一个上下文菜单中。通过点击菜单项来显示上下文菜单。

private void contextMenuToolStripMenuItem_Click(object sender, EventArgs e)
{
    contextMenu_Text.Show(this, new Point(200, 200));
}

文本片段加载到菜单项的 Tag 属性中。如果文本是纯文本,则 bool 变量 standaardtrue。如果文本是 RTF 格式,则 standaardfalse

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;

当鼠标悬停在上下文菜单项上时,会显示一个预览窗口。在 TabText 的构造函数中,将 SplitterDistance 设置为等于分隔符的宽度。

splitContainer.SplitterDistance = splitContainer.Size.Width;

MouseHover 事件中,将 SplitterDistance 设置为宽度的三分之二,并显示预览窗口。

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());
}

当上下文菜单关闭时,预览窗口将随着上下文菜单的 Closed 事件而消失。

private void cmenu_Teksten_Closed(object sender, 
             ToolStripDropDownClosedEventArgs e)
{
    splitContainer.SplitterDistance = splitContainer.Size.Width;
}

文本片段可以通过序列化标签页来创建。在“选项”框中,您可以选择并从磁盘加载文本片段文件。

帮助

用户可以通过三种方式获得帮助:

  • 常规工具提示。
  • 状态栏上的额外信息。
  • 带有用户信息的标签页。

状态栏信息存储在菜单项的 Tag 属性中。当鼠标悬停在菜单项上时,会触发 MouseHover 事件,并在状态栏上显示信息。

private void toolStripMenuItem_MouseHover(object sender, EventArgs e)
{
    ToolStripMenuItem menu = (ToolStripMenuItem)sender;
    sbpMenu.Text = (string)menu.Tag;
}

当鼠标离开菜单项时,会触发 MouseLeave 事件并重置文本。

private void toolStripMenuItem_MouseLeave(object sender, EventArgs e)
{
    sbpMenu.Text = "Ready";
}

在“帮助”菜单中,为用户提供帮助。点击“帮助”菜单中的“帮助”,分割容器将更改为一个带有标签页的选项卡控件。这里最重要的是添加帮助控件和菜单控件的顺序。

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);
}

点击帮助菜单的后退按钮将用分割容器替换帮助菜单。

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);
}

帮助菜单控件声明为一个变量。您可以发现,它与反序列化的标签页完全相同,只是使用了内存流。

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();

更改语言

由于信息存储在 ToolStripMenuItem 的三个属性中,您可以轻松切换语言选项。

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;
        }
    }
}

English 函数的一部分如下:

void English()
{
    // Menu Bestand
    fileToolStripMenuItem.Text = "&File";
    // New menu
    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();
}

嵌入式资源

这里简要介绍如何将嵌入式资源添加到您的项目中。

  • 根据需要创建标签页,输入文本并进行格式化。
  • 将页面序列化为名为 HelpFile.srl 的文件。
  • 通过在解决方案资源管理器中右键单击 TabText,将文件添加到项目中。添加为现有项。
  • 将文件 HelpFile.srlBuild Action 属性设置为 Embedded Resource
  • 在解决方案资源管理器中打开 Resources.resx
  • 添加资源 -> 添加现有文件,文件 HelpFile.srl
  • 现在您可以使用 Resources.HelpFile 来访问该文件。

键盘状态

要获取 Caps、Insert、ScrollLock 和 Numlock 键的状态,我们需要 Keyboard 类。该类在 Microsoft.VisualBasic.Devices 命名空间中可用。要使用此命名空间,您需要添加必要的引用。

  • 转到解决方案资源管理器。
  • 右键单击“引用”。
  • 添加引用。
  • .NET 选项卡中选择“Microsoft.VisualBasic”项。
  • 然后单击“确定”。

现在,您可以:

using Microsoft.VisualBasic.Devices;

并在 TabText 的构造函数中声明类:

public Keyboard board = new Keyboard();

TimerTick 事件中,可以读取状态:

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";
}

sbpDatesbpTimesbpCapsKeysbpNumberKeysbpScrollLocksbpInsert 是状态栏上的标签。

但是 Keyboard 类没有 Insert 键的属性。因此,我们需要一个 API 函数,我将其放置在 RichTextBox 中。

[DllImportAttribute("user32.dll")]
private static extern uint GetKeyState(int keystate);

public uint GetKeyStateInsert()
{
    return GetKeyState(45); // Keynumber InsertKey
}

注册程序

在窗体的 Load 函数中,程序的设置将从注册表中加载。在窗体的 Closed 事件中,设置将被保存在注册表中。

更多信息

© . All rights reserved.