同期されたRichTextBoxをドラッグ&ドロップにも対応させます。
ドラッグ&ドロップの場合、以下のような場合が考えられます。
ファイルがドロップされた
テキスト、リッチテキストがドロップされた
コントロール内部でドラッグ&ドロップがおこなわれた
この3つが考えられます。
まず「ファイルがドロップされた」場合について考えます。
DragEventArgs.Data.GetDataPresentでドロップされたものがファイルだった場合、OnDragDropFilesメソッドを呼び出して処理をさせています。複数個のファイルがドロップされても実際に処理がおこなわれるのはひとつだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public partial class SyncRichTextBox : UserControl { protected override void OnDragOver(DragEventArgs e) { e.Effect = DragDropEffects.Copy; } protected override void OnDragDrop(DragEventArgs e) { if(e.Data.GetDataPresent(DataFormats.FileDrop)) { OnDragDropFiles(e); } e.Effect = DragDropEffects.None; } protected virtual void OnDragDropFiles(DragEventArgs e) { string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); string filePath = files[0]; RichTextBox rich = new RichTextBox(); RichTextBoxOle.RichTextBoxOle.InsertFileBmpIfImage(rich, filePath, 0); rich.SelectAll(); if (rich.SelectedText != "") { SetSelectedRtf(rich.SelectedRtf, "DropFile"); } rich.Dispose(); } } |
テキスト、リッチテキストがドロップされた
コントロール内部でドラッグ&ドロップがおこなわれた
このふたつですが、
1 2 3 |
if (e.Data.GetDataPresent(DataFormats.Rtf)) { } |
とやってしまうと、外部からRtfデータがドロップされたのか、同一コントロール内部でのドラッグ&ドロップなのか区別できません。両者は別々の処理をするため区別する必要があります。
ドラッグが開始されるときにその情報をフィールド変数に記憶させておけばよいのですが、ドラッグが開始されるときをどうやって知ればいいのでしょうか?
DragEnterイベントを使えばいいじゃないか! DragEnterイベントはドラッグ中のイベントやテキストの選択範囲が、妥当なドロップターゲットに入ったときに発生するイベントですが、ドラッグが開始されたときにも発生します。
しかしこれは絶対ではありません。文字列を選択してゆっくりドラッグをするとDragEnterイベントは発生しますが、すばやくコントロールの外にドラッグしてしまうとこのイベントは発生しません。
ではどうするか? MouseLeaveイベントを使います。ただしMouseLeaveイベントはマウスポインタが要素の領域から離れた時に発生するイベントであり、ドラッグ開始以外のときも発生します。そこでマウスボタンが押されているかどうかを確認します。そうすればドラッグの開始を知ることができるのです。
ドラッグの開始を知ることができたらどのようなデータがドラッグされようとしているのか情報を保存してしまいましょう。
こんなクラスをつくります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
internal class DragDropRtfInfo { internal DragDropRtfInfo(SyncRichTextBox dragSource, int selectionStart, string rtf, string text) { DragSourceCtrl = dragSource; SelectionStart = selectionStart; SelectionLength = text.Length; Rtf = rtf; Text = text; } internal SyncRichTextBox DragSourceCtrl { get; } internal int SelectionStart { get; } internal int SelectionLength { get; } internal string Rtf { get; } internal string Text { get; } } |
SyncRichTextBoxクラス内に静的フィールド変数
static DragDropRtfInfo _dragDropRtfInfo = null;
これでどのSyncRichTextBoxオブジェクトからでもどのようなデータがドラッグされているのかを知ることができます。
またドロップされたらそれを知る必要があります。このタイミングで_dragDropRtfInfo = null;とする必要があります。DragDropイベントを使えば自分のコントロールでドロップされたことを知ることができますが、ドラッグされたものが自分のコントロールでドロップされるかどうかはわかりません。それを知る方法、それは・・・
MouseMoveイベントです。
そこで
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
public partial class SyncRichTextBox : UserControl { public SyncRichTextBox() { InitializeComponent(); richTextBoxEx1.MouseLeave += (sender1, e1) => this.OnMouseLeave(e1); richTextBoxEx1.MouseMove += (sender1, e1) => this.OnMouseMove(e1); richTextBoxEx1.DragOver += (sender1, e1) => this.OnDragOver(e1); richTextBoxEx1.DragDrop += (sender1, e1) => this.OnDragDrop(e1); } static DragDropRtfInfo _dragDropRtfInfo = null; protected override void OnMouseLeave(EventArgs e) { if (Control.MouseButtons == MouseButtons.Left) { _dragDropRtfInfo = new DragDropRtfInfo(this, this.SelectionStart, this.SelectedRtf, this.SelectedText); } } protected override void OnMouseMove(MouseEventArgs e) { if (_dragDropRtfInfo != null) { _dragDropRtfInfo = null; } } protected override void OnDragDrop(DragEventArgs e) { if(e.Data.GetDataPresent(DataFormats.FileDrop)) { OnDragDropFiles(e); } else if (_dragDropRtfInfo == null || _dragDropRtfInfo.DragSourceCtrl.Data != this.Data) { if (e.Data.GetDataPresent(DataFormats.Rtf)) { string rtf = (string)e.Data.GetData(DataFormats.Rtf); SelectionLength = 0; SetSelectedRtf(rtf, "DropText"); } else if(e.Data.GetDataPresent(DataFormats.UnicodeText)) { string text = (string)e.Data.GetData(DataFormats.UnicodeText); SelectionLength = 0; SetSelectedText(text, "DropText"); } else if (e.Data.GetDataPresent(DataFormats.Text)) { string text = (string)e.Data.GetData(DataFormats.Text); SelectionLength = 0; SetSelectedText(text, "DropText"); } } else { OnDragDropInternal(e, _dragDropRtfInfo); } e.Effect = DragDropEffects.None; } } |
とやれば、うまくいきそうです。
さてOnDragDropInternal(DragEventArgs , DragDropRtfInfo)の内容ですが、次回に続きます。