当前位置:   article > 正文

基于富文本框的全功能编辑器_c#富文本框实现文本编辑器

c#富文本框实现文本编辑器

目录

介绍

使用代码

兴趣点


 

介绍

NET Framework中的RichTextBox控件(RTB)提供了一种以所见即所得方式编辑richtext用于RTF文件和较旧的MS-Word文档以及简单文本的标记代码的简单方法。但是,在其基本形式中,它缺乏更完整的编辑器的功能,例如查找和替换、文档打印、页面布局以及文件和图像的拖放。

使用代码

这个项目最初是为了将基RichTextBox类变成一个功能更全的编辑器,它可以作为我正在开发的其他应用程序的可重用组件。尽管RTB有许多有用的属性和方法,但它并不是一个基本形式的全功能编辑器。在添加我认为必要的改进的过程中,以下是我遇到的主要问题,以及来自网络上许多不同来源的解决方案:

1、打印——没有打印RTB内容的内置方法。我创建了一个带有GeneralPrintForm构造函数的PrintHelper类,可在此处作为单独的提示和技巧发布:A Simple RTF Print Form - CodeProject。请注意,网络PrintDialog也提供了一个显示文档将如何打印的PrintPreview窗口。该PrintHelper类使用默认的打印和预览对话框。

2、页面布局和边距——表单上RTB外观的一个相对简单的问题是其内容没有可视边距。解决此问题的简单方法是将RTB包含在稍大的Panel容器中,并带有FixedSingle BorderStyle,因此控件中的滚动文本与面板的周围边缘之间存在明显的边距。请注意,这仅模拟打印页面的边距。要为打印页面设置实际的边距、页眉和页脚,使用上面第1项中的PrintHelper类中的GeneralPrintForm,填充一个System.Drawing.Printing.PageSettings对象并通过其构造函数发送到GeneralPrintForm

  1. // PRINT MENU OPTION
  2. private void printToolStripMenuItem_Click(object sender, EventArgs e)
  3. {
  4. string finalheader = HeaderString;
  5. Exception ex = new Exception("An Error occurred while Printing");
  6. System.Drawing.Printing.PageSettings PSS = new System.Drawing.Printing.PageSettings();
  7. PSS.Margins.Left = (int)(LeftMargin * 100);
  8. PSS.Margins.Right = (int)(RightMargin * 100);
  9. PSS.Margins.Top = (int)(TopMargin * 100);
  10. PSS.Margins.Bottom = (int)(BottomMargin * 100);
  11. PSS.Landscape = LandScapeModeOn;
  12. if (HeaderOn)
  13. {
  14. if(AddFileNameToHeader)
  15. {
  16. finalheader += " " + FileName;
  17. }
  18. if(AddDateToHeader)
  19. {
  20. finalheader += " Printed: " + System.DateTime.Today.ToShortDateString() +
  21. " " + System.DateTime.Now.ToShortTimeString();
  22. }
  23. }
  24. rtt.GeneralPrintForm("Print Document", rtbMainForm1.Rtf,
  25. ref ex, PSS, HeaderOn, finalheader, HeaderFont, HeaderNumberPages);
  26. }

