他の解決策の問題のいくつかを解決する試みはここにあります:
- 切り取り/コピー/貼り付けの右クリックコンテキストメニューを使用すると、すべてを選択しなかった場合でも、すべてのテキストが選択されます。
- 右クリックのコンテキストメニューから戻ると、すべてのテキストが常に選択されます。
- Alt+ Tabでアプリケーションに戻ると、すべてのテキストが常に選択されます。
- 最初のクリックでテキストの一部のみを選択しようとすると、すべてが選択されます(たとえば、Google Chromeのアドレスバーとは異なります)。
私が書いたコードは構成可能です。:次の3つの読み取り専用フィールドを設定することで起こるべきである何の行動のすべての行動を選択する上で選択することができSelectOnKeybourdFocus
、SelectOnMouseLeftClick
、SelectOnMouseRightClick
。
このソリューションの欠点は、より複雑で静的な状態が保存されることです。これは、TextBox
コントロールのデフォルトの動作との醜い闘いのようです。それでも機能し、すべてのコードはAttached Propertyコンテナクラスに隠されています。
public static class TextBoxExtensions
{
// Configuration fields to choose on what actions the select all behavior should occur.
static readonly bool SelectOnKeybourdFocus = true;
static readonly bool SelectOnMouseLeftClick = true;
static readonly bool SelectOnMouseRightClick = true;
// Remembers a right click context menu that is opened
static ContextMenu ContextMenu = null;
// Remembers if the first action on the TextBox is mouse down
static bool FirstActionIsMouseDown = false;
public static readonly DependencyProperty SelectOnFocusProperty =
DependencyProperty.RegisterAttached("SelectOnFocus", typeof(bool), typeof(TextBoxExtensions), new PropertyMetadata(false, new PropertyChangedCallback(OnSelectOnFocusChanged)));
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static bool GetSelectOnFocus(DependencyObject obj)
{
return (bool)obj.GetValue(SelectOnFocusProperty);
}
public static void SetSelectOnFocus(DependencyObject obj, bool value)
{
obj.SetValue(SelectOnFocusProperty, value);
}
private static void OnSelectOnFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is TextBox textBox)) return;
if (GetSelectOnFocus(textBox))
{
// Register events
textBox.PreviewMouseDown += TextBox_PreviewMouseDown;
textBox.PreviewMouseUp += TextBox_PreviewMouseUp;
textBox.GotKeyboardFocus += TextBox_GotKeyboardFocus;
textBox.LostKeyboardFocus += TextBox_LostKeyboardFocus;
}
else
{
// Unregister events
textBox.PreviewMouseDown -= TextBox_PreviewMouseDown;
textBox.PreviewMouseUp -= TextBox_PreviewMouseUp;
textBox.GotKeyboardFocus -= TextBox_GotKeyboardFocus;
textBox.LostKeyboardFocus -= TextBox_LostKeyboardFocus;
}
}
private static void TextBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (!(sender is TextBox textBox)) return;
// If mouse clicked and focus was not in text box, remember this is the first click.
// This will enable to prevent select all when the text box gets the keyboard focus
// right after the mouse down event.
if (!textBox.IsKeyboardFocusWithin)
{
FirstActionIsMouseDown = true;
}
}
private static void TextBox_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (!(sender is TextBox textBox)) return;
// Select all only if:
// 1) SelectOnMouseLeftClick/SelectOnMouseRightClick is true and left/right button was clicked
// 3) This is the first click
// 4) No text is selected
if (((SelectOnMouseLeftClick && e.ChangedButton == MouseButton.Left) ||
(SelectOnMouseRightClick && e.ChangedButton == MouseButton.Right)) &&
FirstActionIsMouseDown &&
string.IsNullOrEmpty(textBox.SelectedText))
{
textBox.SelectAll();
}
// It is not the first click
FirstActionIsMouseDown = false;
}
private static void TextBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (!(sender is TextBox textBox)) return;
// Select all only if:
// 1) SelectOnKeybourdFocus is true
// 2) Focus was not previously out of the application (e.OldFocus != null)
// 3) The mouse was pressed down for the first after on the text box
// 4) Focus was not previously in the context menu
if (SelectOnKeybourdFocus &&
e.OldFocus != null &&
!FirstActionIsMouseDown &&
!IsObjectInObjectTree(e.OldFocus as DependencyObject, ContextMenu))
{
textBox.SelectAll();
}
// Forget ContextMenu
ContextMenu = null;
}
private static void TextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (!(sender is TextBox textBox)) return;
// Remember ContextMenu (if opened)
ContextMenu = e.NewFocus as ContextMenu;
// Forget selection when focus is lost if:
// 1) Focus is still in the application
// 2) The context menu was not opened
if (e.NewFocus != null
&& ContextMenu == null)
{
textBox.SelectionLength = 0;
}
}
// Helper function to look if a DependencyObject is contained in the visual tree of another object
private static bool IsObjectInObjectTree(DependencyObject searchInObject, DependencyObject compireToObject)
{
while (searchInObject != null && searchInObject != compireToObject)
{
searchInObject = VisualTreeHelper.GetParent(searchInObject);
}
return searchInObject != null;
}
}
添付プロパティをにアタッチするにはTextBox
、添付プロパティのxml名前空間(xmlns
)を追加して、次のように使用するだけです。
<TextBox attachedprop:TextBoxExtensions.SelectOnFocus="True"/>
このソリューションに関するいくつかのメモ:
- マウスダウンイベントのデフォルトの動作をオーバーライドし、最初のクリックでテキストの一部のみを選択できるようにするには、マウスアップイベントですべてのテキストを選択します。
- 私は
TextBox
フォーカスが失われた後、その選択を記憶するという事実に対処しなければなりませんでした。私は実際にこの動作をオーバーライドしました。
- マウスボタンを押すことが
TextBox
(FirstActionIsMouseDown
静的フィールド)の最初のアクションかどうかを覚えておく必要がありました。
- 右クリックで開いたコンテキストメニュー(
ContextMenu
静的フィールド)を覚えておく必要がありました。
私が見つけた唯一の副作用はいつSelectOnMouseRightClick
trueかです。右クリックコンテキストメニューを開いたときにブランクが右クリックしても、TextBox
「すべて選択」されない場合があります。