zoukankan      html  css  js  c++  java
  • Excel催化剂开源第29波-在Winform上使用富文本编辑器控件

    富文本编辑器,一般都是BS架构专利一般,好像百度有一个开源的比较出名,但无奈这些都只能用在JS上,在BS网页端开发上使用。像Winform开发的VSTO,只能羡慕的份。和一般Winform上用的RichText控件,别人BS的富文本编辑器就强大得多。

    笔者找寻过程中,也总算找到一款很不错的开源控件,将它的dll编译后,还真能用了。

    在Excel催化剂的批量邮件功能中, 为了得到最好的体验,不止是不用依赖OUTLOOK的组件来发邮件(好像VBA的方案只能用outlook组件,用户电脑没安装outlook就不能用),同时为了让用户可以在邮件正文编辑区的使用体验和outlookup或网页端的发邮件体验一样,用了一个第3方的富文本编辑器,不是RichText控件,所以对网页的支持特别友好,随便复杂网页上的内容,粘贴过来,渲染得非常出色。

     
    邮件群发功能

    这个第3方富文本编辑器控件,不单单可以在里面作一些格式的配置,还可以有打开html文件,直接从网页其他地方复杂内容直接粘贴和插入本地图片,有了这些能力,在发送邮件正文时,使用体验就非常棒,可以发送出去的邮件正文,不是纯文本的形式,毫无格式,同时可以发送本地图片,特别是有些时候,正文内容需要说明一切重要内容,无需点开附件查看,或者正文中有邮件签名时,签名位置有图片也是常有的事情,特别是放个二维码之类的。

    同样地水平有限,具体技术细节也不懂,只是能用起来的程度。只需引用以下的dll即可。
    有兴趣的可以看作者的github代码https://github.com/yahch/kwig

     
    富文本控件dll

    此控件是自定义控件,可以通过设计器拖拉出来。

    群发邮件核心代码,用这个富文本控件,拿到其渲染后的html文件,发邮件当然有C#自己的轮子,也很好用。

    string htmlBodyContent = this.kEditor1.Html;
    
            private async Task BatchSendingEmail()
            {
                SmtpClient client = GetSmtpClient();
                //string[] lisColNames = { "邮件标题", "收件人邮箱", "抄送邮箱", "密件抄送邮箱", "优先级", "附件路径" };
                int ColIndexOfsubject = this.SendInfoListObject.ListColumns["邮件标题"].Index;
                int ColIndexOfTo = this.SendInfoListObject.ListColumns["收件人邮箱"].Index;
                int ColIndexOfCC = this.SendInfoListObject.ListColumns["抄送邮箱"].Index;
                int ColIndexOfBCC = this.SendInfoListObject.ListColumns["密件抄送邮箱"].Index;
                int ColIndexOfPriority = this.SendInfoListObject.ListColumns["优先级"].Index;
                int ColIndexOfAttachments = this.SendInfoListObject.ListColumns["附件路径"].Index;
                int ColIndexOfAttachmentsSize = this.SendInfoListObject.ListColumns["附件最大限制(M)"].Index;
    
                int ColIndexOfStatus = this.SendInfoListObject.ListColumns["发送状态"].Index;
                string htmlBodyContent = this.kEditor1.Html;
    
                foreach (Excel.ListRow row in this.SendInfoListObject.ListRows)
                {
                    if (isCancelSending == false)
                    {
                        try
                        {
                            string subject = row.Range[ColIndexOfsubject].Value2 != null ? row.Range[ColIndexOfsubject].Value2.ToString() : string.Empty;
                            string to = row.Range[ColIndexOfTo].Value2 != null ? row.Range[ColIndexOfTo].Value2.ToString() : string.Empty;
                            string cc = row.Range[ColIndexOfCC].Value2 != null ? row.Range[ColIndexOfCC].Value2.ToString() : string.Empty;
                            string bcc = row.Range[ColIndexOfBCC].Value2 != null ? row.Range[ColIndexOfBCC].Value2.ToString() : string.Empty;
    
                            string priority = row.Range[ColIndexOfPriority].Value2 != null ? row.Range[ColIndexOfPriority].Value2.ToString() : "正常";
                            MailPriority mailPriority = priority == "高" ? MailPriority.High : priority == "低" ? MailPriority.Low : MailPriority.Normal;
    
                            string attachments = row.Range[ColIndexOfAttachments].Value2 != null ? row.Range[ColIndexOfAttachments].Value2.ToString() : string.Empty;
                            int attachmentsSize = int.Parse(row.Range[ColIndexOfAttachmentsSize].Value2 != null ? row.Range[ColIndexOfAttachmentsSize].Value2.ToString() : "0");
    
                            long totalLength = 0;
                            foreach (var filePath in attachments.Split(new char[] { ';', ';' }, StringSplitOptions.RemoveEmptyEntries))
                            {
                                FileInfo fileInfo = new FileInfo(filePath);
                                totalLength = totalLength + fileInfo.Length;
                            }
                            if (totalLength / 1024.0 / 1024.0 > attachmentsSize)
                            {
                                row.Range[ColIndexOfStatus].Value2 = "发送失败,附件大小超出最大限制";
                                continue;
                            }
    
                            if (!string.IsNullOrEmpty(subject) && !string.IsNullOrEmpty(to))
                            {
                                MailMessage mailMessage = GetMailMessage(row, subject, to, cc, bcc, mailPriority, attachments, htmlBodyContent);
    
                                try
                                {
                                    await client.SendTaskAsync(mailMessage);
                                    row.Range[ColIndexOfStatus].Value2 = "发送完成";
                                }
                                catch (Exception ex)
                                {
                                    row.Range[ColIndexOfStatus].Value2 = "发送失败,原因为" + ex.Message;
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            row.Range[ColIndexOfStatus].Value2 = "发送失败,原因为" + ex.Message;
                        }
                        finally
                        {
                            await Task.Factory.StartNew(() => Thread.Sleep(int.Parse(Properties.Settings.Default.BatchMailSendStopSecond) * 1000));
                        }
    
                    }
    
                }
                //this.Close();
                this.Invoke(new Action(() => this.Close()));
    
            }
    
    

    此控件,需要重写的方法,具体原理自己去看作者示例文档解释

            public void OnEditorErrorOccured(Exception ex)
            {
                //throw new NotImplementedException();
            }
    
            public void OnEditorLoadComplete()
            {
                //throw new NotImplementedException();
            }
    
            public void OnInsertImageClicked()
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Filter = "图片|*.jpg;*.png;*.gif";
                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    string n = "<img src="file://" + ofd.FileName + "" />";
                    kEditor1.InsertNode(n);
                    //把图片放到字典中,供之后清洗时取出来
                    dicImageInfo.Add("t" + (dicImageInfo.Count + 1), "file://" + ofd.FileName);
                }
    
    
            }
    
            public void OnOpenButtonClicked()
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Filter = "*.html|*.html";
                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    string text = System.IO.File.ReadAllText(ofd.FileName);
                    kEditor1.InsertNode(text);
                }
            }
    
            public void OnSaveButtonClicked()
            {
                SaveFileDialog sfd = new SaveFileDialog();
                sfd.Filter = "*.html|*.html";
                if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    System.IO.File.WriteAllText(sfd.FileName, kEditor1.Html);
                }
            }
    

    结语

    接连的这几篇文章,大家都可以感受到,对于一个业务导向的业余开发者来说,如果找到好的轮子,也可以实现很多复杂场景,用户体验也可以做得很好。所以VSTO和VBA开发,真的有一个本质的区别,VBA大不了最多用下系统的API函数,OFFICE的对象模型,但在VSTO的世界里,只要用心找,可以尽性地用尽一切世界上优秀的代码轮子。

    技术交流QQ群

    QQ群名:Excel催化剂开源讨论群, QQ群号:788145319

     
    Excel催化剂开源讨论群二维码

    关于Excel催化剂

    Excel催化剂先是一微信公众号的名称,后来顺其名称,正式推出了Excel插件,插件将持续性地更新,更新的周期视本人的时间而定争取一周能够上线一个大功能模块。Excel催化剂插件承诺个人用户永久性免费使用!

    Excel催化剂插件使用最新的布署技术,实现一次安装,日后所有更新自动更新完成,无需重复关注更新动态,手动下载安装包重新安装,只需一次安装即可随时保持最新版本!

    Excel催化剂插件下载链接:https://pan.baidu.com/s/1Iz2_NZJ8v7C9eqhNjdnP3Q

     
    联系作者
     
    公众号

    取名催化剂,因Excel本身的强大,并非所有人能够立马享受到,大部分人还是在被Excel软件所虐的阶段,就是头脑里很清晰想达到的效果,而且高手们也已经实现出来,就是自己怎么弄都弄不出来,或者更糟的是还不知道Excel能够做什么而停留在不断地重复、机械、手工地在做着数据,耗费着无数的青春年华岁月。所以催生了是否可以作为一种媒介,让广大的Excel用户们可以瞬间点燃Excel的爆点,无需苦苦地挣扎地没日没夜的技巧学习、高级复杂函数的烧脑,最终走向了从入门到放弃的道路。

    最后Excel功能强大,其实还需树立一个观点,不是所有事情都要交给Excel去完成,也不是所有事情Excel都是十分胜任的,外面的世界仍然是一个广阔的世界,Excel只是其中一枚耀眼的明星,还有其他更多同样精彩强大的技术、工具等。*Excel催化剂也将借力这些其他技术,让Excel能够发挥更强大的爆发!

    关于Excel催化剂作者

    姓名:李伟坚,从事数据分析工作多年(BI方向),一名同样在路上的学习者。
    服务过行业:零售特别是鞋服类的零售行业,电商(淘宝、天猫、京东、唯品会)

    技术路线从一名普通用户,通过Excel软件的学习,从此走向数据世界,非科班IT专业人士。
    历经重重难关,终于在数据的道路上达到技术平原期,学习众多的知识不再太吃力,同时也形成了自己的一套数据解决方案(数据采集、数据加工清洗、数据多维建模、数据报表展示等)。

    擅长技术领域:Excel等Office家族软件、VBA&VSTO的二次开发、Sqlserver数据库技术、Sqlserver的商业智能BI技术、Powerbi技术、云服务器布署技术等等。

    2018年开始职业生涯作了重大调整,从原来的正职工作,转为自由职业者,暂无固定收入,暂对前面道路不太明朗,苦重新回到正职工作,对Excel催化剂的运营和开发必定受到很大的影响(正职工作时间内不可能维护也不可能随便把工作时间内的成果公布于外,工作外的时间也十分有限,因已而立之年,家庭责任重大)。

    和广大拥护者一同期盼:Excel催化剂一直能运行下去,我所惠及的群体们能够给予支持(多留言鼓励下、转发下朋友圈推荐、小额打赏下和最重点的可以和所在公司及同行推荐推荐,让我的技术可以在贵司发挥价值,实现双赢(初步设想可以数据顾问的方式或一些小型项目开发的方式合作)。

    姓名:李伟坚 从事数据分析工作多年(BI方向),一名同样在路上的学习者。 技术路线从一名普通用户,通过Excel软件的学习,从此走向数据世界,非科班IT专业人士。 历经重重难关,终于在数据的道路上达到技术平原期,学习众多的知识不再太吃力,同时也形成了自己的一套数据解决方案(数据采集、数据加工清洗、数据多维建模、数据报表展示等)。 擅长技术领域:Excel等Office家族软件、VBAVSTO的二次开发、Sqlserver数据库技术、Sqlserver的商业智能BI技术、Powerbi技术、云服务器布署技术等等。 2018年起成为自由职业工作者,期待Excel催化剂可以一直能运行下去,我所惠及的群体们能够给予支持(多留言鼓励下、转发下朋友圈推荐、小额打赏下和最重点的可以和所在公司及同行推荐推荐,让我的技术可以在贵司发挥价值,实现双赢,初步设想可以数据顾问的方式或一些小型项目开发的方式合作。
  • 相关阅读:
    ECSHOP获取当前分类所在顶级分类信息
    二级域名会不会分散主域名权重
    ECSHOP增加模板页的方法
    ECSHOP之transport.js/run() error:'process_request' 未定义
    AIR任务栏图标的闪烁
    googlemap数据采集器(三)
    Flex中List自己定义itemrenderer渲染问题的解决
    GIS理论(墨卡托投影、地理坐标系、地面分辨率、地图比例尺、Bing Maps Tile System)
    android下歌曲名称乱码的解决办法
    史上最强劲的android模拟器命令详解
  • 原文地址:https://www.cnblogs.com/ExcelCuiHuaJi/p/10579335.html
Copyright © 2011-2022 走看看