我创建了一个简单的表单来从编辑器中的用户那里收集这些设置:

  1. public partial class marginsForm : Form
  2. {
  3. public marginsForm()
  4. {
  5. InitializeComponent();
  6. }
  7. // OVERRIDE 1
  8. public marginsForm(float Top,float Bottom, float Left, float Right,bool landscapemodeon)
  9. {
  10. InitializeComponent();
  11. top = Top;
  12. bottom = Bottom;
  13. left = Left;
  14. right = Right;
  15. landscapemode = landscapemodeon;
  16. }
  17. // OVERRIDE 2
  18. public marginsForm(float Top, float Bottom, float Left, float Right,
  19. bool landscapemodeon,string headerstring,Font headerfont,bool headeron,bool numberpages,
  20. bool adddatetoheader, bool addfilenametoheader)
  21. {
  22. InitializeComponent();
  23. top = Top;
  24. bottom = Bottom;
  25. left = Left;
  26. right = Right;
  27. landscapemode = landscapemodeon;
  28. this.headerfont = headerfont;
  29. this.headeron = headeron;
  30. this.pagenumberson = numberpages;
  31. this.headerstring = headerstring;
  32. this.adddatetoheader = adddatetoheader;
  33. this.addfilenametoheader = addfilenametoheader;
  34. }
  35. // PUBLIC ACCESSORS
  36. public bool ResultOk
  37. {
  38. get
  39. {
  40. return resultok;
  41. }
  42. }
  43. public float Top
  44. {
  45. get
  46. {
  47. return top;
  48. }
  49. set
  50. {
  51. top = value;
  52. }
  53. }
  54. public float Bottom
  55. {
  56. get
  57. {
  58. return bottom;
  59. }
  60. set
  61. {
  62. bottom = value;
  63. }
  64. }
  65. public float Left
  66. {
  67. get
  68. {
  69. return left;
  70. }
  71. set
  72. {
  73. left = value;
  74. }
  75. }
  76. public float Right
  77. {
  78. get
  79. {
  80. return right;
  81. }
  82. set
  83. {
  84. right = value;
  85. }
  86. }
  87. public bool LandscapeMode
  88. {
  89. get
  90. {
  91. return landscapemode;
  92. }
  93. set
  94. {
  95. landscapemode = value;
  96. }
  97. }
  98. public Font HeaderFont
  99. {
  100. get
  101. {
  102. return headerfont;
  103. }
  104. }
  105. public bool HeaderOn
  106. {
  107. get
  108. {
  109. return headeron;
  110. }
  111. set
  112. {
  113. headeron = value;
  114. }
  115. }
  116. public bool PageNumbersOn
  117. {
  118. get
  119. {
  120. return pagenumberson;
  121. }
  122. set
  123. {
  124. pagenumberson = value;
  125. }
  126. }
  127. public string DocumentFilename
  128. {
  129. get
  130. {
  131. return documentfilename;
  132. }
  133. set
  134. {
  135. documentfilename = value;
  136. }
  137. }
  138. public string HeaderString
  139. {
  140. get
  141. {
  142. return headerstring;
  143. }
  144. set
  145. {
  146. headerstring = value;
  147. }
  148. }
  149. public bool AddDateToHeader
  150. {
  151. get
  152. {
  153. return adddatetoheader;
  154. }
  155. set
  156. {
  157. adddatetoheader = value;
  158. }
  159. }
  160. public bool AddFileNameToHeader
  161. {
  162. get
  163. {
  164. return addfilenametoheader;
  165. }
  166. set
  167. {
  168. addfilenametoheader = value;
  169. }
  170. }
  171. // PRIVATE VARIABLES
  172. private float top, bottom, left, right = 0.0f;
  173. private bool landscapemode = false;
  174. private Font headerfont = new Font("Arial", 10); // DEFAULT
  175. private bool headeron = false;
  176. private bool pagenumberson = false;
  177. private bool adddatetoheader = false;
  178. private bool addfilenametoheader = false;
  179. private string documentfilename = string.Empty;
  180. private string headerstring = string.Empty;
  181. ExceptionHandlerTools eht = new ExceptionHandlerTools();
  182. // OK Button
  183. private void button1_Click(object sender, EventArgs e)
  184. {
  185. try
  186. {
  187. top = (float)Convert.ToDouble(tbTop.Text);
  188. bottom = (float)Convert.ToDouble(tbBottom.Text);
  189. left = (float)Convert.ToDouble(tbLeft.Text);
  190. right = (float)Convert.ToDouble(tbRight.Text);
  191. }
  192. catch
  193. {
  194. eht.GeneralExceptionHandler("Enter Margins as 3 digit decimals",
  195. "(60) Set Margins", false, null);
  196. return;
  197. }
  198. if ((top == 0 || bottom == 0 || left == 0 || right == 0) && headeron)
  199. {
  200. Exception ex = new Exception("(Application) - You must set the margins
  201. for the header to print correctly\r\n"+
  202. "Either set the margins to values greater
  203. than 0 or turn the header option off");
  204. eht.GeneralExceptionHandler("Header will not print if margins are set to 0",
  205. "Page Layout", false, ex);
  206. return;
  207. }
  208. if (rbMarginsFormLandscape.Checked)
  209. {
  210. landscapemode = true;
  211. }
  212. else
  213. {
  214. landscapemode = false;
  215. }
  216. if (headeron)
  217. {
  218. headerstring = tbHeaderText.Text;
  219. }
  220. resultok = true;
  221. this.Close();
  222. }
  223. // SET MARGINS TO 0
  224. private void button3_Click(object sender, EventArgs e)
  225. {
  226. tbTop.Text = "0.0";
  227. tbBottom.Text = "0.0";
  228. tbRight.Text = "0.0";
  229. tbLeft.Text = "0.0";
  230. }
  231. // SET MARGINS TO 1" ALL AROUND
  232. private void btnOneInch_Click(object sender, EventArgs e)
  233. {
  234. tbTop.Text = "1.0";
  235. tbBottom.Text = "1.0";
  236. tbRight.Text = "1.0";
  237. tbLeft.Text = "1.0";
  238. }
  239. // HEADER ON BUTTON CHECK CHANGED
  240. private void rbHeaderOn_CheckedChanged(object sender, EventArgs e)
  241. {
  242. if (rbHeaderOn.Checked)
  243. {
  244. headeron = true;
  245. }
  246. else
  247. {
  248. headeron = false;
  249. }
  250. }
  251. // HEADER TEXT BOX HANDLER
  252. private void tbHeaderText_TextChanged(object sender, EventArgs e)
  253. {
  254. headerstring = tbHeaderText.Text;
  255. }
  256. // SELECT FONT BUTTON
  257. private void button4_Click(object sender, EventArgs e)
  258. {
  259. DialogResult result;
  260. try
  261. {
  262. fontDialog1.Font = headerfont;
  263. result = fontDialog1.ShowDialog();
  264. }
  265. catch (Exception ex)
  266. {
  267. eht.GeneralExceptionHandler("Invalid Font Selection",
  268. "(01) Change Font", false, ex);
  269. return;
  270. }
  271. if (result == DialogResult.OK)
  272. {
  273. headerfont = fontDialog1.Font;
  274. tbHeaderFont.Text = headerfont.FontFamily.Name.ToString() +
  275. " " + headerfont.SizeInPoints.ToString() + " " + headerfont.Style.ToString();
  276. };
  277. }
  278. private void label3_Click(object sender, EventArgs e)
  279. {
  280. }
  281. // PAGE NUMBER BUTTON CHECK CHANGED HANDLER
  282. private void cbPageNumbersOn_CheckedChanged(object sender, EventArgs e)
  283. {
  284. if (cbPageNumbersOn.Checked)
  285. {
  286. pagenumberson = true;
  287. }
  288. else
  289. {
  290. pagenumberson = false;
  291. }
  292. }
  293. // DATE TIME CHECKBOX HANDLER
  294. private void cbAddDateToHeader_CheckedChanged(object sender, EventArgs e)
  295. {
  296. if (cbAddDateToHeader.Checked)
  297. {
  298. adddatetoheader = true;
  299. }
  300. else
  301. {
  302. adddatetoheader = false;
  303. }
  304. }
  305. // ADD FILENAME CHECKBOX HANDLER
  306. private void cbAddFileName_CheckedChanged(object sender, EventArgs e)
  307. {
  308. if (cbAddFileName.Checked)
  309. {
  310. addfilenametoheader = true;
  311. }
  312. else
  313. {
  314. addfilenametoheader = false;
  315. }
  316. }
  317. // CANCEL BUTTON
  318. private void button2_Click(object sender, EventArgs e)
  319. {
  320. resultok = false;
  321. this.Close();
  322. }
  323. private bool resultok = false;
  324. // FORM LOAD
  325. private void marginsForm_Load(object sender, EventArgs e)
  326. {
  327. tbTop.Text = top.ToString("F1");
  328. tbBottom.Text = bottom.ToString("F1");
  329. tbLeft.Text = left.ToString("F1");
  330. tbRight.Text = right.ToString("F1");
  331. if (headerstring != string.Empty)
  332. {
  333. tbHeaderText.Text = headerstring;
  334. }
  335. if (headeron)
  336. {
  337. rbHeaderOn.Checked = true;
  338. rbHeaderOff.Checked = false;
  339. }
  340. else
  341. {
  342. rbHeaderOn.Checked = false;
  343. rbHeaderOff.Checked = true;
  344. }
  345. if (pagenumberson)
  346. {
  347. cbPageNumbersOn.Checked = true;
  348. }
  349. else
  350. {
  351. cbPageNumbersOn.Checked = false;
  352. }
  353. if (adddatetoheader)
  354. {
  355. cbAddDateToHeader.Checked = true;
  356. }
  357. else
  358. {
  359. cbAddDateToHeader.Checked = false;
  360. }
  361. if (addfilenametoheader)
  362. {
  363. cbAddFileName.Checked = true;
  364. }
  365. else
  366. {
  367. cbAddFileName.Checked = false;
  368. }
  369. if (landscapemode)
  370. {
  371. rbMarginsFormLandscape.Checked = true;
  372. }
  373. else
  374. {
  375. rbMarginsFormPortrait.Checked = true;
  376. }
  377. tbHeaderFont.Text = headerfont.FontFamily.Name.ToString() +
  378. " " + headerfont.SizeInPoints.ToString() + " " +
  379. headerfont.Style.ToString();
  380. }
  381. }
  382. }

 3、Find & Replace——这个重要的编辑器功能不是由基本的RTB实现的。可以通过创建一个单独的表单来添加它,以处理要搜索和/或替换的文本输入,并使用查找、查找全部、替换、全部替换和匹配大小写复选框的常用功能按钮。由于在主RTB之上运行,因此它使用委托来控制搜索功能:在表单应用程序的Program.cs中声明委托原型,如果构建DLL ,则在Class.cs中声明:

  1. // DELEGATES
  2. public delegate int Rep(string search, string replace,
  3. bool match, int startpos, int function); // used for search call-back

