再也不須要ImageOle或DynamicGifCtl,.NET實現IM編輯控件

  多年前寫過一篇文章《C# 實現IM聊天信息輸入顯示控件(1)-顯示GIF動畫圖片》,主要是使用ActiveX控件實現RichTextBox插入gif動畫圖片,包括使用QQ的ImageOle和飛信使用的DynamicGifCtl,這2種方式都須要先註冊ActiveX。後續發現QQ新版本沒有再使用ImageOle,最近恰好有這方面的需求,因而經過萬能的谷歌,找到了相關的資料,不敢獨享,因而就有了這篇文章。html

1、致謝

  感謝大神萬大俠,沒有他的系列文章和介紹,我也不可能寫下這篇文章。我只是在他提供的控件基礎上進行簡單的封裝,以便用於.NET。萬大俠的系列文章地址:致力於richedit應用於IM解決方案app

2、im_richedit簡介

  萬大俠的im_richedit提供了2個抽象類和一個函數供實現一個IMRichTextBox,它們分別是:ide

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

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

  三、CreateIMRichEdit函數

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

3、.NET IMRichTextBox實現

  主要參考萬大俠提供的示例,使用C++/CLI對im_richedit進行封裝。函數

一、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)
			{
			}
		}
	}
}

二、定義IIMRichTextBox接口,並使用萬大俠提供的IMRichEdit抽象類進行實現。

  IIMRichTextBox.hui

#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.hspa

#pragma once

#include <msclr\marshal.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);
				};
			};
		}
	}
}

三、經過繼承.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;
				}
			}
		}
	}
}

4、示例及效果

  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, @"Emotion\2.gif"));
            wrapper.SaveSelectionCharFormat();
            wrapper.SetSelectionCharColor(Color.Red);
            wrapper.InsertText("Hello, IMRichTextBox!");
            wrapper.RestoreSelectionCharFormat(-1);
            wrapper.InsertImage(Path.Combine(path, @"Emotion\18.gif"));
            wrapper.InsertLink("博客園");
            wrapper.InsertBreak();
            wrapper.ScrollToCaret();
        }
    }
}

  效果:

5、總結

  一、C++/CLI在封裝現有C++項目供.NET使用仍是很是給力的。

  二、萬大俠的im_richedit還提供了WindowLess的richedit的封裝,因爲我沒有使用,因此沒有進行封裝,若是有須要,你們可自行封裝。

  三、項目使用VS2013進行開發、編譯和調試,不保證其餘版本VS下能正常編譯,項目源碼下載:IMRichTextBox

相關文章
相關標籤/搜索