WPF、マウスドラッグ移動できるTextBox、Templateを改変したThumbで作成
結果
コード
2022WPF/20220615_TextBoxThumb0/20220615_TextBoxThumb0 at master · gogowaten/2022WPF
github.com
MainWindow.xaml
<Window x:Class="_20220615_TextBoxThumb0.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:_20220615_TextBoxThumb0" mc:Ignorable="d" Title="MainWindow" Height="250" Width="400"> <Canvas Name="MyCanvas"/> </Window>
MainWindow.xaml.cs
using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Controls.Primitives; namespace _20220615_TextBoxThumb0 { public partial class MainWindow : Window { private TBThumb MyTBThumb; private TBThumb2 MyTBThumb2; public MainWindow() { #if DEBUG Left = 100; Top = 100; #endif InitializeComponent(); MyTBThumb = new TBThumb(); MyCanvas.Children.Add(MyTBThumb); MyTBThumb.TextBox.Text = "移動できないTextBoxThumb"; MyTBThumb.DragDelta += Thumb_DragDelta; MyTBThumb2 = new TBThumb2(); MyCanvas.Children.Add(MyTBThumb2); MyTBThumb2.TextBox.Text = "ダブルクリックで\nテキスト編集と移動を切り替え"; MyTBThumb2.TextBox.AcceptsReturn = true;//改行を有効 MyTBThumb2.DragDelta += Thumb_DragDelta; } private void Thumb_DragDelta(object sender, DragDeltaEventArgs e) { if (sender is not FrameworkElement elem) { return; } Canvas.SetLeft(elem, Canvas.GetLeft(elem) + e.HorizontalChange); Canvas.SetTop(elem, Canvas.GetTop(elem) + e.VerticalChange); } } //TemplateをTextBoxにしたThumb、マウスドラッグ移動できない public class TBThumb : Thumb { private const string TEXTBOX_NAME = "ttt"; public TextBox TextBox; public TBThumb() { this.Template = MakeControlTemplate(); //Templateの更新、必要 ApplyTemplate(); //Templateの中のTextBoxを検索、取得 this.TextBox = (TextBox)Template.FindName(TEXTBOX_NAME, this); Canvas.SetLeft(this, 0); Canvas.SetTop(this, 0); } //TextBoxをベースにしたControlTemplateを作成 private ControlTemplate MakeControlTemplate() { FrameworkElementFactory textF = new(typeof(TextBox), TEXTBOX_NAME); ControlTemplate template = new(); template.VisualTree = textF; return template; } } //TemplateをGridを乗せたTextBoxにしたThumb、マウスドラッグ移動できる //ダブルクリックで移動モードとText編集モードを切り替える public class TBThumb2 : Thumb { private const string COVER_NAME = "cover"; private const string TEXTBOX_NAME = "ttt"; public Grid CoverGrid; public TextBox TextBox; public TBThumb2() { this.Template = MakeControlTemplate(); //Templateの更新、必要 ApplyTemplate(); //Templateの中のTextBoxと蓋用のGridを検索、取得 this.TextBox = (TextBox)Template.FindName(TEXTBOX_NAME, this); this.CoverGrid = (Grid)Template.FindName(COVER_NAME, this); //表示位置を指定 Canvas.SetLeft(this, 0); Canvas.SetTop(this, 0); //ダブルクリック時の動作 MouseDoubleClick += TBThumb2_MouseDoubleClick; } //ControlTemplate作成 private static ControlTemplate MakeControlTemplate() { FrameworkElementFactory coverF = new(typeof(Grid), COVER_NAME);//蓋 FrameworkElementFactory textF = new(typeof(TextBox), TEXTBOX_NAME); FrameworkElementFactory underF = new(typeof(Grid));//ベースGrid //蓋の背景は透明色 coverF.SetValue(Panel.BackgroundProperty, Brushes.Transparent); //ベースGridに要素追加、順番はTextBox、蓋Gridの順 underF.AppendChild(textF); underF.AppendChild(coverF); //テンプレート作成、VisualTreeにベースを指定 ControlTemplate template = new(); template.VisualTree = underF; return template; } //ダブルクリックでテキスト編集状態の切り替え private void TBThumb2_MouseDoubleClick(object sender, MouseButtonEventArgs e) { //蓋の背景が透明色ならTextBoxを編集状態にする、背景をnullにする if (CoverGrid.Background == Brushes.Transparent) { CoverGrid.Background = null; Keyboard.Focus(TextBox); } //それ以外なら編集状態終了、背景を透明色にしてキーボードのフォーカスを外す else { CoverGrid.Background = Brushes.Transparent; Keyboard.ClearFocus();//キーボードのフォーカスを外す } } } }
最初は失敗した
TemplateをTextBoxに変更したThumbを作れば、
ドラッグ移動可能なTextBoxになると思って作ったクラスが↓
Thumbを継承したクラスにして、41行目
TextBoxだけで構成したTemplateを作成、57行目
これを自身のTemplateに指定する、47行目
で、これをMainWindowのほうで
ドラッグ移動イベント(DragDelta)で移動するようにしたんだけど、これが動かない
どうやらドラッグ移動イベント自体が発生していないみたい
デバッグで
要素の選択を有効にするとクリックした要素が判別できるから、これで見てみると
TBThumbの中のTextBoxのそれも、かなり下の方にあるTextBoxViewってのが選択されていた
ドラッグ移動イベントはもっと上の方で発生するのに、それをスルーしてTextBoxViewってのが選択されてしまうから移動しないのかも?
じゃあTextBoxの上に何かの要素を乗せて蓋をしておけば、TextBoxViewより蓋が選択されてドラッグ移動イベントが発生するじゃないかと考えてできたのがTBThumb2↓
蓋にはGrid要素を使った
TextBoxと蓋を重ねるから、それを詰め込むためのPanel要素にもGridを使用
- Grid
- TextBox
- Grid(蓋)
GridやCanvas要素は背景色を指定しない(null)と、クリックが無視されるので透明色を指定
これで見た目は普通のTextBoxになるし、ドラッグ移動もできるようになる、けどこのままだと
今度はクリックが蓋に邪魔されてTextBoxまで到達できないので、テキスト編集ができない
これは蓋の背景色をnullにすれば蓋をスルーするようになるので、
要は蓋の背景色を透明とnullの切り替えができればいい
今回はダブルクリックで切り替えるようにしてみた
キーボードのフォーカスを外すKeyboard.ClearFocus()
これをしないと編集状態を終了して移動モードになってもカーソルが残ったままになる
この方法は
WPFでコントロールからフォーカスを外す方法 - Qiita
qiita.com
こちらから
要素の選択:移動時
蓋用のGridが選択されるのかとも思っていたけど、選択されていたのは一番上のTBThumb2だった
テキスト編集状態のときは?
これは普通にTextBoxViewが選択されていた
この辺の動作はさっぱりわからんし、TextBoxって11個もの要素で構成されているのは意外だった、へー
あとは文字の装飾したい!
関連記事
次回のWPF記事
gogowaten.hatenablog.com
前回のWPF記事は昨日
gogowaten.hatenablog.com