WinForms TextBox をブラウザのアドレス バーのように動作させる
-
01-07-2019 - |
質問
C# WinForms テキストボックスがフォーカスを受け取ったとき、ブラウザのアドレス バーのように動作するようにしたいと考えています。
私の言っている意味を確認するには、Web ブラウザのアドレス バーをクリックしてください。次のような動作が見られます。
- テキストボックスが以前にフォーカスされていない場合は、テキストボックスをクリックするとすべてのテキストが選択されます。
- マウスを押してテキストボックス内をドラッグすると、マウスで強調表示したテキストのみが選択されます。
- テキストボックスがすでにフォーカスされている場合、クリックしてもすべてのテキストが選択されません。
- プログラムまたはキーボードのタブ操作によってテキストボックスにフォーカスすると、すべてのテキストが選択されます。
まさにこれを WinForms で実行したいと考えています。
最速の銃警報:回答する前に以下をお読みください。 みんなありがとう。:-)
.enterまたは.gotfocusイベント中に.selectall()が機能しない なぜなら、ユーザーがテキストボックスをクリックした場合、CARETはクリックされた場所に配置され、すべてのテキストが選択されないためです。
.Click イベント中に .SelectAll() を呼び出すと機能しない ユーザーはマウスでテキストを選択できないためです。.SelectAll() 呼び出しは、ユーザーのテキスト選択を上書きし続けます。
フォーカス/エンターイベントの Enter で BeginInvoke((Action)textbox.SelectAll) を呼び出すと機能しない これは上記のルール #2 に違反するため、フォーカス上のユーザーの選択をオーバーライドし続けます。
解決
まず最初に、ご回答いただきありがとうございます。合計9つの回答。ありがとう。
悪いニュース:すべての答えにはいくつかの癖があるか、まったく正しく機能しませんでした(またはまったく機能しませんでした)。それぞれの投稿にコメントを追加しました。
良いニュース:それを機能させる方法を見つけました。この解決策は非常に簡単で、すべてのシナリオ (マウスダウン、テキストの選択、タブ移動など) で機能するようです。
bool alreadyFocused;
...
textBox1.GotFocus += textBox1_GotFocus;
textBox1.MouseUp += textBox1_MouseUp;
textBox1.Leave += textBox1_Leave;
...
void textBox1_Leave(object sender, EventArgs e)
{
alreadyFocused = false;
}
void textBox1_GotFocus(object sender, EventArgs e)
{
// Select all text only if the mouse isn't down.
// This makes tabbing to the textbox give focus.
if (MouseButtons == MouseButtons.None)
{
this.textBox1.SelectAll();
alreadyFocused = true;
}
}
void textBox1_MouseUp(object sender, MouseEventArgs e)
{
// Web browsers like Google Chrome select the text on mouse up.
// They only do it if the textbox isn't already focused,
// and if the user hasn't selected all text.
if (!alreadyFocused && this.textBox1.SelectionLength == 0)
{
alreadyFocused = true;
this.textBox1.SelectAll();
}
}
私の知る限り、これによりテキストボックスは Web ブラウザのアドレス バーとまったく同じように動作します。
これが、この一見単純な問題を解決しようとする次の人に役立つことを願っています。
皆さん、私を正しい道に導くのに役立ったすべての回答に改めて感謝します。
他のヒント
これに対するより簡単な解決策を見つけました。これには、次を使用して SelectAll を非同期的に開始することが含まれます。 Control.BeginInvoke
Enter イベントと Click イベントが発生した後に発生するようにします。
C# の場合:
private void MyTextBox_Enter(object sender, EventArgs e)
{
// Kick off SelectAll asyncronously so that it occurs after Click
BeginInvoke((Action)delegate
{
MyTextBox.SelectAll();
});
}
VB.NET では (おかげで クリシャヌ・デイ)
Private Sub MyTextBox_Enter(sender As Object, e As EventArgs) Handles MyTextBox.Enter
BeginInvoke(DirectCast(Sub() MyTextBox.SelectAll(), Action))
End Sub
あなたのソリューションは優れていますが、ある特定のケースで失敗します。単にクリックするのではなく、テキストの範囲を選択して TextBox にフォーカスを与えると、readyFocussed フラグが true に設定されないため、TextBox 内を 2 回目にクリックすると、すべてのテキストが選択されます。
これが私のバージョンのソリューションです。また、コードを TextBox を継承するクラスに配置したので、ロジックはうまく隠蔽されています。
public class MyTextBox : System.Windows.Forms.TextBox
{
private bool _focused;
protected override void OnEnter(EventArgs e)
{
base.OnEnter(e);
if (MouseButtons == MouseButtons.None)
{
SelectAll();
_focused = true;
}
}
protected override void OnLeave(EventArgs e)
{
base.OnLeave(e);
_focused = false;
}
protected override void OnMouseUp(MouseEventArgs mevent)
{
base.OnMouseUp(mevent);
if (!_focused)
{
if (SelectionLength == 0)
SelectAll();
_focused = true;
}
}
}
少し面倒ですが、クリック イベントで使用します。 SendKeys.Send( "{HOME}+{END}" );
.
テキストボックスのクリックイベント?または、MouseCaptureChanged イベントでも機能します。- わかりました。機能しません。
したがって、次の 2 つのことを行う必要があります。
private bool f = false;
private void textBox_MouseClick(object sender, MouseEventArgs e)
{
if (this.f) { this.textBox.SelectAll(); }
this.f = false;
}
private void textBox_Enter(object sender, EventArgs e)
{
this.f = true;
this.textBox.SelectAll();
}
private void textBox_MouseMove(object sender, MouseEventArgs e) // idea from the other answer
{
this.f = false;
}
(textBox を介してそのタブに移動する) タブ移動にも機能します - 念のため Enter で SelectAll() を呼び出します...
私が使用する 1 行の答え...あなたは自分自身を蹴っているかもしれません...
Enterイベントで:
txtFilter.BeginInvoke(new MethodInvoker( txtFilter.SelectAll));
'Inside the Enter event
TextBox1.SelectAll();
OK、試してみると、あなたが望むものは次のとおりです。
- Enter イベントで、自分が Enter イベントに参加したことを示すフラグを開始します。
- Click イベントでフラグを設定した場合は、.SelectAll() を呼び出してフラグをリセットします。
- MouseMove イベントで、入力されたフラグを false に設定します。これにより、最初にテキスト ボックスに入力しなくてもハイライトをクリックできるようになります。
これにより、入力時にすべてのテキストが選択されましたが、後でテキストの一部を強調表示したり、最初のクリックで強調表示したりすることができます。
リクエストにより:
bool entered = false;
private void textBox1_Enter(object sender, EventArgs e)
{
entered = true;
textBox1.SelectAll(); //From Jakub's answer.
}
private void textBox1_Click(object sender, EventArgs e)
{
if (entered) textBox1.SelectAll();
entered = false;
}
private void textBox1_MouseMove(object sender, MouseEventArgs e)
{
if (entered) entered = false;
}
私の場合、タブでコントロールに移動すると、すべてのテキストが選択されます。
これは、ソリューションを次のレベルに引き上げるヘルパー関数です。継承を行わずに再利用します。
public static void WireSelectAllOnFocus( TextBox aTextBox )
{
bool lActive = false;
aTextBox.GotFocus += new EventHandler( ( sender, e ) =>
{
if ( System.Windows.Forms.Control.MouseButtons == MouseButtons.None )
{
aTextBox.SelectAll();
lActive = true;
}
} );
aTextBox.Leave += new EventHandler( (sender, e ) => {
lActive = false;
} );
aTextBox.MouseUp += new MouseEventHandler( (sender, e ) => {
if ( !lActive )
{
lActive = true;
if ( aTextBox.SelectionLength == 0 ) aTextBox.SelectAll();
}
});
}
これを使用するには、TextBox を渡す関数を呼び出すだけで、煩雑な部分はすべて処理されます。すべてのテキスト ボックスを Form_Load イベントに接続することをお勧めします。この関数をフォームに配置することも、私と同じようにユーティリティ クラスのどこかに配置して、さらに再利用することもできます。
これは WPF/XAML TextBox では機能しました。
private bool initialEntry = true;
private void TextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
if (initialEntry)
{
e.Handled = true;
initialEntry = false;
TextBox.SelectAll();
}
}
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
TextBox.SelectAll();
initialEntry = true;
}
これは次のようなものです ンジェンリの人気のある答えですが、サブクラス化する必要がない方が簡単だと思います。
Private LastFocused As Control = Nothing
Private Sub TextBox1_Enter(sender As Object, e As System.EventArgs) Handles TextBox1.Enter, TextBox2.Enter, TextBox3.Enter
If MouseButtons = Windows.Forms.MouseButtons.None Then LastFocused = sender
End Sub
Private Sub TextBox1_Leave(sender As Object, e As System.EventArgs) Handles TextBox1.Leave, TextBox2.Leave, TextBox3.Leave
LastFocused = Nothing
End Sub
Private Sub TextBox1_MouseUp(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles TextBox1.MouseUp, TextBox2.MouseUp, TextBox3.MouseUp
With CType(sender, TextBox)
If LastFocused IsNot sender AndAlso .SelectionLength = 0 Then .SelectAll()
End With
LastFocused = sender
End Sub
SelectAll は私にとってはうまくいきませんでした。
これは機能します。
ActiveControl = textBox1;
textBox1->SelectionStart = 0;
textBox1->SelectionLength = textBox1->Text->Length;
さらに簡単な解決策を見つけました。
textBox をクリックしたときにすべてのテキストが選択されていることを確認するには、Click ハンドラーが Enter ハンドラーを呼び出すようにします。追加の変数は必要ありません。
例:
private void textBox1_Click(object sender, EventArgs e){
textBox1_Enter(sender, e);
}
private void textBox1_Enter(object sender, EventArgs e){
TextBox tb = ((TextBox)sender);
tb.SelectAll();
}
private bool _isSelected = false;
private void textBox_Validated(object sender, EventArgs e)
{
_isSelected = false;
}
private void textBox_MouseClick(object sender, MouseEventArgs e)
{
SelectAllText(textBox);
}
private void textBox_Enter(object sender, EventArgs e)
{
SelectAllText(textBox);
}
private void SelectAllText(TextBox text)
{
if (!_isSelected)
{
_isSelected = true;
textBox.SelectAll();
}
}
興味深いことに、DropDownStyle=Simple を指定した ComboBox は、あなたが探している動作をほぼ正確に実現していると思います。
(リストを表示しないようにコントロールの高さを下げ、さらに数ピクセル下げると、ComboBox と TextBox の間に実質的な違いはなくなります。)
単純にテキスト ボックスの MouseDown イベントを使用してみてはいかがでしょうか。私にとってはうまく機能し、追加のブール値は必要ありません。非常にクリーンでシンプルです。例:
private void textbox_MouseDown(object sender, MouseEventArgs e) {
if (textbox != null && !string.IsNullOrEmpty(textbox.Text))
{
textbox.SelectAll();
} }
MouseUp イベント内で SelectAll を呼び出したところ、うまくいきました。
private bool _tailTextBoxFirstClick = false;
private void textBox1_MouseUp(object sender, MouseEventArgs e)
{
if(_textBoxFirstClick)
textBox1.SelectAll();
_textBoxFirstClick = false;
}
private void textBox1_Leave(object sender, EventArgs e)
{
_textBoxFirstClick = true;
textBox1.Select(0, 0);
}
TextBox または MaskedTextBox からクラスを派生するだけです。
public class SMaskedTextBox : MaskedTextBox
{
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
this.SelectAll();
}
}
そしてそれをフォームに使用してください。
やってみました MSDN フォーラム「Windows フォーム全般」で提案されている解決策 これは単純に TextBox をサブクラス化するものですか?
実際、GotFocus は、あなたが興味を持っている正しいイベント (実際にはメッセージ) です。どのようにコントロールに到達しても、最終的にはこれを取得することになるからです。問題は、いつ SelectAll() を呼び出すかです。
これを試して:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.textBox1.GotFocus += new EventHandler(textBox1_GotFocus);
}
private delegate void SelectAllDelegate();
private IAsyncResult _selectAllar = null; //So we can clean up afterwards.
//Catch the input focus event
void textBox1_GotFocus(object sender, EventArgs e)
{
//We could have gotten here many ways (including mouse click)
//so there could be other messages queued up already that might change the selection.
//Don't call SelectAll here, since it might get undone by things such as positioning the cursor.
//Instead use BeginInvoke on the form to queue up a message
//to select all the text after everything caused by the current event is processed.
this._selectAllar = this.BeginInvoke(new SelectAllDelegate(this._SelectAll));
}
private void _SelectAll()
{
//Clean-up the BeginInvoke
if (this._selectAllar != null)
{
this.EndInvoke(this._selectAllar);
}
//Now select everything.
this.textBox1.SelectAll();
}
}
フォーム内のテキストボックスのグループの場合:
private System.Windows.Forms.TextBox lastFocus;
private void textBox_GotFocus(object sender, System.Windows.Forms.MouseEventArgs e)
{
TextBox senderTextBox = sender as TextBox;
if (lastFocus!=senderTextBox){
senderTextBox.SelectAll();
}
lastFocus = senderTextBox;
}
以下は機能するようです。Enter イベントはコントロールへのタブ移動を処理し、コントロールがクリックされたときに MouseDown が機能します。
private ########### void textBox1_Enter(object sender, EventArgs e)
{
textBox1.SelectAll();
}
private void textBox1_MouseDown(object sender, MouseEventArgs e)
{
if (textBox1.Focused)
textBox1.SelectAll();
}
これはすでに解決されているとは思いますが、実際にはかなり簡単だと思う提案があります。
マウスアップイベントでは、配置するだけで済みます。
if(textBox.SelectionLength = 0)
{
textBox.SelectAll();
}
VB.NET ではうまくいくようです (これが C# の質問であることはわかっています...)悲しいことに、仕事で VB を使用せざるを得なくなっています。私はこの問題を抱えていたので、ここに来ました...)
まだ問題は見つかっていません。クリックしてもすぐに選択されないという事実を除いて、私はそれに問題を抱えていました...
次の解決策は私にとってはうまくいきます。追加した OnKeyDown
そして OnKeyUp
TextBox テキストを常に選択した状態に保つためのイベント オーバーライド。
public class NumericTextBox : TextBox
{
private bool _focused;
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
if (MouseButtons == MouseButtons.None)
{
this.SelectAll();
_focused = true;
}
}
protected override void OnEnter(EventArgs e)
{
base.OnEnter(e);
if (MouseButtons == MouseButtons.None)
{
SelectAll();
_focused = true;
}
}
protected override void OnLeave(EventArgs e)
{
base.OnLeave(e);
_focused = false;
}
protected override void OnMouseUp(MouseEventArgs mevent)
{
base.OnMouseUp(mevent);
if (!_focused)
{
if (SelectionLength == 0)
SelectAll();
_focused = true;
}
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
if (SelectionLength == 0)
SelectAll();
_focused = true;
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (SelectionLength == 0)
SelectAll();
_focused = true;
}
}
コントロールを離れるときに選択を設定します。戻ってきたらそこにあります。Tab キーを押してフォーム内を移動し、コントロールに戻ると、すべてのテキストが選択されます。
マウスで移動すると、クリックした位置にキャレットが正しく配置されます。
private void maskedTextBox1_Leave(object sender, CancelEventArgs e)
{
maskedTextBox1.SelectAll();
}
答えは実際には、上記のすべてよりもはるかに単純です。たとえば (WPF の場合):
public void YourTextBox_MouseEnter(object sender, MouseEventArgs e)
{
YourTextBox.Focus();
YourTextBox.SelectAll();
}
もちろん、このコードをどのように使用したいのかはわかりませんが、ここで注目すべき主な部分は次のとおりです。最初に .Focus() を呼び出し、次に .SelectAll() を呼び出します。
非常に簡単な解決策:
private bool _focusing = false;
protected override void OnEnter( EventArgs e )
{
_focusing = true;
base.OnEnter( e );
}
protected override void OnMouseUp( MouseEventArgs mevent )
{
base.OnMouseUp( mevent );
if( _focusing )
{
this.SelectAll();
_focusing = false;
}
}
編集: 元の OP は、マウスダウン / テキスト選択 / マウスアップのシーケンスに特に懸念を持っていました。その場合、上記の単純な解決策ではテキストが部分的に選択されることになります。
これで問題は解決* するはずです (実際には WM_SETCURSOR をインターセプトします)。
protected override void WndProc( ref Message m )
{
if( m.Msg == 32 ) //WM_SETCURSOR=0x20
{
this.SelectAll(); // or your custom logic here
}
base.WndProc( ref m );
}
*実際には、次のシーケンスでは部分的なテキストの選択が行われますが、テキスト ボックス上にマウスを移動すると、すべてのテキストが再度選択されます。
マウスダウン / テキスト選択 / マウス移動テキストボックス / マウスアップ
Enterイベントとclickイベントでselectall()を使用するだけです
private void textBox1_Enter(object sender, EventArgs e)
{
textBox1.SelectAll();
}
private void textBox1_Click(object sender, EventArgs e)
{
textBox1.SelectAll();
}
マウスをクリックしてすぐに放さない場合、これが最適であると思います。
private bool SearchBoxInFocusAlready = false;
private void SearchBox_LostFocus(object sender, RoutedEventArgs e)
{
SearchBoxInFocusAlready = false;
}
private void SearchBox_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (e.ButtonState == MouseButtonState.Released && e.ChangedButton == MouseButton.Left &&
SearchBox.SelectionLength == 0 && SearchBoxInFocusAlready == false)
{
SearchBox.SelectAll();
}
SearchBoxInFocusAlready = true;
}
私のソリューションはかなり原始的ですが、私の目的にはうまく機能します
private async void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
if (sender is TextBox)
{
await Task.Delay(100);
(sender as TextBox).SelectAll();
}
}
これは.NET 2005ではうまくいきました -
' * if the mouse button is down, do not run the select all.
If MouseButtons = Windows.Forms.MouseButtons.Left Then
Exit Sub
End If
' * OTHERWISE INVOKE THE SELECT ALL AS DISCUSSED.