C#生成CHM文件(应用篇)之代码库编辑器(5)【总结、程序、源代码】

简介:

经历了快一个月的开发(因为都是在闲暇时间做的,实际实际可能不到一周),AlexisEditor总算完成了。

这边说明一下为什么有些网友不能新增文章的问题。

原因是路径中有中文字符,我一直用的是英文系统,所以没有出现这样的情况。感谢网友初中生的net梦 在xp、.Net2.0 平台下的测试 ,发现了这个bug。
 

具体来说一下这个bug,是因为WebBrowser导航发生改变的时候会对Uri进行编码,而我们这边不需要进行编码,于是就可以用反编码就行了。

解决方案如下

 //特别注意,如果路径中有中文,url会对其进行编码

 
  1. if (System.Web.HttpUtility.UrlDecode(e.Url.AbsolutePath.ToString()).Replace('/', '\\') == saveUrl) 

再次更新下程序(也许是最后一次更新了)

源代码下载(vs2010版)

源代码下载(vs2005版)

程序下载(XP版,如果你的IE版本是6.0,请下载此版本) 
 

呵呵,如果觉得好的话,请推荐之!

下面将我这个系列遇到的问题和经验总结下,然后着手学习WPF和SilverLight的知识,希望里面的知识点能够对你有帮助。

篇幅可能有点长,为了方便起见,增加导航

 

 一、在WinForm实现类似CSS Sprites(CSS图像拼合技术)

 二、WebBrowser控件的使用技巧

 三、XML的妙用之存储树

 四、Visual Studio界面风格WinForm实现

 五、 WinForm中的状态栏初探

 六、 C#调用系统的cmd命令

 七、 TreeView节点重命名

 八、DataGridView中的一些技巧

 九、Lucene.Net简单的应用

 十、简易版的log类

一、在WinForm实现类似CSS Sprites(CSS图像拼合技术)       

在WinForm我们会用到许多的小图片,可能要求是ico格式的,而且像素一般是16*16的,如果将这么多的ico图片放在一个文件夹里,当然 是可以,不过,如果一张图片2k,那么50张图片就是100k,浪费空间。我们可以像web那样做,将许多图片拼合到一张图片中,然后写一个静态类来调用 图片中的第几个图形。

如下图一张480*16 bmp格式的图片(演示需要,放大了)

