ダブルクリックで編集可能になるTextBoxをつくる
WPFで、ダブルクリックで編集可能になるTextBoxをつくりたいなーと思ったので作ってみた。
Behaviorで、TextBoxのIsReadOnlyとFocusableを制御する作戦。
コードはこんな感じ。
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interactivity; namespace Railgun.Helper { /** * ダブルクリックで編集モードに移行するTextBoxになる * フォーカスが外れるか、Enterキーで編集終了となる * * <TextBox Text="hogehoge"> * <i:Interaction.Behaviors> * <local:EditableLabelBehavior/> * </i:Interaction.Behaviors> * </Button> */ public class EditableLabelBehavior : Behavior<TextBox> { // bool IsEditable 編集可能か? public static readonly DependencyProperty IsEditableProperty = DependencyProperty.Register("IsEditable", typeof(bool), typeof(EditableLabelBehavior), new PropertyMetadata(false, OnIsEditableChanged)); public bool IsEditable { get { return (bool)GetValue(IsEditableProperty); } set { SetValue(IsEditableProperty, value); } } static void OnIsEditableChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { EditableLabelBehavior behavior = obj as EditableLabelBehavior; behavior.ApplyEditable(); } // Thickness EditingBorderThickness 編集中の枠線 public static readonly DependencyProperty EditingBorderThicknessProperty = DependencyProperty.Register("EditingBorderThickness", typeof(Thickness), typeof(EditableLabelBehavior), new PropertyMetadata(new Thickness(3), OnEditingBorderThicknessChanged)); public Thickness EditingBorderThickness { get { return (Thickness)GetValue(EditingBorderThicknessProperty); } set { SetValue(EditingBorderThicknessProperty, value); } } static void OnEditingBorderThicknessChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { } // textBoxの状態を編集可・不可に切り替える void ApplyEditable() { if (IsEditable) { // 編集可 AssociatedObject.IsReadOnly = false; AssociatedObject.BorderThickness = EditingBorderThickness; AssociatedObject.Focusable = true; } else { // 編集不可 AssociatedObject.IsReadOnly = true; AssociatedObject.BorderThickness = _borderThickness; // 元に戻す AssociatedObject.Focusable = false; } } Thickness _borderThickness; /*-----------------------------------------------*/ // 初期化 protected override void OnAttached() { _borderThickness = AssociatedObject.BorderThickness; ApplyEditable(); AssociatedObject.MouseDoubleClick += AssociatedObject_MouseDoubleClick; AssociatedObject.LostFocus += AssociatedObject_LostFocus; AssociatedObject.KeyDown += AssociatedObject_KeyDown; } protected override void OnDetaching() { AssociatedObject.MouseDoubleClick -= AssociatedObject_MouseDoubleClick; AssociatedObject.LostFocus -= AssociatedObject_LostFocus; AssociatedObject.KeyDown -= AssociatedObject_KeyDown; } void AssociatedObject_MouseDoubleClick(object sender, MouseButtonEventArgs e) { IsEditable = true; // 編集可 // ダブルクリック後のカーソル位置を決める Point pos = Mouse.GetPosition(AssociatedObject); int textIndex = AssociatedObject.GetCharacterIndexFromPoint(pos, false); if (textIndex < 0) { textIndex = AssociatedObject.Text.Length; } AssociatedObject.Select(textIndex, 0); // BeginInvokeで実行しないとフォーカスが取れなかった Dispatcher.BeginInvoke(new Action(() => { AssociatedObject.Focus(); })); } void AssociatedObject_LostFocus(object sender, RoutedEventArgs e) { IsEditable = false; //Console.WriteLine("AssociatedObject_LostFocus"); } void AssociatedObject_KeyDown(object sender, KeyEventArgs e) { // Enterキーが押されたら編集終了 if (e.Key == Key.Enter) { IsEditable = false; } } } }
使い方
<TextBox Text="hogehoge"> <i:Interaction.Behaviors> <local:EditableLabelBehavior EditingBorderThickness="3"/> </i:Interaction.Behaviors> </Button>
ゲームエディタ作ってる中で実験してたので、実はシンプルな例で試してない。
うまく動かんかったらすまぬ。
ダブルクリックで編集可能になって、フォーカスが外れると編集不可になる。
編集時はBorderThicknessの太さが変わるようにして、Enterキーで編集終了。
という動きになってる。
実装上のポイントは、
編集可能:TextBox.IsReadOnly = false; TextBox.Focusable = true;
編集不可:TextBox.IsReadOnly = true; TextBox.Focusable = false;
としているところ。これでだいたい困らないかなーと。
あと、ダブルクリックしたときはカーソル位置を決めてからフォーカスをとっている。
ただ、普通にAssociatedObject.Focus();としてもうまく動かなかったので、Dispatcher.BeginInvoke経由で実行してる。
(なお、このやり方見つけるまではmouse_eventを駆使して無理やり再クリックさせてフォーカスをとるという実装だった・・・)
その時の実装もメモ程度に載せておく。一応これでも動く
// マウスクリック [DllImport("USER32.dll", CallingConvention = CallingConvention.StdCall)] private static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo); private const int MOUSEEVENTF_LEFTDOWN = 0x2; private const int MOUSEEVENTF_LEFTUP = 0x4; // ...(略)... // AssociatedObject.Focus()だとうまくいかなかったので、無理やりクリックさせてみる mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); // ...(略)...
参考にしたのはこのあたり:
stackoverflow.com
stackoverflow.com
www.nuits.jp