zoukankan      html  css  js  c++  java
  • C#生成CHM文件(应用篇)之代码库编辑器(5)【总结、程序、源代码】

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

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

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

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

     

     解决方案如下

     //特别注意,如果路径中有中文,url会对其进行编码
     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中了,详细代码如下:

    System.Resources.ResourceManager resource = new System.Resources.ResourceManager("AlexisEditor.Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
                Bitmap bitmap = (Bitmap)resource.GetObject("bookicons");

                //将加载的位图的图片提取出来,并放在list中
                imageList = new ImageList();
                iconList = new List<Icon>();
                for (int i = 0; i < bitmap.Width / 16; i++)
                {
                    Bitmap img = bitmap.Clone(new Rectangle(16 * i, 0, 16, 16), bitmap.PixelFormat);//切割图标
                    img.MakeTransparent(Color.Magenta);//设置过滤色
                    imageList.Images.Add(img);
                    System.IntPtr iconHandle = img.GetHicon();
                    System.Drawing.Icon icon = Icon.FromHandle(iconHandle);
                    iconList.Add(icon);
                } 

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

    这边给出了如何将bmp图片转换为Icon图片的代码
    System.IntPtr iconHandle = img.GetHicon();                

    System.Drawing.Icon icon = Icon.FromHandle(iconHandle);

     整个IconHelper类的代码如下:

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

            public static Image GetBuildImage()
            {
                System.Resources.ResourceManager resource = new System.Resources.ResourceManager("AlexisEditor.Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
                return (Image)resource.GetObject("Build");
            }

            /// <summary>
            /// 书籍ico图标
            /// </summary>
            public static Icon BookIcon
            {
              get { return iconList[0]; }
            }


     二、WebBrowser控件的使用技巧      返回导航

     设置当前的Uri地址

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


    获取当前WebBrowser中的文档

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


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

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

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

    首先添加mshtml的引用

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

     

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

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

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

     三、XML的妙用之存储树           返回导航

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

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

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

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

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

    private void FromXML(System.Xml.XmlElement RootElement)
            {
                //this.defaultPage = RootElement.GetAttribute("DefaultTopic");
                this._title = RootElement.GetAttribute("Title");//标题
                nodeList.Clear();

                foreach (System.Xml.XmlNode node in RootElement.ChildNodes)
                {
                    if (node.Name == "Items")
                    {
                        NodesFromXML(nodeList, (System.Xml.XmlElement)node);
                    }
                }
            } 

     //xml转为为nodes
            private void NodesFromXML(CHMNodeList nodes, System.Xml.XmlElement RootElement)
            {
                foreach (System.Xml.XmlNode node in RootElement.ChildNodes)
                {
                    if (node.Name == "Node")
                    {
                        System.Xml.XmlElement element = (System.Xml.XmlElement)node;
                        CHMNode NewNode = new CHMNode();
                        NewNode.Name = element.GetAttribute("Name");
                        NewNode.Local = element.GetAttribute("Local");
                        NewNode.ImageNo = element.GetAttribute("ImageNumber");
                        nodes.Add(NewNode);
                        foreach (System.Xml.XmlNode node2 in element.ChildNodes)
                        {
                            if (node2.Name == "Items")
                            {
                                NodesFromXML(NewNode.Nodes, (System.Xml.XmlElement)node2);
                            }
                        }
                    }
                }
            }

     四、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,代码如下

    SolutionForm  form=new SolutionForm();

    form.Show(dockPanel);//显示目录窗体
    form.DockTo(dockPanel, DockStyle.Right); 

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

     五、 WinForm中的状态栏初探           返回导航

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

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

           ToolStripStatusLabel tsl = new ToolStripStatusLabel();
                tsl.Text = "正在编译....";
                ToolStripStatusLabel tslBuilding = new ToolStripStatusLabel();
                tsl.Image = IconHelper.GetBuildImage();
                tsl.Dock = DockStyle.Right;
                this.statusStrip.Items.AddRange(new ToolStripItem[] { tsl,tslBuilding });
                this.statusStrip.Text = "正在编译...";
                chmDocument.Compile();
                frmOutPut.TxtOutput.Text = chmDocument.OutPutText;
                this.statusStrip.Items.Remove(tsl);
                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文件

          ProcessStartInfo processStartInfo = new ProcessStartInfo();
                    processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                    processStartInfo.FileName = config.HhcPath;  //调入HHC.EXE文件
                    processStartInfo.Arguments = "\"" + strHhp + "\"";//获取空的HHP文件
                    processStartInfo.UseShellExecute = false;
                    processStartInfo.CreateNoWindow = true;
                    processStartInfo.RedirectStandardOutput = true;
                    helpCompileProcess.StartInfo = processStartInfo;
                    helpCompileProcess.Start();
                    helpCompileProcess.WaitForExit(); //组件无限期地等待关联进程退出 

         string _outPutText = helpCompileProcess.StandardOutput.ReadToEnd();//进程中的信息

     七、 TreeView节点重命名             返回导航

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

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

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


    八、DataGridView中的一些技巧 返回导航

    程序中使用DataGridView显示查询的结果,因为查询的结果是自己拼凑起来的,即使用Lucene.Net检索到多少内容,然后拼凑为DataTable,最后在绑定到
    DataGridView中,因为要在DataGridView中增加链接,使得点击链接可以打开文章进行编辑,所以在前台DataGridView可视化界面中增加LinkLabel的Column。
    并设置它的DataPropertyName设为后台DataTable中的名字,代码如下
    //创建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);
    注意,如果在可视化界面中不设置列的DataPropertyName属性,运行后会出现多列,有点类似于GridView中的AutoGenerateColumn属性

    然后遍历索引检索到的条数,将内容添加到DataTable中
    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("没有查到相关记录!");
    }
    使用this.dgvResult.DataSource = dtResult;绑定数据源

    下面来看看如何获取DataGridView选中行中所有列的数据,当我们点击超链接时会触发CellContentClick事件,我们在这个事件中获取选中行的
    private void dgvResult_CellContentClick(object sender, DataGridViewCellEventArgs e)
    {
                //点击的是超链接
                if (e.ColumnIndex==0)
                {
                    //获取当前行的文件名
                    string path=this.dgvResult.CurrentRow.Cells[3].Value.ToString();

                    //调用编辑窗口
                    GetNode(path,this.nodes);
                    CHMNode node = this.nodeOpen;
                    EditForm form = new EditForm();
                    form.Edit(node);
                    form.ShowDialog();
                }
    }
    这句话就是获取我们要打开文章
    string path=this.dgvResult.CurrentRow.Cells[3].Value.ToString();


    九、Lucene.Net简单的应用 返回导航

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

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

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

    //INDEX_STORE_PATH 为索引存储目录
    string INDEX_STORE_PATH = Application.StartupPath+@"\index";  

    //先存储索引
    IndexWriter writer = new IndexWriter(INDEX_STORE_PATH, new StandardAnalyzer(), true);
    SetIndex(writer,this.nodes);
     而SetIndex是使用递归的方法遍历树,有关写入Index的代码如下
    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);
    }

    然后查询
    1.申明一个IndexSearcher 2.设置查询路径 3.申明一个QueryParser 4.设置查询使用的value 5.查询 6.Hits 对象即查询结果
    //在从索引中查询           
    IndexSearcher searcher;
    searcher = new IndexSearcher(INDEX_STORE_PATH);
    QueryParser q = null;
    q = new QueryParser("title"new StandardAnalyzer());
    Query query = q.Parse(KEYWORD);
    Hits hits = searcher.Search(query);


     十、简易版的log类 返回导航

    在写一写“危险”代码时,我们经常使用try。。catch语句进行异常捕获,而且往往也会忘了对捕获信息的存储。这里提供一个简易版的日志记录类
    public static class LogHelper
    {
    public static void WriteLog(string log)
    {
    string path = Application.StartupPath + "\\log.txt";
    FileStream filestream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
    StreamWriter sw = new StreamWriter(filestream, Encoding.Default);
    sw.BaseStream.Seek(0, SeekOrigin.End);
    sw.WriteLine("*********Exception*********");
    sw.WriteLine("Time:" + DateTime.Now);
    sw.WriteLine("Message:" + log);
    sw.WriteLine("*********Exception*********");
    sw.WriteLine();
    sw.WriteLine();
    sw.Close();
    }
    }

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

     


  • 相关阅读:
    ThickBox弹出框的使用方法
    DATASET排序
    jQuery重要插件!
    获取所有querystring变量名
    using要写多少
    【MM系列】SAP MM模块-关于批次特性的查看和获取
    【MM系列】SAP SAP的账期分析和操作
    【ABAP系列】SAP ABAP基础-abap数据类型的解析整理
    【ABAP系列】SAP ABAP基础-录制BDC的MODE定义解析
    【ABAP系列】SAP ABAP基础-数据更新至数据库操作解析
  • 原文地址:https://www.cnblogs.com/alexis/p/1859801.html
Copyright © 2011-2022 走看看