设置图片的背景为比较明显的颜色(为了后面显示透明),总共就有30个图形,于是我们就可以遍历然后将图片存到一个List中了,详细代码如下:

 
  1. System.Resources.ResourceManager resource = new System.Resources.ResourceManager("AlexisEditor.Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());  
  2.             Bitmap bitmap = (Bitmap)resource.GetObject("bookicons");  
  3.  
  4.             //将加载的位图的图片提取出来,并放在list中  
  5.             imageList = new ImageList();  
  6.             iconList = new List<Icon>();  
  7.             for (int i = 0; i < bitmap.Width / 16; i++)  
  8.             {  
  9.                 Bitmap img = bitmap.Clone(new Rectangle(16 * i, 0, 16, 16), bitmap.PixelFormat);//切割图标  
  10.                 img.MakeTransparent(Color.Magenta);//设置过滤色  
  11.                 imageList.Images.Add(img);  
  12.                 System.IntPtr iconHandle = img.GetHicon();  
  13.                 System.Drawing.Icon icon = Icon.FromHandle(iconHandle);  
  14.                 iconList.Add(icon);  
  15.             }  

首先从资源文件中获取名为bookicons的位图,然后遍历,将每个图形存入到imageList中或是自定义的List,

这边给出了如何将bmp图片转换为Icon图片的代码

 
  1. System.IntPtr iconHandle = img.GetHicon();                   
  2.  
  3. System.Drawing.Icon icon = Icon.FromHandle(iconHandle);  
  4.  

整个IconHelper类的代码如下:

 
  1. private static ImageList imageList;  
  2.        private static List<Icon> iconList;  
  3.        static IconHelper()  
  4.        {  
  5.            System.Resources.ResourceManager resource = new System.Resources.ResourceManager("AlexisEditor.Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());  
  6.            Bitmap bitmap = (Bitmap)resource.GetObject("bookicons");  
  7.            //将加载的位图的图片提取出来,并放在list中  
  8.            imageList = new ImageList();  
  9.            iconList = new List<Icon>();  
  10.            for (int i = 0; i < bitmap.Width / 16; i++)  
  11.            {  
  12.                Bitmap img = bitmap.Clone(new Rectangle(16 * i, 0, 16, 16), bitmap.PixelFormat);//切割图标  
  13.                img.MakeTransparent(Color.Magenta);//设置过滤色  
  14.                imageList.Images.Add(img);  
  15.                System.IntPtr iconHandle = img.GetHicon();  
  16.                System.Drawing.Icon icon = Icon.FromHandle(iconHandle);  
  17.                iconList.Add(icon);  
  18.            }  
  19.        }  
  20.  
  21.        public static Image GetBuildImage()  
  22.        {  
  23.            System.Resources.ResourceManager resource = new System.Resources.ResourceManager("AlexisEditor.Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());  
  24.            return (Image)resource.GetObject("Build");  
  25.        }  
  26.  
  27.        /// <summary> 
  28.        /// 书籍ico图标  
  29.        /// </summary> 
  30.        public static Icon BookIcon  
  31.        {  
  32.          get { return iconList[0]; }  
  33.        } 

二、WebBrowser控件的使用技巧

 设置当前的Uri地址

 
  1. this.wbEditor.Url = new Uri(startPath + @"\CSDN_UBB\normalTemp.htm"); 

获取当前WebBrowser中的文档

 
  1. HtmlDocument hd = this.wbEditor.Document;//获取文档信息 

获取当前Dom文档中指定Id的元素

 
  1. HtmlElement he = hd.GetElementById("content"); 

获取指定元素的值,比如获取TextArea中的值

首先添加mshtml的引用

 
  1. IHTMLDocument2 doc = (IHTMLDocument2)this.wbEditor.Document.DomDocument;mshtml.HTMLInputElement text1 = (HTMLInputElement)doc.all.item("content"); 

 后台调用页面中已有的js函数

 
  1. ((mshtml.HTMLDocumentClass)webBrowser.Document).parentWindow.execScript( "func()", "JScript" ); 

具体的应用可以参考我的源代码


三、XML的妙用之存储树        

程序中使用XML存储电子书的目录信息,方便hhc.exe编译为CHM电子书。

在.Net中有现成的类来操作XML,即System.Xml.XmlDocument, 他表示一个XML文档(XML基本知识我就不介绍了),XML有根节点,根节点里面可以有子节点,节点有属性等等。
 

可以使用XmlDocument的Load方法来将一个XML文档加载到内存中,如下代码:

 
  1. System.Xml.XmlDocument doc = new System.Xml.XmlDocument();  
  2. doc.Load(filename);  

然后获取xml根节点里面的一些信息,根节点下面才是书籍目录的信息

 
  1. private void FromXML(System.Xml.XmlElement RootElement)  
  2.         {  
  3.             //this.defaultPage = RootElement.GetAttribute("DefaultTopic");  
  4.             this._title = RootElement.GetAttribute("Title");//标题  
  5.             nodeList.Clear();  
  6.  
  7.             foreach (System.Xml.XmlNode node in RootElement.ChildNodes)  
  8.             {  
  9.                 if (node.Name == "Items")  
  10.                 {  
  11.                     NodesFromXML(nodeList, (System.Xml.XmlElement)node);  
  12.                 }  
  13.             }  
  14.         }   
  15.  
  16.  //xml转为为nodes  
  17.         private void NodesFromXML(CHMNodeList nodes, System.Xml.XmlElement RootElement)  
  18.         {  
  19.             foreach (System.Xml.XmlNode node in RootElement.ChildNodes)  
  20.             {  
  21.                 if (node.Name == "Node")  
  22.                 {  
  23.                     System.Xml.XmlElement element = (System.Xml.XmlElement)node;  
  24.                     CHMNode NewNode = new CHMNode();  
  25.                     NewNode.Name = element.GetAttribute("Name");  
  26.                     NewNode.Local = element.GetAttribute("Local");  
  27.                     NewNode.ImageNo = element.GetAttribute("ImageNumber");  
  28.                     nodes.Add(NewNode);  
  29.                     foreach (System.Xml.XmlNode node2 in element.ChildNodes)  
  30.                     {  
  31.                         if (node2.Name == "Items")  
  32.                         {  
  33.                             NodesFromXML(NewNode.Nodes, (System.Xml.XmlElement)node2);  
  34.                         }  
  35.                     }  
  36.                 }  
  37.             }  
  38.         } 

四、Visual Studio界面风格WinForm实现                  

实现步骤(一个小的Demo):
 

1.创建WinForm项目,取名为DockDemo, 

2.在工具栏中添加工具,导向 WeifenLuo.WinFormsUI.Docking.dll

3.建立MainForm,将DockPanel拖到MainForm中 ,设置其Dock属性为Fill

4.建立SolutionForm,将其继承有Form改为 WeifenLuo.WinFormsUI.Docking.DockContent

5.在MainForm的构造函数中实例化SolutionForm,代码如下

 
  1. SolutionForm  form=new SolutionForm();  
  2. form.Show(dockPanel);//显示目录窗体  
  3. form.DockTo(dockPanel, DockStyle.Right);   
  4.  

同时,我们看到visual studio中,将ToolBox关闭掉可以点击 工具栏中的图标重新调用,我们可以设置SolutionForm的属性HideOnClose为True,即点击关闭时并不是真正的释放,而是隐藏起来。重新显示调form.Show(dockPanel);即可

五、 WinForm中的状态栏初探    

用过Visual Studio的程序员都知道,Visual Studio下方的状态栏提供了各种各样的状态给开发者,使得开发者能够实时知道Visual Studio现在处于什么状态。

 代码如下,创建ToolStripStatusLabel 对象的实例,添加到StatusStrip中,当编译完后移除。(有更好的方法欢迎指导

 

 
  1. ToolStripStatusLabel tsl = new ToolStripStatusLabel();  
  2.             tsl.Text = "正在编译....";  
  3.             ToolStripStatusLabel tslBuilding = new ToolStripStatusLabel();  
  4.             tsl.Image = IconHelper.GetBuildImage();  
  5.             tsl.Dock = DockStyle.Right;  
  6.             this.statusStrip.Items.AddRange(new ToolStripItem[] { tsl,tslBuilding });  
  7.             this.statusStrip.Text = "正在编译...";  
  8.             chmDocument.Compile();  
  9.             frmOutPut.TxtOutput.Text = chmDocument.OutPutText;  
  10.             this.statusStrip.Items.Remove(tsl);  
  11.             this.statusStrip.Items.Remove(tslBuilding);ToolStripStatusLabel tsl = new ToolStripStatusLabel();  
  12.             tsl.Text = "正在编译....";  
  13.             ToolStripStatusLabel tslBuilding = new ToolStripStatusLabel();  
  14.             tsl.Image = IconHelper.GetBuildImage();  
  15.             tsl.Dock = DockStyle.Right;  
  16.             this.statusStrip.Items.AddRange(new ToolStripItem[] { tsl,tslBuilding });  
  17.             this.statusStrip.Text = "正在编译...";  
  18.             chmDocument.Compile();  
  19.             frmOutPut.TxtOutput.Text = chmDocument.OutPutText;  
  20.             this.statusStrip.Items.Remove(tsl);  
  21.             this.statusStrip.Items.Remove(tslBuilding); 

六、 C#调用系统的cmd命令           

 我们有时候需要在C#中调用一些command命令,如点击Label打开浏览器。AlexisEditor程序中编译为CHM电子书的功能就是 调用系统自带(一般正版的Windows系统都会自带,其他版本的Windows可能会阉割掉)的hhc.exe,并且可以获得当前的编译情况

Process helpCompileProcess = new Process();  //创建新的进程,用Process启动HHC.EXE来Compile一个CHM文件

 
  1. ProcessStartInfo processStartInfo = new ProcessStartInfo();  
  2.                processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;  
  3.                processStartInfo.FileName = config.HhcPath;  //调入HHC.EXE文件   
  4.                processStartInfo.Arguments = "\"" + strHhp + "\"";//获取空的HHP文件  
  5.                processStartInfo.UseShellExecute = false;  
  6.                processStartInfo.CreateNoWindow = true;  
  7.                processStartInfo.RedirectStandardOutput = true;  
  8.                helpCompileProcess.StartInfo = processStartInfo;  
  9.                helpCompileProcess.Start();  
  10.                helpCompileProcess.WaitForExit(); //组件无限期地等待关联进程退出   
  11.  
  12.     string _outPutText = helpCompileProcess.StandardOutput.ReadToEnd();//进程中的信息 

七、 TreeView节点重命名      

有时候我们需要重命名TreeView的节点,实现代码如下:

首先设置TreeView的LabelEdit属性为True,然后在触发的事件中,设置选中的节点为编辑状态,接下来就是编辑后触发的事件即AfterLabelEdit 。
 

 
  1. private void ReNameMToolStripMenuItem_Click(object sender, EventArgs e)  
  2. {  
  3.             this.tvIndex.SelectedNode.BeginEdit();  
  4. }  
  5.  
  6. private void tvIndex_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)  
  7. {  
  8.  
  9.             this.tvIndex.SelectedNode.Name = e.Label;  
  10.             TreeNode node = this.tvIndex.SelectedNode;  
  11.             if (node.Tag is CHMDocument)  
  12.             {  
  13.                ((CHMDocument)node.Tag).Title = e.Label;  
  14.             }  
  15.  
  16.             if (node.Tag is CHMNode)  
  17.             {  
  18.                ((CHMNode)node.Tag).Name = e.Label;  
  19.             }  


 八、DataGridView中的一些技巧  

程序中使用DataGridView显示查询的结果,因为查询的结果是自己拼凑起来的,即使用Lucene.Net检索到多少内容,然后拼凑为DataTable,最后在绑定到

DataGridView中,因为要在DataGridView中增加链接,使得点击链接可以打开文章进行编辑,所以在前台DataGridView可视化界面中增加LinkLabel的Column。

并设置它的DataPropertyName设为后台DataTable中的名字,代码如下

 
  1. //创建DataTable用于绑定  
  2. DataTable dtResult = new DataTable();  
  3. DataColumn dc1 = new DataColumn("Title", Type.GetType("System.String"));  
  4. DataColumn dc2new DataColumn("KeyWords", Type.GetType("System.String"));  
  5. DataColumn dc3 = new DataColumn("Content", Type.GetType("System.String"));  
  6. DataColumn dc4 = new DataColumn("FilePath", Type.GetType("System.String"));  
  7. dtResult.Columns.Add(dc1);  
  8. dtResult.Columns.Add(dc2);  
  9. dtResult.Columns.Add(dc3);  
  10. dtResult.Columns.Add(dc4); 

注意,如果在可视化界面中不设置列的DataPropertyName属性,运行后会出现多列,有点类似于GridView中的AutoGenerateColumn属性

然后遍历索引检索到的条数,将内容添加到DataTable中

 
  1. if (hits != null && hits.Length()>0)  
  2. {  
  3.       for (int i = 0; i < hits.Length(); i++)  
  4.       {  
  5.             Document doc = hits.Doc(i);  
  6.             DataRow dr=dtResult.NewRow();  
  7.             dr["Title"] = doc.Get("title");//文章标题  
  8.             dr["KeyWords"] = doc.Get("keywords");//文章关键字  
  9.             dr["Content"] = doc.Get("contents");//内容  
  10.             dr["FilePath"] = doc.Get("filename");//文件路径  
  11.            dtResult.Rows.Add(dr);  
  12.        }  
  13.        this.tcList.SelectedIndex = 1;  
  14.        this.dgvResult.DataSource = dtResult;  
  15.        this.dgvResult.Columns["FilePath"].Visible = false;  
  16. }  
  17. else  
  18. {  
  19.      MessageBox.Show("没有查到相关记录!");  

使用this.dgvResult.DataSource = dtResult;绑定数据源

下面来看看如何获取DataGridView选中行中所有列的数据,当我们点击超链接时会触发CellContentClick事件,我们在这个事件中获取选中行的

 
  1. private void dgvResult_CellContentClick(object sender, DataGridViewCellEventArgs e)  
  2. {  
  3.             //点击的是超链接  
  4.             if (e.ColumnIndex==0)  
  5.             {  
  6.                 //获取当前行的文件名  
  7.                 string path=this.dgvResult.CurrentRow.Cells[3].Value.ToString();  
  8.  
  9.                 //调用编辑窗口  
  10.                 GetNode(path,this.nodes);  
  11.                 CHMNode node = this.nodeOpen;  
  12.                 EditForm form = new EditForm();  
  13.                 form.Edit(node);  
  14.                 form.ShowDialog();  
  15.             }  

这句话就是获取我们要打开文章

 
  1. string path=this.dgvResult.CurrentRow.Cells[3].Value.ToString(); 

九、Lucene.Net简单的应用 

程序中使用了Lucene.Net来搜索文,原因是因为Lucene.Net支持全文检索,即我们可以输入关键字,在我们写的文章中查询有没有匹配的词。

思路是这样的,选择检索方式:标题检索、关键字检索、全文检索,每个检索都会搜索不同的索引。

点击搜索按钮的时候生成索引的,获取关键字 ,获取检索方式,检索,显示搜索结构。

 
  1. //INDEX_STORE_PATH 为索引存储目录  
  2. string INDEX_STORE_PATH = Application.StartupPath+@"\index";    
  3.  
  4. //先存储索引  
  5. IndexWriter writer = new IndexWriter(INDEX_STORE_PATH, new StandardAnalyzer(), true);  
  6. SetIndex(writer,this.nodes); 

而SetIndex是使用递归的方法遍历树,有关写入Index的代码如下 

 
  1. try  
  2. {  
  3.      Document doc = new Document();  
  4.      doc.Add(new Field("filename", node.Local, Field.Store.YES, Field.Index.TOKENIZED));  
  5.      doc.Add(new Field("title", node.Name, Field.Store.YES, Field.Index.TOKENIZED));  
  6.      doc.Add(new Field("keywords", node.KeyWords, Field.Store.YES, Field.Index.TOKENIZED));  
  7.      doc.Add(new Field("contents", new StreamReader(node.Local, System.Text.Encoding.Default)));  
  8.      writer.AddDocument(doc);  
  9. }  
  10. catch (FileNotFoundException fnfe)  
  11. {  
  12.      LogHelper.WriteLog(fnfe.Message);  

然后查询

1.申明一个IndexSearcher 2.设置查询路径 3.申明一个QueryParser 4.设置查询使用的value 5.查询 6.Hits 对象即查询结果

 
  1. //在从索引中查询              
  2.  
  3. IndexSearcher searcher;  
  4. searcher = new IndexSearcher(INDEX_STORE_PATH);  
  5.  
  6. QueryParser q = null;  
  7.  
  8. q = new QueryParser("title", new StandardAnalyzer());  
  9.  
  10. Query qquery = q.Parse(KEYWORD);  
  11. Hits hits = searcher.Search(query); 

十、简易版的log类 

在写一写“危险”代码时,我们经常使用try。。catch语句进行异常捕获,而且往往也会忘了对捕获信息的存储。这里提供一个简易版的日志记录类

 
  1. public static class LogHelper  
  2. {  
  3. public static void WriteLog(string log)  
  4. {  
  5. string path = Application.StartupPath + "\\log.txt";  
  6. FileStream filestream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);  
  7. StreamWriter sw = new StreamWriter(filestream, Encoding.Default);  
  8. sw.BaseStream.Seek(0, SeekOrigin.End);  
  9. sw.WriteLine("*********Exception*********");  
  10. sw.WriteLine("Time:" + DateTime.Now);  
  11. sw.WriteLine("Message:" + log);  
  12. sw.WriteLine("*********Exception*********");  
  13. sw.WriteLine();  
  14. sw.WriteLine();  
  15. sw.Close();  
  16. }  

在捕获到异常的时候直接调用方法即可





    本文转自xshf12345 51CTO博客,原文链接:http://blog.51cto.com/alexis/574473,如需转载请自行联系原作者



相关文章
|
1月前
|
XML C# 数据格式
使用C#操作XML文件
使用C#操作XML文件
11 0
|
1月前
|
C#
C# 文件操作(全部) 追加、拷贝、删除、移动文件、创建目录
C# 文件操作(全部) 追加、拷贝、删除、移动文件、创建目录
22 0
|
3月前
|
C#
C#读取html文件
C#读取html文件
28 3
|
3月前
|
C# 开发者
C# 10.0中的文件范围命名空间:简化代码组织的新方式
【1月更文挑战第18天】C# 10.0引入了文件范围的命名空间,这是一种新的语法糖,用于更简洁地组织和管理代码。文件范围命名空间允许开发者在每个文件的基础上定义命名空间,而无需显式使用花括号包裹整个文件内容。本文将深入探讨文件范围命名空间的工作原理、使用场景以及它们为C#开发者带来的便利。
|
3月前
|
C# 开发者
C# 9.0中的模块初始化器:程序启动的新控制点
【1月更文挑战第14天】本文介绍了C# 9.0中引入的新特性——模块初始化器(Module initializers)。模块初始化器允许开发者在程序集加载时执行特定代码,为类型初始化提供了更细粒度的控制。文章详细阐述了模块初始化器的语法、用途以及与传统类型初始化器的区别,并通过示例代码展示了如何在实际项目中应用这一新特性。
|
3月前
|
编译器 C# 开发者
C# 9.0中的顶级语句:简化程序入口的新特性
【1月更文挑战第13天】本文介绍了C# 9.0中引入的顶级语句(Top-level statements)特性,该特性允许开发者在不使用传统的类和方法结构的情况下编写简洁的程序入口代码。文章详细阐述了顶级语句的语法、使用场景以及与传统程序结构的区别,并通过示例代码展示了其在实际应用中的便捷性。
|
6天前
|
XML C# 数据格式
C# 解析XML文件
C# 解析XML文件
14 1
|
30天前
|
安全 数据处理 C#
C# Post数据或文件到指定的服务器进行接收
C# Post数据或文件到指定的服务器进行接收
|
30天前
|
C# 开发工具 数据安全/隐私保护
C#实现基于Word保护性模板文件的修改
C#实现基于Word保护性模板文件的修改
|
1月前
|
Java C# 开发工具
第一个C#程序
第一个C#程序
12 0