然后,将执行任务的实际方法添加到包含以下RichTextBox内容的表单中:

  1. // REPLACE DELEGATE FUNCTION
  2. public int ReplaceDelegateMethod(string search, string replace,
  3. bool match, int startpos, int function)
  4. {
  5. const int FIND = 1;
  6. const int FINDNEXT = 2;
  7. const int REPLACE = 3;
  8. const int REPLACEALL = 4;
  9. /* DEBUGMessageBox.Show("Search = "+search+"
  10. Replace = "+replace+" Match = "+match.ToString()
  11. , "Delegate Test", MessageBoxButtons.OK,
  12. MessageBoxIcon.Information);*/
  13. int currentposition = startpos;
  14. int stopposition = this.rtbMainForm1.Text.Length - 1; /* text or rtf? */
  15. switch (function)
  16. {
  17. case FIND:
  18. {
  19. this.rtbMainForm1.Find(search);
  20. return (this.rtbMainForm1.SelectionStart);
  21. }
  22. case FINDNEXT:
  23. {
  24. if (search.Length == 0) // ERROR HANDLER EMPTY SEARCH FIELD
  25. {
  26. GeneralExceptionForm g = new GeneralExceptionForm("Find Text",
  27. "Find Field is Empty", "Error(01) - Replace Dialog", false, null);
  28. g.ShowDialog();
  29. g.Dispose();
  30. return currentposition;
  31. }
  32. if (startpos < (stopposition)) // changed from stopposition-search.length
  33. {
  34. int searchresult = 0;
  35. /*this.rtbMainForm1.SelectionStart = currentposition;*/
  36. if (!match)
  37. {
  38. searchresult = this.rtbMainForm1.Find(search,
  39. currentposition, stopposition, RichTextBoxFinds.None);
  40. }
  41. else // MATCH CASE
  42. {
  43. searchresult = this.rtbMainForm1.Find(search,
  44. currentposition, stopposition, RichTextBoxFinds.MatchCase);
  45. }
  46. if (searchresult > 0)
  47. {
  48. return searchresult;
  49. }
  50. else
  51. {
  52. return 0;
  53. }
  54. }
  55. return 0;
  56. }
  57. case REPLACE:
  58. {
  59. if (replace.Length == 0) // ERROR HANDLER EMPTY REPLACE FIELD
  60. {
  61. GeneralExceptionForm g = new GeneralExceptionForm("Replace Text",
  62. "Replace Field is Empty", "Error(02) - Replace Dialog", false, null);
  63. g.ShowDialog();
  64. g.Dispose();
  65. return currentposition;
  66. }
  67. if (this.rtbMainForm1.SelectedText.Length > 0) // SKIP IF NONE SELECTED
  68. {
  69. this.rtbMainForm1.SelectedText = replace;
  70. }
  71. return currentposition;
  72. }
  73. case REPLACEALL:
  74. {
  75. if (search.Length == 0 || replace.Length == 0) // ERROR HANDLER EMPTY
  76. // SEARCH FIELD
  77. {
  78. GeneralExceptionForm g = new GeneralExceptionForm("Replace All",
  79. "Field(s) empty", "Error(03) - Replace Dialog", false, null);
  80. g.ShowDialog();
  81. g.Dispose();
  82. return 0;
  83. }
  84. int searchresult = 1;
  85. int count = 0;
  86. while ((currentposition < stopposition) &&
  87. searchresult >= 0) // changed from
  88. // stopposition-search.length
  89. {
  90. if (!match)
  91. {
  92. searchresult = this.rtbMainForm1.Find
  93. (search, currentposition, stopposition, RichTextBoxFinds.None);
  94. }
  95. else // MATCH CASE
  96. {
  97. searchresult = this.rtbMainForm1.Find
  98. (search, currentposition, stopposition, RichTextBoxFinds.MatchCase);
  99. }
  100. if (this.rtbMainForm1.SelectedText.Length > 0)
  101. {
  102. this.rtbMainForm1.SelectedText = replace;
  103. count++;
  104. currentposition = searchresult + replace.Length;
  105. }
  106. }
  107. dlt.NotifyDialog(this, "Replaced " +
  108. count.ToString() + " items.",displaytime);
  109. return 1;
  110. }
  111. default:
  112. {
  113. return 0;
  114. }
  115. }
  116. }

