WPF の TextBox をマウスでクリックして選択した場合、クリックした箇所に近い文字間にキャレットが挿入されます。
そうじゃなくて、IE の URL を入力するところのように、テキストを全選択したい…というケースの実装方法について。
具体的には、以下のような流れで処理しています。
- 左マウスボタンを押したとき、まだフォーカスを持っていなければ、フォーカスを強制的に得る。
- そして、イベントチェーンを切断する。(e.Handled = True)
- フォーカスイベントで、テキストを全選択する。
コードビハインド側
Class MainWindow
Private Sub TextBox_PreviewMouseLeftButtonDown(sender As Object, e As MouseButtonEventArgs)
Dim t = CType(sender, TextBox)
If (t.IsFocused = False) Then
t.Focus()
e.Handled = True
End If
End Sub
Private Sub TextBox_GotFocus(sender As Object, e As RoutedEventArgs)
Dim t = CType(sender, TextBox)
t.SelectAll()
End Sub
End Class
XAML側
<Window ....
<Window.Resources>
<Style TargetType="TextBox">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="TextBox_PreviewMouseLeftButtonDown"/>
<EventSetter Event="GotFocus" Handler="TextBox_GotFocus"/>
</Style>
</Window.Resources>
<StackPanel>
<TextBox Text="AAAA"/>
<TextBox Text="BBBB"/>
<TextBox Text="CCCC"/>
<TextBox Text="DDDD"/>
</StackPanel>
</Window>
なんでこんな処理になるかというと、デフォルトでは、以下の動きをしているようです。(予想/根拠なし)
- 左クリックする。
- テキストボックスにフォーカスが与えられる。そして、フォーカスが与えられた段階ではキャレットはテキストの先頭にいる。
- その後、クリックした近辺にキャレットを移動する。(=MouseLeftButtonDownイベントのタイミング?)
つまり、2. のフォーカスを取得した段階でテキストを全選択 (.SelectAll) しても、その後の 3. の処理で全選択が解除(=キャレットが設定)されてしまいます。
このため、PreviewMouseLeftButtonDown イベントの段階でイベントチェーンを断ち切ることで、MouseLeftButtonDown イベントを抑止し、キャレットが移動することを阻止しています。
阻止するのはそのテキストボックスが初めてフォーカスを得るとき(クリックしたときにフォーカスがない場合)のみです。でないと、マウスクリックでキャレットを移動させることそのものができなくなってしまいます。
また、以後のイベントを抑止してしまうと、既定の TextBox に Focus を与える処理に届かなくなるので、イベントの抑止と同時にフォーカスを強制的に得るようにします。
なお、PreviewMouseLeftButtonDown イベント(トンネル イベント)と MouseLeftButtonDown イベント(バブルイベント)の違いについては以下が詳しいです。
http://msdn.microsoft.com/ja-jp/library/ms742806.aspx#how_event_processing_works
参考) イベントの伝播
