C# - RichTextBox関連やりたいこと逆引きリファレンス【自分用】

# 文字位置情報の取得と設定

## 位置情報の取得

| やりたいこと | プロパティまたはメソッド名 | 参考サイト |
|----|----|----|
| 位置座標 ⇒ 文字indexの取得 | [`GetCharIndexFromPosition`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextbox.getcharindexfromposition?view=netframework-4.8#System_Windows_Forms_RichTextBox_GetCharIndexFromPosition_System_Drawing_Point_) |  |
| 文字index ⇒ 位置座標の取得　| [`GetPositionFromCharIndex`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextbox.getpositionfromcharindex?view=netframework-4.8) |  |
| 文字index ⇒ 行番号indexの取得　| [`GetLineFromCharIndex`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextbox.getlinefromcharindex?view=netframework-4.8#System_Windows_Forms_RichTextBox_GetLineFromCharIndex_System_Int32_) |  [dobon](https://dobon.net/vb/dotnet/control/tbgetcaretposition.html#section4) |
| 行番号index ⇒ 行頭文字indexの取得 | [`GetFirstCharIndexFromLine`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.textboxbase.getfirstcharindexfromline?view=netframework-4.8) | |
| 現在行の行頭文字indexの取得 | [`GetFirstCharIndexOfCurrentLine`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.textboxbase.getfirstcharindexofcurrentline?view=netframework-4.8#System_Windows_Forms_TextBoxBase_GetFirstCharIndexOfCurrentLine) | |

## 選択位置の取得と設定

| やりたいこと | プロパティまたはメソッド名 | 参考サイト |
|----|----|----|
| 選択開始位置(index)の取得[^1] | [`SelectionStart`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.textboxbase.selectionstart?view=netframework-4.8#System_Windows_Forms_TextBoxBase_SelectionStart) |
| 選択文字数の取得 | [`SelectionLength`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextbox.selectionlength?view=netframework-4.8#System_Windows_Forms_RichTextBox_SelectionLength) |
| 選択対象文字列(indexと文字数)の設定 | [`Select`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.textboxbase.select?view=netframework-4.8) |
| 全選択 | [`SelectAll()`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.textboxbase.selectall?view=netframework-4.8#System_Windows_Forms_TextBoxBase_SelectAll) |


# 書式設定

選択されている部分文字列に対して書式を設定します。<b><font color="red">※要注意[^2]</font></b>

| やりたいこと | プロパティまたはメソッド名 | 参考サイト |
|----|----|----|
| 背景色を設定／取得 | [`SelectionBackColor`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextbox.selectionbackcolor?view=netframework-4.8#System_Windows_Forms_RichTextBox_SelectionBackColor) |
| 文字の色を設定／取得 | [`SelectionColor`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextbox.selectioncolor?view=netframework-4.8) |
| フォントを設定／取得 | [`SelectionFont`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextbox.selectionfont?view=netframework-4.8) |
| 上付き下付きオフセットを設定／取得 | [`SelectionCharOffset`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextbox.selectioncharoffset?view=netframework-4.8) | [dobon](https://dobon.net/vb/dotnet/control/rtbcharoffset.html) |

# テキスト検索

| やりたいこと | プロパティまたはメソッド名 | 参考サイト |
|----|----|----|
| 文字列検索 | [`Find`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextbox.find?view=netframework-4.8) |

パラメータ: [`RichTextBoxFinds`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextboxfinds?view=netframework-4.8)

# ファイル操作

自前で読み込み処理を作っても問題はないが、下記が使えるかも。

| やりたいこと | プロパティまたはメソッド名 | 参考サイト |
|----|----|----|
| 読み込み | [`LoadFile`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextbox.loadfile?view=netframework-4.8) |
| 保存 | [`SaveFile`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextbox.savefile?view=netframework-4.8) |

パラメータ: [`RichTextBoxStreamType`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextboxstreamtype?view=netframework-4.8)

# 主要なイベント

| やりたいこと | イベント名 | 参考サイト |
|----|----|----|
| テキストが変更されたとき<b><font color="red">（※要注意[^2]）</font></b> | [`TextChanged`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.control.textchanged?view=netframework-4.8) |
| 選択文字位置または文字数が変更されたとき |  [`SelectionChanged`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextbox.selectionchanged?view=netframework-4.8) |

# その他いろいろ

## コントロールのプロパティ

 - [RichTextBox - C#プチリファレンス](https://csharp-ref.com/form_richtextbox.html)
 
## タブ幅の指定

 - [TextBoxやRichTextBoxのタブの幅を変更する - .NET Tips (VB.NET,C#...)](https://dobon.net/vb/dotnet/control/tbtacwidth.html)


`using System.Runtime.InteropServices;`

```csharp:

[DllImport("user32")]
public extern static IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

private const int EM_SETTABSTOPS = 0x00CB;

static IntPtr SetTabStop(TextBoxBase t, int tabSize)
{
    int[] tabarray = new int[] { tabSize*4 };
    int wparam = tabarray.Length;

    IntPtr parray = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)) * tabarray.Length);
    Marshal.Copy(tabarray, 0, parray, tabarray.Length);
    IntPtr ret = NativeMethods.SendMessage(t.Handle, EM_SETTABSTOPS, new IntPtr(wparam), parray);
    return ret;
}
```

## フォントが勝手に変わる問題の対策

 - [フォントが勝手に変更されるのを抑制する - RichTextBox Tips | HIRO's.NET](http://hiros-dot.net/CS2005/Control/RichTextBox/RichTextBox13.htm)

タブ幅設定より先に実施したほうがよい・・はず。

```csharp:

private const long IMF_DUALFONT = 0x80;
private const int WM_USER = 0x0400;
private const int EM_SETLANGOPTIONS = WM_USER + 120;
private const int EM_GETLANGOPTIONS = WM_USER + 121;

static IntPtr SetFixFont(TextBoxBase t)
{
    IntPtr lParam = NativeMethods.SendMessage(t.Handle, EM_GETLANGOPTIONS, new IntPtr(0), new IntPtr(0));
    lParam = new IntPtr( ((long)lParam) & (~IMF_DUALFONT));
    return NativeMethods.SendMessage(t.Handle, EM_SETLANGOPTIONS, new IntPtr(0), lParam);
}
```


## 再描画の停止と再開

 - [コントロールの描画を一時的に停止する - .NET Tips (VB.NET,C#...)](https://dobon.net/vb/dotnet/control/beginupdate.html#wmsetredraw)

### 描画の再開についての補足
（別記事に引っ越すかも）
RichTextBoxに関しては、描画の再開が`Invalidate()`や[`RedrawWindow`(Win32API)](https://docs.microsoft.com/en-us/windows/win32/gdi/wm-setredraw#remarks)ではうまくいかない。（矢印キー操作は描画がされるが、マウス操作だと描画されないままになってしまう。）
`this.ActiveControl = null;` と RichTextBox`.Focus();` して強引に復帰させればいけているっぽいが、現物合わせなので根拠はない。

## オーナードロー

 プログラム起点で`Select`で書式を変えると、色々と
邪魔な
イベントが発生してしまうため、自前で描画したほうが整合をとりやすいかもしれない。オーナードローは有力な選択肢と思っている。（もはや`RichTextBox`である必要性がないかもしれない。。。[^3]）

 - [C#のRichTextBoxで描画処理を追加する - Qiita](https://qiita.com/R_TES_/items/76f1a2be939cbd3aaa09)

## IMEが勝手に確定する問題の対策

 - [HiBARA Software Blog](https://hibara.org/blog/2013/06/18/textchanged-event-ime/)

  - [Get text from richtextbox control without disturbing IME when input east Asian language](https://social.msdn.microsoft.com/Forums/en-US/cefa5376-7912-47f6-b86a-197a211c2b70/get-text-from-richtextbox-control-without-disturbing-ime-when-input-east-asian-language?forum=winforms)

# 色々取り込んでみた結果

ほとんど`RichBoxText`の使い方をしていない気がするが。。。

 - [C# - いろんな対策を取り込んだ RichTextBox テンプレ - 全角空白とタブと改行を表示【自分用】 - Qiita](https://qiita.com/kob58im/items/d238434149125f7cad56)

# 参考サイト

 - [RichTextBox Tips メニュー < C# 2005 Tips HOME < HIRO's.NET](http://hiros-dot.net/CS2005/Control/RichTextBoxMenu.htm)
 - [コントロール(System.Windows.Forms) - .NET Tips (VB.NET,C#...)](https://dobon.net/vb/dotnet/control/index.html)
 - [RichTextBox Class (System.Windows.Forms) | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.richtextbox?view=netframework-4.8)
 - [Edit Control Messages - Win32 apps | Microsoft Docs](https://docs.microsoft.com/en-us/windows/win32/controls/bumper-edit-control-reference-messages)

[^1]: 右端が選択されている場合、取得結果=文字数(=`Text.Length`)となりえるので注意。

[^2]: [書式を変えるだけなのに`TextChanged`イベントが発生する](https://qiita.com/kob58im/items/28d59794d1e64a2427fa)ようなので注意。（設定に依存？）

[^3]: ただし、`TextBox`だと文字を入力しただけだと、`WM_PAINT`メッセージが発火しない。