最后,从搜索和替换表单中调用delegate

  1. public partial class ReplaceForm : Form
  2. {
  3. public ReplaceForm()
  4. {
  5. InitializeComponent();
  6. }
  7. // Overload with delegate - prototype def in program.cs
  8. // Callback Delegate for EditForm to initiate search/replace code
  9. public ReplaceForm(Rep r)
  10. {
  11. InitializeComponent();
  12. ReplaceDelegate = r; // transer a copy of the delegate to local object
  13. }
  14. public ReplaceForm(Rep r,Scr d)
  15. {
  16. InitializeComponent();
  17. ReplaceDelegate = r;
  18. ScrollDelegate = d;
  19. }
  20. public string searchstring
  21. {
  22. get
  23. {
  24. return SearchString;
  25. }
  26. set
  27. {
  28. SearchString = value;
  29. }
  30. }
  31. public string replacestring
  32. {
  33. get
  34. {
  35. return ReplaceString;
  36. }
  37. set
  38. {
  39. ReplaceString = value;
  40. }
  41. }
  42. public bool matchcase
  43. {
  44. get
  45. {
  46. return MatchCase;
  47. }
  48. set
  49. {
  50. MatchCase = value;
  51. }
  52. }
  53. private Rep ReplaceDelegate; // a private copy of the delegate in the constructor
  54. private Scr ScrollDelegate;
  55. private void btnReplaceFormCancel_Click(object sender, EventArgs e)
  56. {
  57. this.Close();
  58. }
  59. private string SearchString = String.Empty;
  60. private string ReplaceString = String.Empty;
  61. private bool MatchCase = false;
  62. private int position = 0; // CHANGED FROM 1, 01-24-2013 missed 1st word
  63. private const int FINDNEXT = 2;
  64. private const int REPLACE = 3;
  65. private const int REPLACEALL = 4;
  66. private bool foundnext = false;
  67. private void ReplaceForm_Load(object sender, EventArgs e)
  68. {
  69. if (SearchString != String.Empty)
  70. {
  71. tbFindWhat.Text = SearchString;
  72. }
  73. if (ReplaceString != String.Empty)
  74. {
  75. tbReplaceWith.Text = ReplaceString;
  76. }
  77. cbMatchCase.Checked = MatchCase;
  78. }
  79. private void btnFindNext_Click(object sender, EventArgs e)
  80. {
  81. int placeholder=0;
  82. SearchString = this.tbFindWhat.Text;
  83. placeholder = ReplaceDelegate
  84. (SearchString, ReplaceString, MatchCase, position, FINDNEXT);
  85. ScrollDelegate();
  86. lblposition.Text = placeholder.ToString() + " " + SearchString;
  87. if (placeholder != 0)
  88. {
  89. position = placeholder+ SearchString.Length;
  90. foundnext = true;
  91. }
  92. else
  93. {
  94. position = 0;
  95. foundnext = false;
  96. MessageBox.Show("Finished searching through document.",
  97. "Search Complete", MessageBoxButtons.OK,
  98. MessageBoxIcon.Information);
  99. this.Close();
  100. }
  101. }
  102. private void tbFindWhat_TextChanged(object sender, EventArgs e)
  103. {
  104. SearchString = tbFindWhat.Text;
  105. }
  106. private void tbReplaceWith_TextChanged(object sender, EventArgs e)
  107. {
  108. ReplaceString = tbReplaceWith.Text;
  109. }
  110. private void cbMatchCase_CheckedChanged(object sender, EventArgs e)
  111. {
  112. MatchCase = cbMatchCase.Checked;
  113. }
  114. private void btnReplace_Click(object sender, EventArgs e)
  115. {
  116. if (!foundnext)
  117. {
  118. btnFindNext_Click(sender, e);
  119. return; // find next word first
  120. }
  121. int placeholder = 0;
  122. SearchString = this.tbFindWhat.Text;
  123. placeholder = ReplaceDelegate
  124. (SearchString, ReplaceString, MatchCase, position, REPLACE);
  125. lblposition.Text = placeholder.ToString() + " " + SearchString;
  126. if (placeholder != 0)
  127. {
  128. position = placeholder + SearchString.Length;
  129. foundnext = false;
  130. }
  131. else
  132. {
  133. position = 0;
  134. MessageBox.Show("Finished searching through document.",
  135. "Search Complete", MessageBoxButtons.OK,
  136. MessageBoxIcon.Information);
  137. this.Close();
  138. }
  139. }
  140. private void btnReplaceAll_Click(object sender, EventArgs e)
  141. {
  142. if (ReplaceDelegate(SearchString, ReplaceString, MatchCase, 1, REPLACEALL) == 1)
  143. {
  144. this.Close(); // RETURNS 1 if successful, 0 if field(s) are missing
  145. }
  146. }
  147. // SHORTCUTS FOR REPLACE FORM - You can add custom shortcuts here if desired
  148. private void ReplaceForm_KeyPress(object sender, KeyPressEventArgs e)
  149. {
  150. const int CTRLR = 18; // NOTE CTRL: R = 18, L =12, D = 4
  151. const int CTRLL = 12;
  152. const int CTRLD = 4;
  153. const int CTRLA = 1;
  154. if (System.Windows.Forms.Control.ModifierKeys.ToString() == "Control")
  155. {
  156. int result = e.KeyChar;
  157. switch (result) {
  158. case CTRLR:
  159. this.tbFindWhat.Text = "";
  160. this.tbReplaceWith.Text = "right";
  161. break;
  162. case CTRLL:
  163. this.tbFindWhat.Text = "";
  164. this.tbReplaceWith.Text = "left";
  165. break;
  166. case CTRLD:
  167. this.tbFindWhat.Text = "";
  168. this.tbReplaceWith.Text = System.DateTime.Today.ToShortDateString();
  169. break;
  170. case CTRLA:
  171. this.tbFindWhat.Text = "*";
  172. break;
  173. default:
  174. break;
  175. }
  176. }
  177. }
  178. //ENTER KEY EVENT HANDLERS
  179. private void tbFindWhat_KeyPress(object sender, KeyPressEventArgs e)
  180. {
  181. if (e.KeyChar == (char)Keys.Enter)
  182. {
  183. e.KeyChar = (char)Keys.Tab;
  184. e.Handled = true;
  185. SendKeys.Send(e.KeyChar.ToString());
  186. }
  187. }
  188. private void tbReplaceWith_KeyPress(object sender, KeyPressEventArgs e)
  189. {
  190. if (e.KeyChar == (char)Keys.Enter)
  191. {
  192. e.KeyChar = (char)Keys.Tab;
  193. e.Handled = true;
  194. SendKeys.Send(e.KeyChar.ToString());
  195. }
  196. }
  197. private void cbMatchCase_KeyPress(object sender, KeyPressEventArgs e)
  198. {
  199. if (e.KeyChar == (char)Keys.Enter)
  200. {
  201. e.KeyChar = (char)Keys.Tab;
  202. e.Handled = true;
  203. SendKeys.Send(e.KeyChar.ToString());
  204. }
  205. }
  206. }
  207. }

