zoukankan      html  css  js  c++  java
  • C#生成CHM文件(应用篇)之代码库编辑器(4)【附程序最终版下载】

         呵呵,程序终于告一段落了,程序也终于Finish了,让大家久等了,希望不会让大家失望。

    这也是比较典型的WinForm项目了,想学习WinForm开发的朋友可以照着我的步骤做下去,而且也提供了初版的源代码。

     虽然项目比较小,而且几乎没涉及到什么业务上的东西,不过程序开发涉及面很大,有:

    1.文件操作(包括文件的写,读取等)

    2.XML操作(将字符写入XML中和读取XML、利用XML做配置文件等)

    3.递归算法(树)【虽然在实际中用的不多,还是希望大家能够掌握】

    4.TreeView、DataGridView、WebBrowser、OpenFileDialog等典型的WinForm控件

    5.WinForm中切割图片、图片拼合、读取资源文件中的资源

    ....

    因为是比较小的程序就没有分层,不过程序中也用到了不同一般WinForm项目的思想,具体体现在MainForm和其他Form的关系(详细可以参考源代码)

    Ok ,看下程序的界面,和开始有一点变化。下面看看程序的截图吧,程序下载在最下面

    主界面(去掉了以前没有的菜单里,只剩工具栏)

     

    编译界面(有简陋的正在编译效果)

     

     配置页面(为了简单起见,还是默认Csdn编辑器,如果有兴趣的话,你们自己可以结合以前给的源代码加入新的编辑器)

     

     添加文件界面(可以批量导入)

     

     最终生成的CHM电子书界面

     

    今天的主要内容是目录窗体的实现及搜索的实现

    目录窗体BookIndexForm


    目录窗体中,有一个ContextMenuSTrip,即右击窗体出现的菜单,里面有几个比较重要的方法

    添加文件夹、添加文章、删除、重命名

    添加文件夹即添加父节点, 选中可以添加文件夹的节点(显然只有根节点和目录节点),然后将新增一个CHMNode,将它Add到父节点的Nodes里面

    /// <summary>
            /// 添加文件夹
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void AddFolderToolStripMenuItem_Click(object sender, EventArgs e)
            {
                TreeNode node = this.tvIndex.SelectedNode;//选中的节点
                
                //查看是否是根节点或是目录节点
                CHMNodeList list = this.GetNodeList(node);
                if (list == null || list.Count==0)
                {
                    MessageBox.Show("请选择根节点或是目录节点");
                    return;
                }
                //创建新的节点
                CHMNode newNode = new CHMNode();
                newNode.Name = "新建文件夹";
                newNode.ImageNo = "0";
                //newNode.Nodes = new CHMNodeList();
                list.Add(newNode);
                System.Windows.Forms.TreeNode node2 = new TreeNode(newNode.Name);
                node2.Tag = newNode;
                node2.ImageIndex = 0;
                node2.SelectedImageIndex = 0;
                node.Nodes.Add(node2);//将新节点添加到树中
                node.ImageIndex = 0;
                node.SelectedImageIndex = 0;
                this.tvIndex.SelectedNode = node2;
            }

    添加文章即添加 子节点,注意这时添加的节点他的Nodes要设为null,表示他是文章节点,不能再添加其他节点了

    /// <summary>
            /// 添加文章
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void AddArticleToolStripMenuItem_Click(object sender, EventArgs e)
            {
                TreeNode node = this.tvIndex.SelectedNode;
                CHMNodeList list = this.GetNodeList(node);
                if (list == null)
                {
                    MessageBox.Show("请选择根节点或是目录节点");
                    return;
                }
                EditForm frmEdit = new EditForm();
                frmEdit.ShowDialog(this);

                CHMNode NewNode = frmEdit.Node;
                if(NewNode==null)
                {
                    return;
                }
                list.Add(NewNode);
                System.Windows.Forms.TreeNode node2 = new TreeNode(NewNode.Name);
                node2.Tag = NewNode;
                node2.ImageIndex = 1;
                node2.SelectedImageIndex = 1;
                node.Nodes.Add(node2);//将新节点添加到树中
                node.ImageIndex = 0;
                node.SelectedImageIndex = 0;
                this.tvIndex.SelectedNode = node2;
                
            }

    重命名:右击节点可以编辑节点的Label,

    实现方法如下:

    将TreeView的LabelEdit设为True,然后编写如下代码:

    private void ReNameMToolStripMenuItem_Click(object sender, EventArgs e)
            {
                this.tvIndex.SelectedNode.BeginEdit();
            }

            private void tvIndex_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
            {
                this.tvIndex.SelectedNode.Name = e.Label;
                TreeNode node = this.tvIndex.SelectedNode;
                if (node.Tag is CHMDocument)
                {
                    ((CHMDocument)node.Tag).Title = e.Label;
                }
                if (node.Tag is CHMNode)
                {
                    ((CHMNode)node.Tag).Name = e.Label;
                }
            }

     删除节点,即移除,同时还要删除文件

    /// <summary>
            /// 删除节点
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void DeleteDToolStripMenuItem_Click(object sender, EventArgs e)
            {
                System.Windows.Forms.TreeNode node = this.tvIndex.SelectedNode;//获取要删除的节点
                if (node == null)
                {
                    MessageBox.Show("请选择一个节点");
                    return;
                }
                System.Windows.Forms.TreeNode parent = node.Parent;//获取该节点的父节点
                if (parent == null)
                {
                    MessageBox.Show("请选择目录或页面节点");
                    return;
                }
                CHMNodeList list = GetNodeList(parent);
                if (MessageBox.Show("是否删除文章(删除同时删除本地文件)?","确认"MessageBoxButtons.YesNo)== System.Windows.Forms.DialogResult.Yes)
                {
                    list.Remove((CHMNode)node.Tag);
                    //同时删除文件
                    if (System.IO.File.Exists(((CHMNode)node.Tag).Local))
                    {
                        System.IO.File.Delete(((CHMNode)node.Tag).Local);
                    }
                    node.Remove();//移除该节点    
                }
            }
     

    代码库搜索功能(Lucene.Net 2.0.04)

    这个搜索只是简单的使用Lucene.Net实现的搜索。

     我们在点击搜索按钮的时候,才开始做索引的操作。遍历chmDocument这个类,将节点信息存储为索引,然后查的时候就去查索引,好像有点多此一举。

    其实不然,因为我们要全文模糊检索,我们不能看将每个文件打开,然后找找里面有没有这个搜索词。所以就用Lucene.Net。

    具体实现代码如下:

    /// <summary>
            /// 查询(修改为在点击查询的时候再去索引什么)
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnSearch_Click(object sender, EventArgs e)
            {
                //INDEX_STORE_PATH 为索引存储目录
                string INDEX_STORE_PATH = Application.StartupPath+@"\index";  

                //先存储索引
                IndexWriter writer = new IndexWriter(INDEX_STORE_PATH, new StandardAnalyzer(), true);
                SetIndex(writer,this.nodes);

                //在从索引中查询
                string KEYWORD = this.txtKeyWords.Text.ToString();//关键字
                IndexSearcher searcher;

                try
                {
                    searcher = new IndexSearcher(INDEX_STORE_PATH);
                    QueryParser q = null;

                    if (rbByTitle.Checked)
                    {
                        q = new QueryParser("title"new StandardAnalyzer());
                    }
                    else if (rbByKeywords.Checked)
                    {
                        q = new QueryParser("keywords"new StandardAnalyzer());
                    }
                    else if (rbByAll.Checked)
                    {
                        q = new QueryParser("contents"new StandardAnalyzer());
                    }
                    
                    Query query = q.Parse(KEYWORD);
                    Hits hits = searcher.Search(query);

                    //创建DataTable用于绑定
                    DataTable dtResult = new DataTable();
                    DataColumn dc1 = new DataColumn("Title"Type.GetType("System.String"));
                    DataColumn dc2= new DataColumn("KeyWords"Type.GetType("System.String"));
                    DataColumn dc3 = new DataColumn("Content"Type.GetType("System.String"));
                    DataColumn dc4 = new DataColumn("FilePath"Type.GetType("System.String"));
                    dtResult.Columns.Add(dc1);
                    dtResult.Columns.Add(dc2);
                    dtResult.Columns.Add(dc3);
                    dtResult.Columns.Add(dc4);

                    if (hits != null && hits.Length()>0)
                    {
                        for (int i = 0; i < hits.Length(); i++)
                        {
                            Document doc = hits.Doc(i);
                            DataRow dr=dtResult.NewRow();
                            dr["Title"] = doc.Get("title");//文章标题
                            dr["KeyWords"] = doc.Get("keywords");//文章关键字
                            dr["Content"] = doc.Get("contents");//内容
                            dr["FilePath"] = doc.Get("filename");//文件路径
                            dtResult.Rows.Add(dr);
                        }
                        this.tcList.SelectedIndex = 1;
                        this.dgvResult.DataSource = dtResult;
                        this.dgvResult.Columns["FilePath"].Visible = false;
                    }
                    else
                    {
                        MessageBox.Show("没有查到相关记录!");
                    }
                    searcher.Close();
                }
                catch (Exception ex)
                {
                    LogHelper.WriteLog(ex.Message);
                }
            }
    其中,写入索引的代码如下,也是使用遍历的(一开始想当然地做了,结果老是死循环,看来递归还是没有掌握好)
     /// <summary>
            /// 遍历chmDocument,将节点存储为索引
            /// </summary>
            private void SetIndex(IndexWriter writer, CHMNodeList nodes)
            {
                if (this.nodes == null || this.nodes.Count == 0)
                {
                    return;
                }
                foreach (CHMNode n in nodes)
                {
                    //if (node.Nodes==null)
                    if (n.ImageNo == "1")//使用imageNo来判断
                    {
                        IndexFile(n, writer);
                    }
                    else
                    {
                        SetIndex(writer,n.Nodes);
                    }
                }
                writer.Close();// 关闭writer
            }
    private void IndexFile(CHMNode node, IndexWriter writer)
            {
                try
                {
                    Document doc = new Document();
                    doc.Add(new Field("filename", node.Local, Field.Store.YES, Field.Index.TOKENIZED));
                    doc.Add(new Field("title", node.Name, Field.Store.YES, Field.Index.TOKENIZED));
                    doc.Add(new Field("keywords", node.KeyWords, Field.Store.YES, Field.Index.TOKENIZED));
                    doc.Add(new Field("contents"new StreamReader(node.Local, System.Text.Encoding.Default)));
                    writer.AddDocument(doc);
                }
                catch (FileNotFoundException fnfe)
                {
                    LogHelper.WriteLog(fnfe.Message);
                }
               
            }

    在搜索界面中还是用了TabControl,以前用过DevExpress的TabControl,它隐藏 TabControl标签有现成的方法的,但是MS的TabControl貌似没有这个方法,于是就弄了讨巧的方法,将TabControl放在一个Panel中,头部稍微超出Panel的边界,然后在Form_Load方法中写如下代码:

    //隐藏TabContol的标签栏
                this.tcList.SizeMode = TabSizeMode.Fixed;
                this.tcList.ItemSize = new Size(0, 1);
     

     附件下载 AlexisEditor RC版

    说明:如果没有重大bug或者是反馈了,这个系列就到这边结束了,后面有篇文章专门总结这一系列的,里面的知识点还是比较多的,希望里面的知识能够帮到大家。

     

  • 相关阅读:
    java:字符串的split方法,使用多个分隔符,分割一个字符串
    mysql 导入txt数据到数据表【原创】
    配置SSH无密码登录【原著】
    springboot 控制台程序读取配置文件(原创)
    Idea开发环境中,开发springboot类型的项目,如果只引入parent节点,不添加依赖节点,maven是不会加载springboot的任何依赖的
    Windows版的OpenJDK下载(Red Hat 提供)
    谈谈php里的IOC控制反转,DI依赖注入(转)
    高质量的工程代码为什么难写 (转)
    系统权限管理设计 (转)
    php mqtt client
  • 原文地址:https://www.cnblogs.com/alexis/p/1858340.html
Copyright © 2011-2022 走看看