如果我从GotFocus事件处理程序中调用SelectAll,它对鼠标不起作用——鼠标一释放,选择就消失了。
编辑:人们喜欢唐纳利的回答,我将试着解释为什么我不像公认的答案那样喜欢它。
It is more complex, while the accepted answer does the same thing in a simpler way.
The usability of accepted answer is better. When you click in the middle of the text, text gets unselected when you release the mouse allowing you to start editing instantly, and if you still want to select all, just press the button again and this time it will not unselect on release. Following Donelle's recipe, if I click in the middle of text, I have to click second time to be able to edit. If I click somewhere within the text versus outside of the text, this most probably means I want to start editing instead of overwriting everything.
从这里开始:
在App.xaml.cs文件中注册全局事件处理程序:
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(TextBox),TextBox.GotFocusEvent,
new RoutedEventHandler(TextBox_GotFocus));
base.OnStartup(e);
}
那么处理程序就像这样简单:
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
(sender as TextBox).SelectAll();
}
下面是为方便您实现答案解决方案的Blend行为:
一个用于附加到单个文本框:
public class SelectAllTextOnFocusBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.GotKeyboardFocus += AssociatedObjectGotKeyboardFocus;
AssociatedObject.GotMouseCapture += AssociatedObjectGotMouseCapture;
AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObjectPreviewMouseLeftButtonDown;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.GotKeyboardFocus -= AssociatedObjectGotKeyboardFocus;
AssociatedObject.GotMouseCapture -= AssociatedObjectGotMouseCapture;
AssociatedObject.PreviewMouseLeftButtonDown -= AssociatedObjectPreviewMouseLeftButtonDown;
}
private void AssociatedObjectGotKeyboardFocus(object sender,
System.Windows.Input.KeyboardFocusChangedEventArgs e)
{
AssociatedObject.SelectAll();
}
private void AssociatedObjectGotMouseCapture(object sender,
System.Windows.Input.MouseEventArgs e)
{
AssociatedObject.SelectAll();
}
private void AssociatedObjectPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if(!AssociatedObject.IsKeyboardFocusWithin)
{
AssociatedObject.Focus();
e.Handled = true;
}
}
}
一个用于附加到包含多个文本框的容器的根:
public class SelectAllTextOnFocusMultiBehavior : Behavior<UIElement>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.GotKeyboardFocus += HandleKeyboardFocus;
AssociatedObject.GotMouseCapture += HandleMouseCapture;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.GotKeyboardFocus -= HandleKeyboardFocus;
AssociatedObject.GotMouseCapture -= HandleMouseCapture;
}
private static void HandleKeyboardFocus(object sender,
System.Windows.Input.KeyboardFocusChangedEventArgs e)
{
var txt = e.NewFocus as TextBox;
if (txt != null)
txt.SelectAll();
}
private static void HandleMouseCapture(object sender,
System.Windows.Input.MouseEventArgs e)
{
var txt = e.OriginalSource as TextBox;
if (txt != null)
txt.SelectAll();
}
}
我认为这很有效:
private void ValueText_GotFocus(object sender, RoutedEventArgs e)
{
TextBox tb = (TextBox)e.OriginalSource;
tb.Dispatcher.BeginInvoke(
new Action(delegate
{
tb.SelectAll();
}), System.Windows.Threading.DispatcherPriority.Input);
}
如果你想实现它作为一个扩展方法:
public static void SelectAllText(this System.Windows.Controls.TextBox tb)
{
tb.Dispatcher.BeginInvoke(
new Action(delegate
{
tb.SelectAll();
}), System.Windows.Threading.DispatcherPriority.Input);
}
在GotFocus事件中:
private void ValueText_GotFocus(object sender, RoutedEventArgs e)
{
TextBox tb = (TextBox)e.OriginalSource;
tb.SelectAllText();
}
我发现上面的解决方案是因为几个月前我正在寻找一种方法来将焦点设置到给定的UIElement上。我在某处发现了下面的代码(特此给予),它工作得很好。尽管它与OP的问题没有直接关系,但我还是发布了它,因为它演示了使用Dispatcher处理UIElement的相同模式。
// Sets focus to uiElement
public static void DelayedFocus(this UIElement uiElement)
{
uiElement.Dispatcher.BeginInvoke(
new Action(delegate
{
uiElement.Focusable = true;
uiElement.Focus();
Keyboard.Focus(uiElement);
}),
DispatcherPriority.Render);
}
我使用了Nils的答案,但将其转换为更灵活的答案。
public enum SelectAllMode
{
/// <summary>
/// On first focus, it selects all then leave off textbox and doesn't check again
/// </summary>
OnFirstFocusThenLeaveOff = 0,
/// <summary>
/// On first focus, it selects all then never selects
/// </summary>
OnFirstFocusThenNever = 1,
/// <summary>
/// Selects all on every focus
/// </summary>
OnEveryFocus = 2,
/// <summary>
/// Never selects text (WPF's default attitude)
/// </summary>
Never = 4,
}
public partial class TextBox : DependencyObject
{
public static readonly DependencyProperty SelectAllModeProperty = DependencyProperty.RegisterAttached(
"SelectAllMode",
typeof(SelectAllMode?),
typeof(TextBox),
new PropertyMetadata(SelectAllModePropertyChanged));
private static void SelectAllModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is System.Windows.Controls.TextBox)
{
var textBox = d as System.Windows.Controls.TextBox;
if (e.NewValue != null)
{
textBox.GotKeyboardFocus += OnKeyboardFocusSelectText;
textBox.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
}
else
{
textBox.GotKeyboardFocus -= OnKeyboardFocusSelectText;
textBox.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDown;
}
}
}
private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DependencyObject dependencyObject = GetParentFromVisualTree(e.OriginalSource);
if (dependencyObject == null)
return;
var textBox = (System.Windows.Controls.TextBox)dependencyObject;
if (!textBox.IsKeyboardFocusWithin)
{
textBox.Focus();
e.Handled = true;
}
}
private static DependencyObject GetParentFromVisualTree(object source)
{
DependencyObject parent = source as UIElement;
while (parent != null && !(parent is System.Windows.Controls.TextBox))
{
parent = VisualTreeHelper.GetParent(parent);
}
return parent;
}
private static void OnKeyboardFocusSelectText(object sender, KeyboardFocusChangedEventArgs e)
{
var textBox = e.OriginalSource as System.Windows.Controls.TextBox;
if (textBox == null) return;
var selectAllMode = GetSelectAllMode(textBox);
if (selectAllMode == SelectAllMode.Never)
{
textBox.SelectionStart = 0;
textBox.SelectionLength = 0;
}
else
textBox.SelectAll();
if (selectAllMode == SelectAllMode.OnFirstFocusThenNever)
SetSelectAllMode(textBox, SelectAllMode.Never);
else if (selectAllMode == SelectAllMode.OnFirstFocusThenLeaveOff)
SetSelectAllMode(textBox, null);
}
[AttachedPropertyBrowsableForChildrenAttribute(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(System.Windows.Controls.TextBox))]
public static SelectAllMode? GetSelectAllMode(DependencyObject @object)
{
return (SelectAllMode)@object.GetValue(SelectAllModeProperty);
}
public static void SetSelectAllMode(DependencyObject @object, SelectAllMode? value)
{
@object.SetValue(SelectAllModeProperty, value);
}
}
在XAML中,你可以这样使用:
<!-- On first focus, it selects all then leave off textbox and doesn't check again -->
<TextBox attprop:TextBox.SelectAllMode="OnFirstFocusThenLeaveOff" />
<!-- On first focus, it selects all then never selects -->
<TextBox attprop:TextBox.SelectAllMode="OnFirstFocusThenNever" />
<!-- Selects all on every focus -->
<TextBox attprop:TextBox.SelectAllMode="OnEveryFocus" />
<!-- Never selects text (WPF's default attitude) -->
<TextBox attprop:TextBox.SelectAllMode="Never" />
我用附加行为解决了这个问题,而不是谢尔盖回答中的表达式行为。这意味着我不需要依赖Blend SDK中的System.Windows.Interactivity:
public class TextBoxBehavior
{
public static bool GetSelectAllTextOnFocus(TextBox textBox)
{
return (bool)textBox.GetValue(SelectAllTextOnFocusProperty);
}
public static void SetSelectAllTextOnFocus(TextBox textBox, bool value)
{
textBox.SetValue(SelectAllTextOnFocusProperty, value);
}
public static readonly DependencyProperty SelectAllTextOnFocusProperty =
DependencyProperty.RegisterAttached(
"SelectAllTextOnFocus",
typeof (bool),
typeof (TextBoxBehavior),
new UIPropertyMetadata(false, OnSelectAllTextOnFocusChanged));
private static void OnSelectAllTextOnFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBox = d as TextBox;
if (textBox == null) return;
if (e.NewValue is bool == false) return;
if ((bool) e.NewValue)
{
textBox.GotFocus += SelectAll;
textBox.PreviewMouseDown += IgnoreMouseButton;
}
else
{
textBox.GotFocus -= SelectAll;
textBox.PreviewMouseDown -= IgnoreMouseButton;
}
}
private static void SelectAll(object sender, RoutedEventArgs e)
{
var textBox = e.OriginalSource as TextBox;
if (textBox == null) return;
textBox.SelectAll();
}
private static void IgnoreMouseButton(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var textBox = sender as TextBox;
if (textBox == null || (!textBox.IsReadOnly && textBox.IsKeyboardFocusWithin)) return;
e.Handled = true;
textBox.Focus();
}
}
然后你可以像这样在XAML中使用它:
<TextBox Text="Some Text" behaviors:TextBoxBehavior.SelectAllTextOnFocus="True"/>
我在这里写过博客。