4、搜索和替换期间的正确滚动:您会注意到上面代码中的另一个delegate ScrollDelegate()。这会将通过搜索和替换找到的选定文本滚动到RTB窗口的中间,而不管窗口大小。否则,选择始终位于窗口底部。原型是:

public delegate void Scr(); // scroll delegate call-back

和方法:

  1. // SCROLL DELEGATE FUNCTION - SCROLL SELECTION UP INTO MIDDLE OF WINDOW
  2. // Usage: Scrolls selected text to middle line of current window regardless of size
  3. // Ver: 11-20-2016
  4. // Credit: http://stackoverflow.com/questions/205794/
  5. // how-to-move-scroll-bar-up-by-one-line-in-c-sharp-richtextbox
  6. public void ScrollDownMethod()
  7. {
  8. int topline = rtbMainForm1.GetLineFromCharIndex
  9. (rtbMainForm1.GetCharIndexFromPosition(new Point(0, 0)));
  10. int bottomline = rtbMainForm1.GetLineFromCharIndex
  11. (rtbMainForm1.GetCharIndexFromPosition(new Point(rtbMainForm1.ClientSize.Width,
  12. rtbMainForm1.ClientSize.Height)));
  13. int currentline = rtbMainForm1.GetLineFromCharIndex
  14. (rtbMainForm1.GetFirstCharIndexOfCurrentLine());
  15. int middleline = topline + ((bottomline - topline) / 2);
  16. int linestoscroll = currentline - middleline;
  17. SendMessage(rtbMainForm1.Handle, (uint)0x00B6, (UIntPtr)0, (IntPtr)(linestoscroll));
  18. return;
  19. }

您还需要Windows中的此功能:

  1. [DllImport("user32.dll")]
  2. static extern int SendMessage(IntPtr hWnd, uint wMsg, UIntPtr wParam, IntPtr lParam);

5、在编辑器窗口中显示CapsLock和插入键状态:很高兴以与其他编辑器相同的方式显示这些键的状态。我找到的解决方案(下面引用)覆盖了基本的AppIdle事件处理程序并实时跟踪键状态,更新包含RichTextBox的表单上的两个小标签。 请注意,当程序(主窗体)关闭时,您必须删除自定义处理程序:

  1. // Base Constructor
  2. public EditForm()
  3. {
  4. InitializeComponent();
  5. DoubleBuffered = true;
  6. Application.Idle += App_Idle;
  7. }
  8. // Custom Application.Idle Event Handler
  9. // CREDIT: http://stackoverflow.com/questions/577411/
  10. // how-can-i-find-the-state-of-numlock-capslock-and-scrolllock-in-net
  11. void App_Idle(object sender, EventArgs e)
  12. {
  13. if (System.Windows.Forms.Control.IsKeyLocked(Keys.CapsLock))
  14. {
  15. lblCapsOn.Visible = true;
  16. }
  17. else
  18. {
  19. lblCapsOn.Visible = false;
  20. }
  21. if ((GetKeyState(KEY_INSERT) & 1) > 0)
  22. {
  23. lblOverStrike.Text = "OVR";
  24. }
  25. else
  26. {
  27. lblOverStrike.Text = "INS";
  28. }
  29. }
  30. // FOR READING STATE OF INSERT OR CAPS LOCK KEY
  31. [DllImport("user32.dll")]
  32. private static extern short GetKeyState(int KeyCode);
  33. private const int KEY_INSERT = 0X2D;
  34. // Must Remove Override on Closing
  35. protected override void OnFormClosed(FormClosedEventArgs e)
  36. {
  37. Application.Idle -= App_Idle;
  38. base.OnFormClosed(e);
  39. }

