NanUI for Winform 使用示例【第二集】——做一个所见即所得的Markdown编辑器
【请注意:此文已过期,0.6版NanUI实现方式不同!!!】
经过了这一个多星期的调整与修复,NanUI for .NET Winform的稳定版已经发布。应广大群友的要求,现已将NanUI的全部代码开源。
GitHub: https://github.com/NetDimension/NanUI
Release: https://github.com/NetDimension/NanUI/releases
这次发布的是一个相对稳定的版本,解决和改善了如下问题:
- 页面随机白屏问题(主要原因是GC自动回收后,造成内存地址不可读)
- NanUI编译版本改为.NET 4.0 Client Profile
- 托上面那条改进的福,NanUI现在支持Windows XP了
- 不再支持本地CEF运行支持文件,现在支持文件都需要在线下载安装,当然也可以手动下载离线包安装,但是不论那种方式,CEF都安装到一个共享的位置。CEF运行库只需下载安装一次,不会多次下载。
欢迎下载把玩,也欢迎进群讨论,群号241088256。
下面,进入我们的正题,使用NanUI以及手边的各种开源库制作一个所见即所得的Markdown编辑器。
NanUI系列目录
- NanUI for Winform发布,让Winform界面设计拥有无限可能
- NanUI for Winform 使用示例【第一集】——山寨个代码编辑器
- NanUI for Winform 使用示例【第二集】——做一个所见即所得的Markdown编辑器
NanUI for Winform 使用示例【第二集】
做一个所见即所得的Markdown编辑器
在本集中,使用了如下开源技术来方便的组建我们的“所见即所得Markdown编辑器”:
- bootstrap
- codeMirror
- jquery
- jquery.splitter.js
- markdown-js
- github-markdown.css
利用Nuget,获取上列的各种库不是难题。如效果图所示,我们可以方便的利用这些开源库来设计出心仪的页面。在此着重讲解网页前端和后台C#通信的技术。后面的文章里,凡是HTML、CSS和JS的内容我将称他们为“前端”、涉及C#编程的地方我会称他们为“后端”。
如图所示,软件将要与C#后端交互的几个地方有:
- 代码编辑框
- 新建文件按钮
- 打开文件按钮
- 保存文件按钮
在C#后端,建立HostEditor类来处理由前端发送回来的按钮事件。该类继承自基类JSObject,这个类负责与CEF的V8环境处理各种数据和对象。
- class HostEditor:JSObject
- {
- frmMain MainFrame;
- internal HostEditor(frmMain main)
- {
- MainFrame = main;
- AddFunction("setCleanState").Execute += SetCleanState;
- AddFunction("newFile").Execute += NewFile;
- AddFunction("openFile").Execute += OpenFile;
- AddFunction("saveFile").Execute += SaveFile;
- }
- private void SaveFile(object sender, Chromium.Remote.Event.CfrV8HandlerExecuteEventArgs e)
- {
- var contents = e.Arguments.FirstOrDefault(p => p.IsString);
- var result = false;
- if (contents != null)
- {
- result = MainFrame.SaveFile(contents.StringValue);
- }
- if (result)
- {
- e.SetReturnValue(this.GetCfrObject(new
- {
- success = true,
- fileName = MainFrame.CurrentFile.Name
- }));
- }
- else
- {
- e.SetReturnValue(this.GetCfrObject(new
- {
- success = false
- }));
- }
- }
- private void OpenFile(object sender, Chromium.Remote.Event.CfrV8HandlerExecuteEventArgs e)
- {
- var contents = e.Arguments.FirstOrDefault(p => p.IsString);
- string result = null;
- if (contents != null)
- {
- result = MainFrame.OpenFile(contents.StringValue);
- }
- if (!string.IsNullOrEmpty(result))
- {
- e.SetReturnValue(this.GetCfrObject(new
- {
- success = true,
- fileName = MainFrame.CurrentFile.Name,
- contents = result
- }));
- }
- else
- {
- e.SetReturnValue(this.GetCfrObject(new
- {
- success = false
- }));
- }
- }
- private void NewFile(object sender, Chromium.Remote.Event.CfrV8HandlerExecuteEventArgs e)
- {
- var contents = e.Arguments.FirstOrDefault(p => p.IsString);
- var result = false;
- result = MainFrame.NewFile(contents.StringValue);
- e.SetReturnValue(CfrV8Value.CreateBool(result));
- }
- private void SetCleanState(object sender, Chromium.Remote.Event.CfrV8HandlerExecuteEventArgs e)
- {
- if(e.Arguments.Length> && e.Arguments[].IsBool)
- {
- MainFrame.isClean = e.Arguments[].BoolValue;
- }
- }
- }
在主窗体的构造函数中,将上面的HostEditor类注册到NanUI的JS环境中,并命名为hostEditor,这样在前端的JS中就可以调用hostEditor对象以及对象中内置的C#方法了:
- GlobalObject.Add("hostEditor", new HostEditor(this));
在JS环境中hostEditor对象提供了以下几个方法来实现对当前代码编辑器里的内容进行新增、打开和保存的操作。
- hostEditor.newFile(string)
- hostEditor.openFile(string)
- hostEditor.saveFile(string)
- hostEditor.setCleanState(bool)
同时,将HostEditor中需要用到的新建文件、保存文件、打开文件等操作的方法放在主窗体中,方便前端JS调用。
- /// <summary>
- /// 标记文档是否被修改
- /// </summary>
- internal bool isClean = true;
- /// <summary>
- /// 当前文档的存储路径,如果为空则说明该文档是新文档
- /// </summary>
- internal string currentFilePath = string.Empty;
- /// <summary>
- /// 当前文档的FileInfo
- /// </summary>
- internal System.IO.FileInfo CurrentFile
- {
- get
- {
- return new System.IO.FileInfo(currentFilePath);
- }
- }
- /// <summary>
- /// 获得一个标识当前文档是否为新建文档
- /// </summary>
- private bool IsNewFile
- {
- get
- {
- return string.IsNullOrEmpty(currentFilePath);
- }
- }
- /// <summary>
- /// 新建文件
- /// </summary>
- /// <param name="contents">当前文档中的内容</param>
- /// <returns>如果新建成功则返回true</returns>
- internal bool NewFile(string contents)
- {
- var continueFlag = true;
- if (!isClean)
- {
- var ret = MessageBox.Show(this, "文件已经更改,是否保存下先?", "提示", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
- if (ret == DialogResult.Yes)
- {
- if (!SaveFile(contents))
- {
- continueFlag = false;
- }
- }
- else if (ret == DialogResult.Cancel)
- {
- continueFlag = false;
- }
- }
- if (!continueFlag)
- {
- return false;
- }
- currentFilePath = null;
- isClean = true;
- return true;
- }
- /// <summary>
- /// 打开文档
- /// </summary>
- /// <param name="contents">当前文档中的内容</param>
- /// <returns>如果新建成功则返回打开文档的内容</returns>
- internal string OpenFile(string contents)
- {
- var continueFlag = true;
- if (!isClean)
- {
- var ret = MessageBox.Show(this, "文件已经更改,是否保存下先?", "提示", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
- if (ret == DialogResult.Yes)
- {
- if (!SaveFile(contents))
- {
- continueFlag = false;
- }
- }
- else if (ret == DialogResult.Cancel)
- {
- continueFlag = false;
- }
- }
- if (!continueFlag)
- {
- return null;
- }
- var content = string.Empty;
- var openDialog = new OpenFileDialog()
- {
- AddExtension = true,
- Filter = "Markdown文件|*.md"
- };
- if (openDialog.ShowDialog() == DialogResult.OK)
- {
- currentFilePath = openDialog.FileName;
- var fileInfo = new System.IO.FileInfo(currentFilePath);
- content = System.IO.File.ReadAllText(fileInfo.FullName);
- }
- else
- {
- content = null;
- }
- return content;
- }
- /// <summary>
- /// 保存文档
- /// </summary>
- /// <param name="contents">当前文档中的内容</param>
- /// <returns>如果保存成功则返回true</returns>
- internal bool SaveFile(string contents) {
- if (!IsNewFile) {
- if (isClean) return true;
- System.IO.File.WriteAllText(currentFilePath, contents, Encoding.UTF8);
- isClean = true;
- return true;
- }
- var saveFileDialog = new SaveFileDialog()
- {
- AddExtension = true,
- Filter = "Markdown文件|*.md",
- OverwritePrompt = true
- };
- if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
- {
- currentFilePath = saveFileDialog.FileName;
- System.IO.File.WriteAllText(currentFilePath, contents, Encoding.UTF8);
- isClean = true;
- return true;
- }
- return false;
- }
如此这般,一个所见即所得的Markdown编辑器就制作完成了。有了这个小工具编辑GitHub的README文档就不会那么痛苦了。有兴趣的朋友可以自行到GitHub下载代码来把玩。
那么,NanUI的第二集示例就这么讲完了。最后还是欢迎大家留言,或者进群讨论。当然能在github够提供pull request是最好的。
附件
MarkdownDotNet.zip - 编译好的Markdown编辑器,欢迎下载体验,代码已上传到GitHub
NanUI for .NET Winform系列目录
- NanUI for Winform发布,让Winform界面设计拥有无限可能
- NanUI for Winform 使用示例【第一集】——山寨个代码编辑器
- NanUI for Winform 使用示例【第二集】——做一个所见即所得的Markdown编辑器
经过了这一个多星期的调整与修复,NanUI for .NET Winform的稳定版已经发布。应广大群友的要求,现已将NanUI的全部代码开源。
GitHub: https://github.com/NetDimension/NanUI
Release: https://github.com/NetDimension/NanUI/releases
如果你喜欢NanUI项目,你可以参与到NanUI的开发中来,当然你也可以更直接了当的支持我的工作,使用支付宝或微信扫描下面二维码请我喝一杯热腾腾的咖啡。
支付宝转账
微信转账
另外,打个广告,承接NanUI界面设计与接口开发(收费)。
案例展示
某聊天应用
某公司内部办公系统
NanUI for Winform 使用示例【第二集】——做一个所见即所得的Markdown编辑器的更多相关文章
- NanUI for Winform 使用示例【第一集】——山寨个代码编辑器
NanUI for Winform从昨天写博客发布到现在获得了和多朋友的关注,首先感谢大家的关注和支持!请看昨天本人的博文<NanUI for Winform发布,让Winform界面设计拥有无 ...
- '用Roslynpad做一个轻量级的C#编辑器'
博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:'用Roslynpad做一个轻量级的C#编辑器'.
- Winform窗体用对象数组做一个小项目
首先我我们看一下需求:我们要做的是显示员工信息,实现项目经理给员工评分的功能! 首先项目经理是评分的人所以没有用,因为我们自己写,评分的就是我们自己.所以我们要做的是先在vs也就是我们的环境里建一个项 ...
- 使用Vue自己做一个简单的MarkDown在线编辑器
1.首先要下载mark组件. npm install marked --save 2.在Vcontent.vue中简单写一些样式. <template> <div class=&qu ...
- 做一个所见即所得的CSS效果
style 也是标签(在非ie内核的浏览器中支持),我们将style设置成 contenteditable的时候,那么那的内容就可以编辑了.仔细的体验下,如果我们将背景修改成红色的.那么只要书写完,立 ...
- NanUI for Winform发布,让Winform界面设计拥有无限可能
如今,尽管WPF.UWP大行其道,大有把Winform打残干废的趋势.但是还是有那么一波顽固不化的老家伙们固守着Winform,其中就包括我. 好吧,既然都说Winform做得软件不如WPF界面美观效 ...
- Windows Phone开发(44):推送通知第二集——磁贴通知
原文:Windows Phone开发(44):推送通知第二集--磁贴通知 前面我们说了第一个类型--Toast通知,这玩意儿不知大家是不是觉得很新鲜,以前玩.NET编程应该没接触过吧? 其实这东西绝对 ...
- SpringBoot第二集:注解与配置(2020最新最易懂)
2020最新SpringBoot第二集:基础注解/基础配置(2020最新最易懂) 一.Eclipse安装SpringBoot插件 Eclipse实现SpringBoot开发,为便于项目的快速构建,需要 ...
- PB中用oracle的存储过程返回记录集做数据源来生成数据窗口,PB会找不到此存储过程及不能正常识别存储过程的参数问题(转)
(转)在PB中用oracle的存储过程返回记录集做数据源来生成数据窗口 首先oracle的存储过程写法与MSSQL不一样,差别比较大. 如果是返回数据集的存储过程则需要利用oracle的包来定义游标. ...
随机推荐
- Bootstrap~Panel和Table
回到目录 在我们对一个页面进行设计时,分块是必须的,没有一个网站是一栏而下的,除非你是在看小说,否则你的页面设计一定是分块的,即它由于多个panel组件,在bootstrap里叫到栅格系统,而在每行每 ...
- fir.im Weekly - 深度揭秘 App 启动全过程
世纪寒潮席卷全中国,可谓普天之下莫低0℃.无论怎样的严寒都抵挡不了程序员们的创造的激情... 本期的 fir.im Weekly ,最新的 iOS/Android 开发资源,GitHub 源码.前端技 ...
- WebClient.DownloadFile(线程机制,异步下载文件)
线程机制(避免卡屏),异步下载文件. 我做网站的监控,WebClient.DownloadFile这个方法是我经常用到的,必要的时候肯定是要从网上下载些什么(WebRequest 也可以下载网络文件, ...
- 使用uGUI制作游戏内2D动画
在3D的游戏中制作2D的效果是一个很常见的需求,我在很早前玩过一个叫做艾尔之光的游戏,里面就大量的使用了这个技术.就像下面图片中的伤害数字,这些数字往往还是有一些动画效果在里面的,比如大小的变化,颜色 ...
- Mybatis的缺陷
Mybatis是业界非常流行的持久层框架,轻量级.易用,在金融IT领域完全是领军地位,比Hibernate更受欢迎,优势非常多,也是非常值得我们学习的.但Mybatis并不尽善尽美,其自身的设计.编码 ...
- 关于JavaScript闭包的小问题
怎么说,闭包大体也就是作用域的问题.闭包的一个用途是用于模块化,保护函数体内的私有变量,如: var foo = function(){ var _num = 1; var sayHello = fu ...
- 快速入门系列--MVC--07与HTML5移动开发的结合
现在移动互联网的盛行,跨平台并兼容不同设备的HTML5越来越盛行,很多公司都在将自己过去的非HTML5网站应用渐进式的转化为HTML5应用,使得一套代码可以兼容不同的物理终端设备和浏览器,极大的提高了 ...
- python导入模块和包的使用
做项目的时候经常会要求做到模块化管理,即同样功能的代码放到同一个文件夹下,以便于方便管理,相信很多人对模块的引用都模糊不清,今天鄙人在这里就总结下. 一.模块导入的使用 在同一个文件夹下有两个文件分别 ...
- SQL Server中一个隐性的IO性能杀手-Forwarded record
简介 最近在一个客户那里注意到一个计数器很高(Forwarded Records/Sec),伴随着间歇性的磁盘等待队列的波动.本篇文章分享什么是forwarded record,并从原理上谈一 ...
- Package gp in the OpenCASCADE
Package gp in the OpenCASCADE eryar@163.com China 一.简介 Introduction to Package gp gp是几何处理程序包(Geometr ...