zoukankan      html  css  js  c++  java
  • 不再需要ImageOle或DynamicGifCtl,.NET实现IM编辑控件

      多年前写过一篇文章《C# 实现IM聊天信息输入显示控件(1)-显示GIF动画图片》,主要是使用ActiveX控件实现RichTextBox插入gif动画图片,包括使用QQ的ImageOle和飞信使用的DynamicGifCtl,这2种方式都需要先注册ActiveX。后续发现QQ新版本没有再使用ImageOle,最近刚好有这方面的需求,于是通过万能的谷歌,找到了相关的资料,不敢独享,于是就有了这篇文章。

    一、致谢

      感谢大神万大侠,没有他的系列文章和介绍,我也不可能写下这篇文章。我只是在他提供的控件基础上进行简单的封装,以便用于.NET。万大侠的系列文章地址:致力于richedit应用于IM解决方案

    二、im_richedit简介

      万大侠的im_richedit提供了2个抽象类和一个函数供实现一个IMRichTextBox,它们分别是:

      1、IMRichEditDelegate类

    class IMRichEditDelegate {
     public:
      virtual void EraseBackground(HDC dc, const RECT& rect) = 0;
      virtual void PostRenderRichObject(ULONG richobject_id,
                                        HDC dc, const RECT& rect) = 0;
    };
    

      2、IMRichEdit类

    class IMRichEdit {
     public:
      virtual void DeleteThis() = 0;
      virtual int  GetCharSize() const = 0;
      virtual void SetCharSize(int size) = 0;
      virtual BSTR GetCharFace() const = 0;  // 注意, 返回的BSTR需要释放!!!
      virtual void SetCharFace(const wchar_t* face_name) = 0;
      virtual bool GetCharBold() const = 0;
      virtual void SetCharBold(bool bold) = 0;
      virtual bool GetCharItalic() const = 0;
      virtual void SetCharItalic(bool italic) = 0;
      virtual COLORREF GetCharColor() const = 0;
      virtual void SetCharColor(COLORREF color) = 0;
      virtual int  GetSelectionCharSize() const = 0;
      virtual void SetSelectionCharSize(int size) = 0;
      virtual BSTR GetSelectionCharFace() const = 0;
      virtual void SetSelectionCharFace(const wchar_t* face_name) = 0;
      virtual bool GetSelectionCharBold() const = 0;
      virtual void SetSelectionCharBold(bool bold) = 0;
      virtual bool GetSelectionCharItalic() const = 0;
      virtual void SetSelectionCharItalic(bool italic) = 0;
      virtual COLORREF GetSelectionCharColor() const = 0;
      virtual void SetSelectionCharColor(COLORREF color) = 0;
      virtual int  SaveSelectionCharFormat() = 0;
      virtual bool RestoreSelectionCharFormat(int save_state) = 0;
      virtual void SelectAll() = 0;
      virtual void Cut() = 0;
      virtual void Copy() = 0;
      virtual void Paste() = 0;
      virtual void ResetContent() = 0;
      virtual void SetCaretToEnd() = 0;
      virtual void ScrollToCaret() = 0;
      virtual void InsertText(const wchar_t* text) = 0;
      virtual bool InsertLink(const wchar_t* text) = 0;
      virtual void InsertBreak() = 0;
      virtual ULONG InsertRichObject(IMRichObjectType type) = 0;
      virtual ULONG GetRichObjectId(IOleObject* ole_object) const = 0;
      virtual bool  GetRichObjectType(ULONG richobject_id,
                                      IMRichObjectType* type) const = 0;
      // picture_filepath缓冲区大小为MAX_PATH.
      virtual bool GetRichObjectPicture(ULONG richobject_id,
                                         wchar_t* picture_filepath) const = 0;
      virtual bool SetRichObjectPicture(ULONG richobject_id,
                                         const wchar_t* picture_filepath) = 0;
      // Tag含义:
      //   IMRichObjectCustomPicture:  自定义
      //   IMRichObjectSystemPicture:  系统编号
      //   IMRichObjectFancyCharacter: 字符值
      virtual bool GetRichObjectTag(ULONG richobject_id, int* tag) const = 0;
      virtual bool SetRichObjectTag(ULONG richobject_id, int tag) = 0;
      virtual bool GetRichObjectFrameCount(ULONG richobject_id,
                                           UINT* frame_count) const = 0;
      virtual bool GetRichObjectCurremtFrame(ULONG richobject_id,
                                             UINT* current_frame) const = 0;
    };
    

      3、CreateIMRichEdit函数

    IM_RICHEDIT_EXPORT im_richedit::IMRichEdit* CreateIMRichEdit(
        IRichEditOle* richedit_ole, im_richedit::IMRichEditDelegate* delegate);
    

    三、.NET IMRichTextBox实现

      主要参考万大侠提供的示例,使用C++/CLI对im_richedit进行封装。

    1、IMRichEditDelegate抽象类实现

      IMRichEditDelegateImpl.h

    #pragma once
    #include "im_richedit/im_richedit_sdk.h"
    
    namespace Starts2000
    {
    	namespace Forms
    	{
    		namespace Control
    		{
    			class IMRichEditDelegateImpl : public im_richedit::IMRichEditDelegate
    			{
    			public:
    				IMRichEditDelegateImpl();
    				void EraseBackground(HDC dc, const RECT& rect);
    				void PostRenderRichObject(ULONG richobject_id, HDC dc, const RECT& rect);
    			};
    		}
    	}
    }
    

      IMRichEditDelegateImpl.cpp

    #include "IMRichEditDelegateImpl.h"
    
    namespace Starts2000
    {
    	namespace Forms
    	{
    		namespace Control
    		{
    			using namespace System::Drawing;
    
    			IMRichEditDelegateImpl::IMRichEditDelegateImpl()
    			{
    			}
    
    			void IMRichEditDelegateImpl::EraseBackground(HDC dc, const RECT& rect)
    			{
    				COLORREF old_color = ::SetBkColor(dc, GetSysColor(COLOR_WINDOW));
    				if (old_color != CLR_INVALID)
    				{
    					::ExtTextOut(dc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
    					::SetBkColor(dc, old_color);
    				}
    			}
    
    			void IMRichEditDelegateImpl::PostRenderRichObject(ULONG richobjectId,
    				HDC dc, const RECT& rect)
    			{
    			}
    		}
    	}
    }
    

    2、定义IIMRichTextBox接口,并使用万大侠提供的IMRichEdit抽象类进行实现。

      IIMRichTextBox.h

    #pragma once
    
    #include "im_richedit/im_richedit_sdk.h"
    
    namespace Starts2000
    {
    	namespace Forms
    	{
    		namespace Control
    		{
    			using namespace System;
    			using namespace System::Drawing;
    			using namespace System::Runtime::InteropServices;
    
    			public enum struct IMRichObjectType
    			{
    				CustomPicture = im_richedit::IMRichObjectCustomPicture,
    				SystemPicture = im_richedit::IMRichObjectSystemPicture,
    				FancyCharacter = im_richedit::IMRichObjectFancyCharacter
    			};
    
    			public interface class IIMRichTextBox
    			{
    				Int32  GetCharSize() = 0;
    				void SetCharSize(Int32 size) = 0;
    				String^ GetCharFace() = 0;  // 注意, 返回的BSTR需要释放!!!
    				void SetCharFace(String^ faceNname) = 0;
    				Boolean GetCharBold() = 0;
    				void SetCharBold(Boolean bold) = 0;
    				Boolean GetCharItalic() = 0;
    				void SetCharItalic(Boolean italic) = 0;
    				Color GetCharColor() = 0;
    				void SetCharColor(Color color) = 0;
    				Int32  GetSelectionCharSize() = 0;
    				void SetSelectionCharSize(Int32 size) = 0;
    				String^ GetSelectionCharFace() = 0;
    				void SetSelectionCharFace(String^ faceName) = 0;
    				Boolean GetSelectionCharBold() = 0;
    				void SetSelectionCharBold(Boolean bold) = 0;
    				Boolean GetSelectionCharItalic() = 0;
    				void SetSelectionCharItalic(Boolean italic) = 0;
    				Color GetSelectionCharColor() = 0;
    				void SetSelectionCharColor(Color color) = 0;
    				Int32  SaveSelectionCharFormat() = 0;
    				Boolean RestoreSelectionCharFormat(int saveState) = 0;
    				void SelectAll() = 0;
    				void Cut() = 0;
    				void Copy() = 0;
    				void Paste() = 0;
    				void ResetContent() = 0;
    				void SetCaretToEnd() = 0;
    				void ScrollToCaret() = 0;
    				void InsertText(String^ text) = 0;
    				Boolean InsertLink(String^ text) = 0;
    				void InsertBreak() = 0;
    				UInt32 InsertRichObject(IMRichObjectType type) = 0;
    				UInt32 GetRichObjectId(IntPtr oleObjectPtr) = 0;
    				Boolean  GetRichObjectType(UInt32 richobjectId, [Out] IMRichObjectType %type) = 0;
    				// picture_filepath缓冲区大小为MAX_PATH.
    				Boolean GetRichObjectPicture(UInt32 richobjectId, String^ pictureFilePath) = 0;
    				Boolean SetRichObjectPicture(UInt32 richobjectId, String^ pictureFilePath) = 0;
    				// Tag含义:
    				//   IMRichObjectCustomPicture:  自定义
    				//   IMRichObjectSystemPicture:  系统编号
    				//   IMRichObjectFancyCharacter: 字符值
    				Boolean GetRichObjectTag(UInt32 richobjectId, [Out] IMRichObjectType %tag) = 0;
    				Boolean SetRichObjectTag(UInt32 richobjectId, IMRichObjectType tag) = 0;
    				Boolean GetRichObjectFrameCount(UInt32 richobjectId, [Out] Int32 %frameCount) = 0;
    				Boolean GetRichObjectCurremtFrame(UInt32 richobjectId, [Out] Int32 %currentFrame) = 0;
    
    				void InsertImage(String^ fileName) = 0;
    			};
    		}
    	}
    }
    

      IMRichTextBoxWrapper.h

    #pragma once
    
    #include <msclrmarshal.h>
    # include <vcclr.h>
    #include "IIMRichTextBox.h"
    
    namespace Starts2000
    {
    	namespace Forms
    	{
    		namespace Control
    		{
    			using msclr::interop::marshal_as;
    
    			ref class IMRichTextBoxWrapper : public IIMRichTextBox
    			{
    			private:
    				im_richedit::IMRichEdit* _imRichEdit;
    			public:
    				IMRichTextBoxWrapper(im_richedit::IMRichEdit* imRichEdit)
    				{
    					_imRichEdit = imRichEdit;
    				}
    
    				virtual Int32 GetCharSize() sealed
    				{
    					return _imRichEdit->GetCharSize();
    				};
    
    				virtual void SetCharSize(Int32 size) sealed
    				{
    					return _imRichEdit->SetCharSize(size);
    				};
    
    				virtual String^ GetCharFace() sealed
    				{
    					BSTR bstr = _imRichEdit->GetCharFace();
    					String^ str = marshal_as<String^>(bstr);
    					delete bstr;
    					return str;
    				};// 注意, 返回的BSTR需要释放!!!
    
    				virtual void SetCharFace(String^ faceName) sealed
    				{
    					pin_ptr<const WCHAR> pFaceName = PtrToStringChars(faceName);
    					_imRichEdit->SetCharFace(pFaceName);
    				};
    
    				virtual Boolean GetCharBold() sealed
    				{
    					return _imRichEdit->GetCharBold();
    				};
    
    				virtual void SetCharBold(Boolean bold) sealed
    				{
    					_imRichEdit->SetCharBold(bold);
    				};
    
    				virtual Boolean GetCharItalic() sealed
    				{
    					return _imRichEdit->GetCharItalic();
    				};
    
    				virtual void SetCharItalic(Boolean italic) sealed
    				{
    					_imRichEdit->SetCharItalic(italic);
    				};
    
    				virtual Color GetCharColor() sealed
    				{
    					COLORREF colorRef = _imRichEdit->GetCharColor();
    					return ColorTranslator::FromWin32(colorRef);
    				};
    
    				virtual void SetCharColor(Color color) sealed
    				{
    					_imRichEdit->SetCharColor(ColorTranslator::ToWin32(color));
    				};
    
    				virtual Int32  GetSelectionCharSize() sealed
    				{
    					return _imRichEdit->GetSelectionCharSize();
    				};
    
    				virtual void SetSelectionCharSize(Int32 size) sealed
    				{
    					_imRichEdit->SetSelectionCharSize(size);
    				};
    
    				virtual String^ GetSelectionCharFace() sealed
    				{
    					BSTR bstr = _imRichEdit->GetSelectionCharFace();
    					String^ str = marshal_as<String^>(bstr);
    					delete bstr;
    					return str;
    				};
    
    				virtual void SetSelectionCharFace(String^ faceName) sealed
    				{
    					pin_ptr<const WCHAR> pFaceName = PtrToStringChars(faceName);
    					_imRichEdit->SetSelectionCharFace(pFaceName);
    				};
    
    				virtual Boolean GetSelectionCharBold() sealed
    				{
    					return _imRichEdit->GetSelectionCharBold();
    				};
    
    				virtual void SetSelectionCharBold(Boolean bold) sealed
    				{
    					_imRichEdit->SetSelectionCharBold(bold);
    				};
    
    				virtual Boolean GetSelectionCharItalic() sealed
    				{
    					return _imRichEdit->GetSelectionCharItalic();
    				};
    
    				virtual void SetSelectionCharItalic(Boolean italic) sealed
    				{
    					_imRichEdit->SetSelectionCharItalic(italic);
    				};
    
    				virtual Color GetSelectionCharColor() sealed
    				{
    					COLORREF colorRef = _imRichEdit->GetSelectionCharColor();
    					return ColorTranslator::FromWin32(colorRef);
    				};
    
    				virtual void SetSelectionCharColor(Color color) sealed
    				{
    					_imRichEdit->SetSelectionCharColor(ColorTranslator::ToWin32(color));
    				};
    
    				virtual Int32  SaveSelectionCharFormat() sealed
    				{
    					return _imRichEdit->SaveSelectionCharFormat();
    				};
    
    				virtual Boolean RestoreSelectionCharFormat(int saveState) sealed
    				{
    					return _imRichEdit->RestoreSelectionCharFormat(saveState);
    				};
    
    				virtual void SelectAll() sealed
    				{
    					_imRichEdit->SelectAll();
    				};
    
    				virtual void Cut() sealed
    				{
    					_imRichEdit->Cut();
    				};
    
    				virtual void Copy() sealed
    				{
    					_imRichEdit->Copy();
    				};
    
    				virtual void Paste() sealed
    				{
    					_imRichEdit->Paste();
    				};
    
    				virtual void ResetContent() sealed
    				{
    					_imRichEdit->ResetContent();
    				};
    
    				virtual void SetCaretToEnd() sealed
    				{
    					_imRichEdit->SetCaretToEnd();
    				};
    
    				virtual void ScrollToCaret() sealed
    				{
    					_imRichEdit->ScrollToCaret();
    				};
    
    				virtual void InsertText(String^ text) sealed
    				{
    					pin_ptr<const WCHAR> pText = PtrToStringChars(text);
    					_imRichEdit->InsertText(pText);
    				};
    
    				virtual Boolean InsertLink(String^ text) sealed
    				{
    					pin_ptr<const WCHAR> pText = PtrToStringChars(text);
    					return _imRichEdit->InsertLink(pText);
    				};
    
    				virtual void InsertBreak() sealed
    				{
    					_imRichEdit->InsertBreak();
    				};
    
    				virtual UInt32 InsertRichObject(IMRichObjectType type) sealed
    				{
    					return _imRichEdit->InsertRichObject(
    						static_cast<im_richedit::IMRichObjectType>(type));
    				};
    
    				virtual UInt32 GetRichObjectId(IntPtr oleObjectPtr) sealed
    				{
    					return _imRichEdit->GetRichObjectId(
    						reinterpret_cast<IOleObject *>(oleObjectPtr.ToPointer()));
    				};
    
    				virtual Boolean GetRichObjectType(
    					UInt32 richobjectId, [Out] IMRichObjectType %type) sealed
    				{
    					im_richedit::IMRichObjectType objType;
    					bool rel = _imRichEdit->GetRichObjectType(richobjectId, &objType);
    					type = static_cast<IMRichObjectType>(objType);
    					return rel;
    				};
    
    				// picture_filepath缓冲区大小为MAX_PATH.
    				virtual Boolean GetRichObjectPicture(
    					UInt32 richobjectId, String^ pictureFilePath) sealed
    				{
    					wchar_t *pFilePath = new wchar_t[MAX_PATH];
    					bool rel = _imRichEdit->GetRichObjectPicture(richobjectId, pFilePath);
    					pictureFilePath = marshal_as<String^>(pFilePath);
    					return rel;
    				};
    
    				virtual Boolean SetRichObjectPicture(
    					UInt32 richobjectId, String^ pictureFilePath) sealed
    				{
    					pin_ptr<const WCHAR> pFileName = PtrToStringChars(pictureFilePath);
    					return _imRichEdit->SetRichObjectPicture(richobjectId, pFileName);
    				};
    
    				// Tag含义:
    				//   IMRichObjectCustomPicture:  自定义
    				//   IMRichObjectSystemPicture:  系统编号
    				//   IMRichObjectFancyCharacter: 字符值
    				virtual Boolean GetRichObjectTag(
    					UInt32 richobjectId, [Out] IMRichObjectType %tag) sealed
    				{
    					int iTag;
    					bool rel = _imRichEdit->GetRichObjectTag(richobjectId, &iTag);
    					tag = static_cast<IMRichObjectType>(iTag);
    					return rel;
    				};
    
    				virtual Boolean SetRichObjectTag(UInt32 richobjectId, IMRichObjectType tag) sealed
    				{
    					return _imRichEdit->SetRichObjectTag(
    						richobjectId, static_cast<im_richedit::IMRichObjectType>(tag));
    				};
    
    				virtual Boolean GetRichObjectFrameCount(
    					UInt32 richobjectId, [Out] Int32 %frameCount) sealed
    				{
    					UINT uiFrameCount;
    					bool rel = _imRichEdit->GetRichObjectFrameCount(richobjectId, &uiFrameCount);
    					frameCount = uiFrameCount;
    					return rel;
    				};
    
    				virtual Boolean GetRichObjectCurremtFrame(
    					UInt32 richobjectId, [Out] Int32 %currentFrame) sealed
    				{
    					UINT uiCurrentFrame;
    					bool rel = _imRichEdit->GetRichObjectCurremtFrame(richobjectId, &uiCurrentFrame);
    					currentFrame = uiCurrentFrame;
    					return rel;
    				};
    
    				virtual void InsertImage(String^ fileName) sealed
    				{
    					ULONG id = _imRichEdit->InsertRichObject(im_richedit::IMRichObjectSystemPicture);
    					pin_ptr<const WCHAR> pFileName = PtrToStringChars(fileName);
    					_imRichEdit->SetRichObjectPicture(id, pFileName);
    				};
    			};
    		}
    	}
    }
    

    3、通过继承.NET的RichTextBox,实现IMRichTextBox

      IMRichTextBox主要通过IIMRichTextBox接口定义的IMRichTextBoxWrapper属性来使用万大侠封装的im_richedi的功能。

      IMRichTextBox.h

    #pragma once
    
    #include "im_richedit/im_richedit_sdk.h"
    #include "AutoNative.h"
    #include "IMRichEditDelegateImpl.h"
    #include "IMRichTextBoxWrapper.h"
    
    #pragma comment(lib, "im_richedit/im_richedit.lib")
    
    namespace Starts2000
    {
    	namespace Forms
    	{
    		namespace Control
    		{
    			using namespace System;
    			using namespace System::Diagnostics;
    			using namespace System::Windows::Forms;
    			using namespace System::Security::Permissions;
    
    			public ref class IMRichTextBox : public RichTextBox
    			{
    			public:
    				IMRichTextBox();
    				property IIMRichTextBox^ IMRichTextBoxWrapper
    				{
    					IIMRichTextBox^ get()
    					{
    						return _imRichTextBoxWrapper;
    					}
    				}
    			protected:
    				[SecurityPermission(SecurityAction::LinkDemand, Flags = SecurityPermissionFlag::UnmanagedCode)]
    				void WndProc(System::Windows::Forms::Message %msg) override;
    			private:
    				IMRichEditDelegateImpl* _imRichEditDelegate;
    				im_richedit::IMRichEdit* _imRichEdit;
    				IIMRichTextBox^ _imRichTextBoxWrapper;
    			};
    		}
    	}
    }
    

      IMRichTextBox.cpp

    #include "IMRichTextBox.h"
    
    namespace Starts2000
    {
    	namespace Forms
    	{
    		namespace Control
    		{
    			IMRichTextBox::IMRichTextBox() : RichTextBox()
    			{
    				RichTextBox::HideSelection = false;
    			}
    
    			void IMRichTextBox::WndProc(Message %msg)
    			{
    				if (msg.Msg > 2)
    				{
    					__super::WndProc(msg);
    					return;
    				}
    
    				HWND richEditHwnd = NULL;
    				LPRICHEDITOLE lpRichEditOle = NULL;
    
    				switch (msg.Msg)
    				{
    				case WM_CREATE:
    					__super::WndProc(msg);
    					richEditHwnd = reinterpret_cast<HWND>(Handle.ToPointer());
    					::SendMessage(richEditHwnd, EM_GETOLEINTERFACE, 0, reinterpret_cast<LPARAM>(&lpRichEditOle));
    #ifdef _DEBUG
    					Debug::Assert(lpRichEditOle != NULL);
    #endif
    
    					_imRichEditDelegate = new IMRichEditDelegateImpl();
    					_imRichEdit = ::CreateIMRichEdit(lpRichEditOle, _imRichEditDelegate);
    					_imRichTextBoxWrapper = gcnew Starts2000::Forms::Control::IMRichTextBoxWrapper(_imRichEdit);
    					break;
    				case WM_DESTROY:
    					if (_imRichEdit)
    					{
    						_imRichEdit->DeleteThis();
    						_imRichEdit = NULL;
    					}
    
    					if (_imRichEditDelegate != NULL)
    					{
    						delete _imRichEditDelegate;
    						_imRichEditDelegate = NULL;
    					}
    					__super::WndProc(msg);
    					break;
    				default:
    					__super::WndProc(msg);
    					break;
    				}
    			}
    		}
    	}
    }
    

    四、示例及效果

      IMRichTextBox不能通过工具箱直接拖到窗体设计器上,只能手动添加代码。    

    using System;
    using System.Drawing;
    using System.IO;
    using System.Windows.Forms;
    using Starts2000.Forms.Control;
    
    namespace Starts2000.RichEditDemo
    {
        public partial class FormMain : Form
        {
            IMRichTextBox _imRichTextBox;
    
            public FormMain()
            {
                InitializeComponent();
                _imRichTextBox = new IMRichTextBox();
                _imRichTextBox.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom;
                _imRichTextBox.Location = new Point(3, 3);
                _imRichTextBox.Width = ClientSize.Width - 6;
                _imRichTextBox.Height = ClientSize.Height - 40;
                Controls.Add(_imRichTextBox);
            }
    
            private void btnInsertImage_Click(object sender, EventArgs e)
            {
                OpenFileDialog dialog = new OpenFileDialog();
                dialog.DefaultExt = "gif";
                dialog.Filter = "图片文件|*.jpg;*.gif;*.bmp";
                dialog.Multiselect = true;
                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    foreach (var imgFile in dialog.FileNames)
                    {
                        _imRichTextBox.IMRichTextBoxWrapper.InsertImage(imgFile);
                    }
                }
                _imRichTextBox.IMRichTextBoxWrapper.ScrollToCaret();
            }
    
            private void btnInserText_Click(object sender, EventArgs e)
            {
                var wrapper = _imRichTextBox.IMRichTextBoxWrapper;
                var path = Application.StartupPath;
    
                wrapper.SaveSelectionCharFormat();
                wrapper.SetSelectionCharColor(Color.FromArgb(0, 102, 0));
                wrapper.InsertText("Starts2000 " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                wrapper.RestoreSelectionCharFormat(-1);
                wrapper.InsertBreak();
                wrapper.InsertImage(Path.Combine(path, @"Emotion2.gif"));
                wrapper.SaveSelectionCharFormat();
                wrapper.SetSelectionCharColor(Color.Red);
                wrapper.InsertText("Hello, IMRichTextBox!");
                wrapper.RestoreSelectionCharFormat(-1);
                wrapper.InsertImage(Path.Combine(path, @"Emotion18.gif"));
                wrapper.InsertLink("博客园");
                wrapper.InsertBreak();
                wrapper.ScrollToCaret();
            }
        }
    }
    

      效果:

    五、总结

      1、C++/CLI在封装现有C++项目供.NET使用还是非常给力的。

      2、万大侠的im_richedit还提供了WindowLess的richedit的封装,由于我没有使用,所以没有进行封装,如果有需要,大家可自行封装。

      3、项目使用VS2013进行开发、编译和调试,不保证其他版本VS下能正常编译,项目源码下载:IMRichTextBox

  • 相关阅读:
    智慧养老民政监管平台建设方案
    CF600E Lomsat gelral dsu on tree
    dsu on tree详解
    【Spring 从0开始】Spring5 新功能,整合日志框架 Log4j2
    【Spring 从0开始】JdbcTemplate 数据库事务管理
    【Spring 从0开始】JdbcTemplate 数据库事务参数
    【Spring 从0开始】JdbcTemplate 数据库事务管理
    【Spring 从0开始】JdbcTemplate 操作数据库
    【Spring 从0开始】AOP 操作
    【Spring 从0开始】AOP 操作中的相关术语、环境准备
  • 原文地址:https://www.cnblogs.com/Starts_2000/p/4815915.html
Copyright © 2011-2022 走看看