6、Windows 10中的光标闪烁问题:在某些设置中,Windows处理光标的方式会导致它在RTB中编辑时在I-Beam和箭头之间闪烁,尤其是在具有某些显示设置的Windows 10中。我在从Windows 7升级后使用编辑器时遇到了这个问题,但我找到了这个解决方案(最初在Visual Basic中),它消除了这个问题,虽然副作用是光标现在固定为箭头,这意味着例如当指向一个网址时,它不会变成一只手:

  1. // Prevent Flickering Cursor problem in Windows 10
  2. //CREDIT: http://www.vbforums.com/showthread.php?
  3. // 833547-RESOLVED-Cursor-flicker-RichTextBox-on-Windows-10-Bug
  4. protected override void WndProc(ref Message m)
  5. {
  6. const int WM_SETCURSOR = 0x20;
  7. base.WndProc(ref m);
  8. if (m.Msg == WM_SETCURSOR)
  9. {
  10. m.Result = (IntPtr)1;
  11. }
  12. }

7、添加拖放功能:虽然RTB支持拖放事件,但必须添加事件处理程序才能使这些工作。我使用了以下通用类:

  1. // DRAG AND DROP HANDLERS
  2. // 1st Step in Drag Drop
  3. //
  4. // Attach to Drag Enter Event
  5. //
  6. //
  7. //
  8. public void GenericDragEnterEventHandler
  9. (object sender, System.Windows.Forms.DragEventArgs e)
  10. {
  11. if (e.Data.GetDataPresent(DataFormats.FileDrop))
  12. {
  13. e.Effect = DragDropEffects.Move;
  14. }
  15. else
  16. {
  17. e.Effect = DragDropEffects.None;
  18. }
  19. }
  20. // 2nd Step in Drag Drop
  21. //
  22. // Returns String from Drag & Drop
  23. //
  24. //
  25. //
  26. //
  27. public string[] GenericDragDropEventHandler
  28. (object sender, System.Windows.Forms.DragEventArgs e)
  29. {
  30. if (e.Data.GetDataPresent(DataFormats.FileDrop))
  31. {
  32. string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
  33. return files;
  34. }
  35. else
  36. {
  37. return null;
  38. }
  39. }

然后,您可以将每个新实例添加到RTB的事件处理程序中,以便用户可以将文件拖到窗口中并打开该文件,或者通过将文件拖到RTB来将其插入到文档中并生成图像。根据要删除的文件类型,调用不同的代码来处理它:

  1. // DRAG ENTER EVENT HANDLER
  2. private void rtbMainForm1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
  3. {
  4. GenericDragEnterEventHandler(sender, e);
  5. }
  6. // DRAG DROP HANDLER
  7. private void rtbMainForm1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
  8. {
  9. string ddfilename = string.Empty;
  10. string extension = string.Empty;
  11. ddfilename = GenericDragDropEventHandler(sender, e)[0];
  12. if (ftt.FileExists(ddfilename ))
  13. {
  14. extension = GetFileExtension(ddfilename);
  15. // HANDLE IMAGE FILE INSERTION
  16. if (ExtensionIsImageFile(extension))
  17. {
  18. InsertImage(ddfilename);
  19. return;
  20. }
  21. // INSERT OTHER FILES
  22. if (rtbMainForm1.TextLength > 0)
  23. {
  24. if (!dlt.QueryDialog(this, "Replace Current File?", "Open A Different File"))
  25. {
  26. return; // allow dialog to cancel action
  27. }
  28. }
  29. // RTF OR TEXT
  30. if (extension == "rtf" || extension == "txt" ||
  31. extension == "tex" || extension == "doc")
  32. {
  33. LoadText(ddfilename);
  34. }
  35. // OPEN OFFICE TEST FILE
  36. else
  37. {
  38. if (extension == "odt")
  39. {
  40. ImportODTFile(ddfilename);
  41. return;
  42. }
  43. // ALL OTHER FILE TYPES
  44. else
  45. {
  46. ImportFile(ddfilename);
  47. }
  48. }
  49. }
  50. }

8、其他功能:我添加的一些便利功能包括Page UpPage Down按钮,当按下Control键时,它们也会滚动到文档的顶部和底部,使用以下代码:

  1. // PAGE UP
  2. private void btnPgUp_Click(object sender, EventArgs e)
  3. {
  4. if (ModifierKeys.HasFlag(Keys.Control))
  5. {
  6. rtbMainForm1.SelectionStart = 0;
  7. rtbMainForm1.SelectionLength = 1;
  8. rtbMainForm1.ScrollToCaret();
  9. return;
  10. }
  11. else
  12. {
  13. rtbMainForm1.Focus();
  14. SendKeys.Send("{PGUP}");
  15. }
  16. }
  17. // PAGE DOWN
  18. private void btnPgDn_Click(object sender, EventArgs e)
  19. {
  20. if (ModifierKeys.HasFlag(Keys.Control))
  21. {
  22. rtbMainForm1.SelectionStart = rtbMainForm1.Text.Length;
  23. rtbMainForm1.SelectionLength = 1;
  24. rtbMainForm1.ScrollToCaret();
  25. return;
  26. }
  27. else
  28. {
  29. rtbMainForm1.Focus();
  30. SendKeys.Send("{PGDN}");
  31. }
  32. }

