因为前一段时间看到 NetAnalyzer 在Windows10系统下UI表现惨不忍睹,所以利用一段时间为了学习一下WPF相关的内容,于是停停写写,用了WPF相关的技术,两个星期做了一个Markdown编辑器,并且集成了:编辑时与网页同步,博客发布,PDF导出等功能。也主要是不忿某款外国软件收费,故有此作。
代码下载地址
展示与说明
代码同步编辑
博客发布
代码说明
博客发布
MarkWord支持博客园和CSDN博客发布,并且可以进行图片同步(无论是本地图片还是网上的图片,都可以同步到博客服务器)。 该功能使用了MetaWeblog技术。使用方法如下,其中图片上传为newMediaObject 接口
1 ///
2 ///文档上传,包括新增与更新3 ///
4 public static string UploadBlogs(string apiUrl, string BlogId, string userId, string password, string
5 BlogsModel, string postId, string title, string Markdown, boolpublish)6 {7
8 int procIndex = 1;9
10 SendMsg(5, procIndex, "准备数据中……");11 //转换为html
12 string Blogs = string.Format("\r\n{0}",13 CommonMark.CommonMarkConverter.Convert(Markdown));14 metaTools.Url =apiUrl;15
16
17 Post blogsPost = newPost();18
19 //分类
20 List tmpCategories = new List();21 tmpCategories.Add("");//添加空分类,是因为部分博客(如csdn)字段这部分为必填字段不添加会产生异常
22 blogsPost.categories =tmpCategories.ToArray();23
24 //添加时间
25 blogsPost.dateCreated =DateTime.Now.ToLocalTime();26
27 //添加标题
28 blogsPost.title =title;29
30
31 //指定文章编号
32 blogsPost.postid =postId;33
34 //内容
35 blogsPost.description = BlogsModel.Contains("{0}") ?//必须使用{0}占位符
36 string.Format(BlogsModel, Blogs) : //根据模板生成数据 主要是为了制定Markdown模板
37 BlogsModel + Blogs; //通过前缀方式添加38
39 //开始查找图片并更新到服务器
40 HtmlDocument htmlDoc = newHtmlDocument();41 WebClient webClient = newWebClient();42 htmlDoc.LoadHtml(blogsPost.description);43 var ImgList = htmlDoc.DocumentNode.Descendants("img");44
45 int procCount = 3 +ImgList.Count();46
47 SendMsg(procCount, procIndex++, string.Format("数据分析完成,总共需要上传{0}张图片", ImgList.Count()));48 int imgErr = 0;//图片上传错误数量
49 foreach (var i inImgList)50 {51 SendMsg(procCount, procIndex++, "正在上传图片数据……");52 //获取图片文件字符串
53 string ImgUrl = i.GetAttributeValue("src", "");54 if (string.IsNullOrEmpty(ImgUrl))55 {56 imgErr++;57 continue;58 }59 try
60 {61 var imgeData = webClient.DownloadData(ImgUrl);//下载文件
62
63 FileData fd = default(FileData);64 fd.bits = imgeData;//图片数据
65 fd.name = Path.GetExtension(ImgUrl);//文件名
66 fd.type = string.Format("image/{0}", fd.name.Substring(1));67
68 UrlData obj =metaTools.newMediaObject(BlogId, userId, password, fd);69 blogsPost.description =blogsPost.description.Replace(ImgUrl, obj.url);70 }71 catch
72 {73 imgErr++;74 continue;75 }76 }77 try
78 {79 if (string.IsNullOrWhiteSpace(postId))80 {81 SendMsg(procCount, procIndex++, "开始发布文章……");82 postId =metaTools.newPost(BlogId, userId, password, blogsPost, publish);83 }84 else
85 {86 SendMsg(procCount, procIndex++, "正在更新文章……");87 metaTools.editPost(postId, userId, password, blogsPost, publish);88 }89 }90 catch(Exception ex)91 {92 Common.ShowMessage("博客发送失败");93 returnpostId;94 }95
96 if (imgErr == 0)97 {98 Common.ShowMessage("博客发送成功");99 }100 else
101 {102 Common.ShowMessage(string.Format("博客发送成功了,但是有{0}张图片发送失败", imgErr));103 }104 SendMsg(procCount, procCount, "完成");105 returnpostId;106
107 }
具体API实现方法见代码中的BlogsAPI项目
PDF导出
PDF导出功能,使用了HTML转PDF方法 相关DLL已经包含在项目当中了
1 //html to Pdf
2 public static void HtmlToPdf(string filePath, string html, bool isOrientation = false)3 {4 if (string.IsNullOrEmpty(html))5 html = "Null";6 //创建全局信息
7 GlobalConfig gc = newGlobalConfig();8 gc.SetMargins(new Margins(50, 50, 60, 60))9 .SetDocumentTitle("MarkWord")10 .SetPaperSize(PaperKind.A4)11 .SetPaperOrientation(isOrientation)12 .SetOutlineGeneration(true);13
14
15 //页面信息
16 ObjectConfig oc = newObjectConfig();17 oc.SetCreateExternalLinks(false)18 .SetFallbackEncoding(Encoding.UTF8)19 .SetLoadImages(true)20 .SetScreenMediaType(true)21 .SetPrintBackground(true);22 //.SetZoomFactor(1.5);
23
24 var pechkin = newSimplePechkin(gc);25 pechkin.Finished +=Pechkin_Finished;26 pechkin.Error +=Pechkin_Error;27 pechkin.ProgressChanged +=Pechkin_ProgressChanged;28 var buf =pechkin.Convert(oc, html);29
30 if (buf == null)31 {32 Common.ShowMessage("导出异常");33 return;34 }35
36 try
37 {38 string fn = filePath; //Path.GetTempFileName() + ".pdf";
39 FileStream fs = newFileStream(fn, FileMode.Create);40 fs.Write(buf, 0, buf.Length);41 fs.Close();42
43 //Process myProcess = new Process();44 //myProcess.StartInfo.FileName = fn;45 //myProcess.Start();
46 }47 catch{ }48 }
CommonMark使用
最后就Markdown的转换,在这里我使用了CommonMark,使用方法比较简单
CommonMark.CommonMarkConverter.Convert("### test")
编辑与html页面同步原理
在改工具中比较有意思的就是编辑器与Webbrower的页面同步功能,包括页面"无刷新"同步呈现,已经页面同步滚动,在这里使用的是编辑器触发 textEditor_TextChanged 事件和 ScrollViewer 触发的scrViewer_ScrollChanged 分别通过webbrowser 的 InvokeScript动态调用Js实现的,我们先来看看两个js的内容
同步呈现
functionupdatePageContent(msg){
document.body.innerHTML=msg;
}
非常简单,只是将转换出来的html直接通过document.body.innerHTML 赋给当前页面既可以了。
同步滚动
functionscrollToPageContent(value){
window.scrollTo(0, value * (document.body.scrollHeight -document.body.clientHeight));
}
这部分,是需要通过WPF页面转过来一个对应的页面位移高度与窗口显示高度的一个页面比例,然后在webbrowser中根据该比例计算页面需要偏移量来实现同步移动
而对应的WPF端的代码为
1 ///
2 ///同步呈现3 ///
4 ///
5 public void LoadBody(stringMarkValue)6 {7
8 if (winWebDoc.Document == null)9 return;10 winWebDoc.Document.InvokeScript("updatePageContent", new object[] { CommonMark.CommonMarkConverter.Convert(MarkValue) });11 }12
13 ///
14 ///文本更变15 ///
16 ///
17 ///
18 private void textEditor_TextChanged(objectsender, EventArgs e)19 {20 if (!isLoadFlag)21 {22 if (this.textEditor.Text != "" && scrViewer != null)23 if (scrViewer.ScrollableHeight ==scrViewer.VerticalOffset)24 scrViewer.ScrollToBottom();25
26 BLL.FileManager.isChangeFlag = true;27 }28 //加载文档
29 if (MarkDoc == null)30 return;31 if (Config.Common.WorkType ==WorkType.Both)32 {33 MarkDoc.LoadBody(this.textEditor.Text);34 }35 }36 //37 ///
38 ///同步滚动39 ///
40 ///
41 public void ScrollAuto(doublevalue)42 {43 if (winWebDoc.Document == null)44 return;45 winWebDoc.Document.InvokeScript("scrollToPageContent", new object[] { value.ToString(System.Globalization.CultureInfo.InvariantCulture) });46
47 }48 //计算比例
49 public doubleScrollViewerPositionPercentage50 {51 get
52 {53 double num = this.scrViewer.ExtentHeight - this.scrViewer.ViewportHeight;54 doubleresult;55 if (num != 0.0)56 {57 result = this.scrViewer.VerticalOffset /num;58 }59 else
60 {61 result = 0.0;62 }63 returnresult;64 }65 }66
67 //触发同步
68 private void scrViewer_ScrollChanged(objectsender, ScrollChangedEventArgs e)69 {70 if (MarkDoc == null) return;71 if (Config.Common.WorkType ==WorkType.Both)72 {73 MarkDoc.ScrollAuto(this.ScrollViewerPositionPercentage);74 }75 }
至此,Markword 中设计到的内容点已经基本覆盖到了,如有疑问欢迎交流!!!
最后来一发小广告
NetAnalyzer2016网络协议分析软件源码开放购买,可以分析80多种协议,支持http数据还原(包含chunked和gzip数据) ,欢迎大家可以支持一下!!
祝大家周末愉快