英文:
Avoid select all text in editable ComboBox on mouse click
问题
当单击TextBox
时,光标会放置在我点击的位置。
当单击可编辑的ComboBox
时,整个文本会被选中,然后我必须再次单击(几毫秒后)以放置光标。
我如何使ComboBox
以与TextBox
相同的方式响应。
<StackPanel>
<TextBox Margin="10" Text="This is a test" />
<ComboBox Name="combobox1" Margin="10" IsEditable="True" Text="This is a test" />
</StackPanel>
英文:
When clicking in a TextBox
, the cursor is placed in the position where I click.
When clicking in an editable ComboBox
, the entire text is selected, and I have to click again (after a few milliseconds), to place the cursor.
How can I make the ComboBox
react the same way as the TextBox
.
<StackPanel>
<TextBox Margin="10"
Text="This is a test" />
<ComboBox Name="combobox1" Margin="10"
IsEditable="True"
Text="This is a test"
/>
</StackPanel>
答案1
得分: 0
调用 TextBox.SelectAll()
是 ComboBox
中的一个预期实现:
private static void OnPreviewMouseButtonDown(object sender, MouseButtonEventArgs e)
{
ComboBox comboBox = (ComboBox)sender;
if (comboBox.IsEditable)
{
Visual originalSource = e.OriginalSource as Visual;
Visual textBox = comboBox.EditableTextBoxSite;
if (originalSource != null && textBox != null
&& textBox.IsAncestorOf(originalSource))
{
if (comboBox.IsDropDownOpen && !comboBox.StaysOpenOnEdit)
{
// 当ComboBox不可编辑时,单击ComboBox之外的任何地方都将关闭它。
// 当ComboBox可编辑时,单击文本框也应该关闭ComboBox。
comboBox.Close();
}
else if (!comboBox.IsContextMenuOpen && !comboBox.IsKeyboardFocusWithin)
{
// 如果单击了文本框,要求获得焦点
comboBox.Focus();
e.Handled = true; // 处理以防止文本框尝试更新光标位置
}
}
}
}
private static void OnGotFocus(object sender, RoutedEventArgs e)
{
// 当ComboBox获得逻辑焦点时,选择其中的文本。
ComboBox comboBox = (ComboBox)sender;
// 如果我们是可编辑的ComboBox,请将焦点转发到TextBox元素
if (!e.Handled)
{
if (comboBox.IsEditable && comboBox.EditableTextBoxSite != null)
{
if (e.OriginalSource == comboBox)
{
comboBox.EditableTextBoxSite.Focus();
e.Handled = true;
}
else if (e.OriginalSource == comboBox.EditableTextBoxSite)
{
comboBox.EditableTextBoxSite.SelectAll();
}
}
}
}
这种方式阻止了MouseDown事件,并在鼠标单击时触发了 ComboBox.GotFocus
-> TextBox.GotFocus
。
要达到与 TextBox
相同的行为,需要订阅 TextBox.GotFocus
事件,可以在 ComboBox.Loaded
事件处理程序中执行,然后设置 TextBox.CaretIndex
到正确的位置。所有这些可以封装成一个行为:
public class NoSelectAllOnMouseClick : Behavior<ComboBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += AssociatedObject_Loaded;
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
var tbx = FindVisualChild<TextBox>(AssociatedObject);
if (tbx != null)
{
tbx.GotFocus += Tbx_GotFocus;
}
}
private async void Tbx_GotFocus(object sender, RoutedEventArgs e)
{
var tbx = sender as TextBox;
if (tbx == null)
{
return;
}
var mousePos = Mouse.GetPosition(tbx);
if (tbx.InputHitTest(mousePos) != null) // 检查点是否在TextBox中
{
await tbx.Dispatcher.InvokeAsync(() =>
{
tbx.CaretIndex = tbx.GetCharacterIndexFromPoint(mousePos, true) + 1;
});
}
}
protected override void OnDetaching()
{
AssociatedObject.Loaded -= AssociatedObject_Loaded;
base.OnDetaching();
}
private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
{
return (childItem)child;
}
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
{
return childOfChild;
}
}
}
return null;
}
}
然后在ComboBox中使用它:
<ComboBox x:Name="combobox1" Margin="10" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
IsEditable="True"
Text="This is a test">
<i:Interaction.Behaviors>
<beh:NoSelectAllOnMouseClick/>
</i:Interaction.Behaviors>
</ComboBox>
英文:
Calling the TextBox.SellectAll()
is an intended implementation in ComboBox
:
private static void OnPreviewMouseButtonDown(object sender, MouseButtonEventArgs e)
{
ComboBox comboBox = (ComboBox)sender;
if (comboBox.IsEditable)
{
Visual originalSource = e.OriginalSource as Visual;
Visual textBox = comboBox.EditableTextBoxSite;
if (originalSource != null && textBox != null
&& textBox.IsAncestorOf(originalSource))
{
if (comboBox.IsDropDownOpen && !comboBox.StaysOpenOnEdit)
{
// When combobox is not editable, clicks anywhere outside
// the combobox will close it. When the combobox is editable
// then clicking the text box should close the combobox as well.
comboBox.Close();
}
else if (!comboBox.IsContextMenuOpen && !comboBox.IsKeyboardFocusWithin)
{
// If textBox is clicked, claim focus
comboBox.Focus();
e.Handled = true; // Handle so that textbox won't try to update cursor position
}
}
}
}
private static void OnGotFocus(object sender, RoutedEventArgs e)
{
// When ComboBox gets logical focus, select the text inside us.
ComboBox comboBox = (ComboBox)sender;
// If we're an editable combobox, forward focus to the TextBox element
if (!e.Handled)
{
if (comboBox.IsEditable && comboBox.EditableTextBoxSite != null)
{
if (e.OriginalSource == comboBox)
{
comboBox.EditableTextBoxSite.Focus();
e.Handled = true;
}
else if (e.OriginalSource == comboBox.EditableTextBoxSite)
{
comboBox.EditableTextBoxSite.SelectAll();
}
}
}
}
Such a way MouseDown event being stopped and ComboBox.GotFocus
-> TextBox.GotFocus
being initiated on mouse click.
In order to reach the behavior as by TextBox
you need to subscribe for TextBox.GotFocus
, you can do it in event handler for ComboBox.Loaded
, and post setting the TextBox.CaretIndex
to the right position.
All this you can pack to a behavior:
public class NoSelectAllOnMouseClick : Behavior<ComboBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += AssociatedObject_Loaded;
}
private void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
var tbx = FindVisualChild<TextBox>(AssociatedObject);
if (tbx != null)
{
tbx.GotFocus += Tbx_GotFocus;
}
}
private async void Tbx_GotFocus(object sender, RoutedEventArgs e)
{
var tbx = sender as TextBox;
if (tbx == null)
{
return;
}
var mousePos = Mouse.GetPosition(tbx);
if (tbx.InputHitTest(mousePos) != null) //Check point is in the TextBox
{
await tbx.Dispatcher.InvokeAsync(() =>
{
tbx.CaretIndex = tbx.GetCharacterIndexFromPoint(mousePos, true) + 1;
});
}
}
protected override void OnDetaching()
{
AssociatedObject.Loaded -= AssociatedObject_Loaded;
base.OnDetaching();
}
private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
{
return (childItem)child;
}
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
{
return childOfChild;
}
}
}
return null;
}
}
and use it:
<ComboBox x:Name="combobox1" Margin="10" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
IsEditable="True"
Text="This is a test">
<i:Interaction.Behaviors>
<beh:NoSelectAllOnMouseClick/>
</i:Interaction.Behaviors>
</ComboBox>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论