在处理某些文档时,删除嵌入的回车换行对很有用,它们在显示的RTB文档的实际富文本标记代码中显示为\r\n,因此我添加了此功能:

  1. // REMOVE EMBEDDED LINE BREAKS MENU ITEM
  2. private void removeEmbeddedCRLFsToolStripMenuItem_Click(object sender, EventArgs e)
  3. {
  4. if (rtbMainForm1.SelectedText.Length > 0)
  5. {
  6. RemoveCRLFs();
  7. }
  8. }
  9. // REMOVE CRLFS - (Note: dlt.QueryDialog is a generic custom OK Cancel Dialog
  10. // that returns true if OK is clicked)
  11. private void RemoveCRLFs()
  12. {
  13. if (dlt.QueryDialog(this, "Warning: This will remove all embedded CRLFs permanently.
  14. Do You Wish to proceed?", "Remove Embedded Line Breaks")) ;
  15. string source = rtbMainForm1.SelectedText;
  16. StringBuilder sb = new StringBuilder();
  17. foreach (char ch in source)
  18. {
  19. if (ch == '\r' || ch == '\n')
  20. {
  21. sb.Append(' '); // remove hard coded CRLF
  22. continue;
  23. }
  24. else
  25. {
  26. sb.Append(ch);
  27. }
  28. }
  29. rtbMainForm1.Cut();
  30. Clipboard.Clear();
  31. Clipboard.SetData(DataFormats.Text, sb.ToString());
  32. rtbMainForm1.Paste();
  33. return;
  34. }

修复这些问题和遗漏的结果是EditForm.dll类,可以将其添加到项目中,然后通过创建 editor()的实例、根据需要定制她病调用DisplayEditForm()方法来使用。该Document属性包含要编辑的richtext或简单文本,并被传递回调用者以在应用程序中使用。主要的编辑器public属性是:

  1. // PUBLIC PROPERTY ACCESSORS
  2. // Allow Rich Text editing or text only
  3. public bool AllowRtf
  4. {
  5. get
  6. {
  7. return _allowrtf;
  8. }
  9. set
  10. {
  11. _allowrtf = value;
  12. }
  13. }
  14. // Allow file saving and loading from within the editor
  15. public bool AllowDiscAccess
  16. {
  17. get
  18. {
  19. return _allowdiscacccess;
  20. }
  21. set
  22. {
  23. _allowdiscacccess = value;
  24. }
  25. }
  26. // Offer to save the file when closing the editor
  27. // Disable if using only to edit for a parent application
  28. public bool UseSaveFileDialogWhenClosing
  29. {
  30. get
  31. {
  32. return _EnableSaveFileDialogWhenClosing;
  33. }
  34. set
  35. {
  36. _EnableSaveFileDialogWhenClosing = value;
  37. }
  38. }
  39. // The Document to edit, as RTF or Text
  40. public string Document
  41. {
  42. get
  43. {
  44. return _documenttext;
  45. }
  46. set
  47. {
  48. _documenttext = value;
  49. }
  50. }
  51. // Title of the editor window
  52. public string WindowTitle
  53. {
  54. get
  55. {
  56. return _windowtitle;
  57. }
  58. set
  59. {
  60. _windowtitle = value;
  61. }
  62. }
  63. // Open a Default file if desired
  64. public string FileToOpen
  65. {
  66. set
  67. {
  68. _filetoopen = value;
  69. }
  70. }
  71. // Remember previous editor window size if desired
  72. public Size StartingWindowSize
  73. {
  74. get
  75. {
  76. return _startsize;
  77. }
  78. set
  79. {
  80. _startsize = value;
  81. }
  82. }

附加的DLLhcwgenericclasses通过提供一些用于错误处理和通知的标准化对话框来支持编辑器。这是Demo中的一个应用程序示例,它基于EditForm.dll创建了名为qed.exe的全功能编辑器:

9、

  1. using editform; // INCLUDE IN PROJECT AND REFERENCE
  2. using hcwgenericclasses; // SUPPORTING LIBRARY FUNCTIONS, INCLUDE IN PROJECT AND REFERENCE
  3. //
  4. // QED - A Demo Stand Alone Editor using editform.dll
  5. // HC Williams Copyright (C) 2022 - freeware / opensource GNU public license V3
  6. //
  7. public partial class Form1 : Form
  8. {
  9. public Form1()
  10. {
  11. InitializeComponent();
  12. }
  13. public Form1(string[] arguments) // if run from a command line,
  14. // load a default file specified
  15. {
  16. if (arguments.Length > 0)
  17. {
  18. file = arguments[0];
  19. }
  20. InitializeComponent();
  21. }
  22. // Prevent Low Level WINAPI errors from a recent disk removal
  23. // Revised: 04-10-2016
  24. // http://stackoverflow.com/questions/6080605/can-i-use-seterrormode-in-c-sharp-process
  25. // https://msdn.microsoft.com/en-us/library/
  26. // aa288468%28v=vs.71%29.aspx#pinvoke_callingdllexport
  27. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680621%28v=vs.85%29.aspx
  28. [DllImport("kernel32.dll")]
  29. static extern ErrorModes SetErrorMode(ErrorModes uMode);
  30. [Flags]
  31. public enum ErrorModes : uint
  32. {
  33. SYSTEM_DEFAULT = 0x0,
  34. SEM_FAILCRITICALERRORS = 0x0001, // the one to use
  35. SEM_NOALIGNMENTFAULTEXCEPT = 0x0004,
  36. SEM_NOGPFAULTERRORBOX = 0x0002,
  37. SEM_NOOPENFILEERRORBOX = 0x8000
  38. }
  39. private string file = String.Empty;
  40. private void Form1_Load(object sender, EventArgs e)
  41. {
  42. SetErrorMode(ErrorModes.SEM_FAILCRITICALERRORS); // set on startup
  43. editor ed = new editor();
  44. ed.AllowDiscAccess = true;
  45. ed.WindowTitle = "Quick Edit";
  46. ed.UseAdvancedPrintForm = true; //Added Version 1092
  47. ed.UseSpeechRecognition = true; //Added Version 1092
  48. ed.UseSpellCheck = true; //Added Version 1092
  49. if (file != String.Empty)
  50. {
  51. ed.FileToOpen = file;
  52. }
  53. ed.DisplayEditForm(this);
  54. this.Close();
  55. }
  56. }
  57. }

兴趣点

在这个项目上工作的主要兴趣点是上面概述的那些。但是,我发现我必须添加许多其他小的改进,例如在将图像文件放入文档时正确调整其大小、更改文本和背景颜色以及添加粗体、下划线和斜体按钮,这些都记录在EditForm.dll和演示源代码。

版本1071添加了对上标和下标的支持,方法是在所选文本的开头和结尾添加RTF标记\\super\\nosupersubstring,并将修改后的内容重新插入到文档源代码中。还添加了将表格插入文档的功能,使用表单选择所需的行数和列数。这是通过创建包含表格尺寸和属性的富文本标记语言块并使用剪贴板插入来实现的。插入后,您可以在表格单元格中添加和编辑文本。请注意,不能从文档内部编辑表格对象本身,尽管您可以剪切和粘贴它。创建原始表格富文本代码非常复杂。新的源代码向感兴趣的人展示了它是如何工作的。

1075版添加了将扩展的UNICODE字符从当前字体插入到文档中的功能。这包括选定的特殊字符和符号。我在平铺模式下使用了ListView控件。此版本禁用了以十六进制模式导入文件选项,该选项很慢并且效果不佳。

版本1092 2022212

这个版本增加了改进的用户界面外观,以及使用我的zoomprint.dll 项目进行所见即所得打印的选项,而不是更基本的内置打印预览,一个使用Windows内置语音识别的语音听写选项(如果你训练它,效果最好) ,以及使用流行的NHunspell.dll进行基本拼写检查的选项。 在构建项目时,使用这个更复杂的版本需要几个额外的步骤。首先,这些表单是为高分辨率显示而设计的,您的exe文件的兼容性选项卡下的Windows属性可能需要通过将高DPI缩放覆盖启用为由系统执行来进行调整。二、较新版本的NET 4.6.1更高版本将导致NHunspell.dll 出现混合程序集异常,除非您在与应用程序相同的目录中包含具有以下内容的自定义配置文件:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <configuration>
  3. <startup useLegacyV2RuntimeActivationPolicy="true">
  4. <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
  5. </startup>
  6. </configuration>

此文本文件应与您的应用程序同名,即myapplication.exe.config。使用Visual Studio构建项目并从IDE运行时,应启用相同的选项。

如果您想在创建编辑器时使用拼写检查功能,这只是一个要求。如果是这样,您还必须确保NHunspell.dll及其两个字典文件en_US.affen_US.dic也在应用程序目录中。

除了HNunspell.dll之外,其他支持依赖hcwgenericclasses.dlleditform.dllzoomprint.dll可以内置到您的应用程序项目中,因此它们是程序集的一部分,因此不需要作为单独的文件存在使用它。将它们添加到项目中,添加对每个的引用,然后选择构建选项 -> 嵌入式资源然后在Project.cs文件中包含一个Auto Loader处理程序:

  1. static class Program
  2. {
  3. /// <summary>
  4. /// The main entry point for the application.
  5. /// </summary>
  6. [STAThread]
  7. static void Main(string[] args)
  8. {
  9. Application.EnableVisualStyles();
  10. Application.SetCompatibleTextRenderingDefault(false);
  11. // EMBEDDED DLL HANDLER tested OK 01-15-2014
  12. // Must run in Program Class (where exception occurs
  13. AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
  14. Application.Run(new Form1(args));
  15. }
  16. // EMBEDDED DLL LOADER
  17. // VERSION 3.0 10-27-2020 derives resourcename from args and application namespace
  18. // assumes resource is a DLL
  19. // this should load any missing DLL that is properly embedded
  20. // This version corrects null reference exception when AssemblyStream is null
  21. static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
  22. {
  23. string appname = Application.ProductName + "."; // gets Application Namespace
  24. string[] dll = args.Name.ToString().Split(','); // separates args.Name string
  25. string resourcename = appname + dll[0] + ".dll"; // element [0] contains the missing resource name
  26. Assembly MyAssembly = Assembly.GetExecutingAssembly();
  27. Stream AssemblyStream = MyAssembly.GetManifestResourceStream(resourcename);
  28. // Revised 10-27-2020
  29. if (AssemblyStream == null)
  30. {
  31. return null;
  32. }
  33. // end Rev
  34. byte[] raw = new byte[AssemblyStream.Length];
  35. AssemblyStream.Read(raw, 0, raw.Length);
  36. return Assembly.Load(raw);
  37. }
  38. }

当它们没有作为单独的文件找到时,这会从程序集中加载dll。请参阅示例应用程序qed.exe源代码。

https://www.codeproject.com/Articles/1191753/A-Full-Featured-Editor-Based-on-the-Richtext-Box-3

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/230368
推荐阅读
相关标签
  

闽ICP备14008679号