避免在鼠标点击时选择可编辑组合框中的所有文本。

huangapple go评论72阅读模式
英文:

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.

&lt;StackPanel&gt;
    &lt;TextBox  Margin=&quot;10&quot;
              Text=&quot;This is a test&quot; /&gt;
    &lt;ComboBox Name=&quot;combobox1&quot; Margin=&quot;10&quot;
              IsEditable=&quot;True&quot;
              Text=&quot;This is a test&quot;
               /&gt;
&lt;/StackPanel&gt;

答案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, 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) // 检查点是否在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 &amp;&amp; textBox != null
			&amp;&amp; textBox.IsAncestorOf(originalSource))
		{
			if (comboBox.IsDropDownOpen &amp;&amp; !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 &amp;&amp; !comboBox.IsKeyboardFocusWithin)
			{
				// If textBox is clicked, claim focus
				comboBox.Focus();
				e.Handled = true;   // Handle so that textbox won&#39;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&#39;re an editable combobox, forward focus to the TextBox element
	if (!e.Handled)
	{
		if (comboBox.IsEditable &amp;&amp; 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&lt;ComboBox&gt;
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Loaded += AssociatedObject_Loaded;
    }

    private void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e)
    {
        var tbx = FindVisualChild&lt;TextBox&gt;(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(() =&gt;
            {
                tbx.CaretIndex = tbx.GetCharacterIndexFromPoint(mousePos, true) + 1;
            });
        }
    }

    protected override void OnDetaching()
    {
        AssociatedObject.Loaded -= AssociatedObject_Loaded;
        base.OnDetaching();
    }

    private childItem FindVisualChild&lt;childItem&gt;(DependencyObject obj) where childItem : DependencyObject
    {
        for (int i = 0; i &lt; VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(obj, i);
            if (child != null &amp;&amp; child is childItem)
            {
                return (childItem)child;
            }
            else
            {
                childItem childOfChild = FindVisualChild&lt;childItem&gt;(child);
                if (childOfChild != null)
                {
                    return childOfChild;
                }
            }
        }
        return null;
    }
}

and use it:

&lt;ComboBox x:Name=&quot;combobox1&quot; Margin=&quot;10&quot; xmlns:i=&quot;clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity&quot;
    IsEditable=&quot;True&quot;
    Text=&quot;This is a test&quot;&gt;
    &lt;i:Interaction.Behaviors&gt;
        &lt;beh:NoSelectAllOnMouseClick/&gt;
    &lt;/i:Interaction.Behaviors&gt;
&lt;/ComboBox&gt;

huangapple
  • 本文由 发表于 2023年3月21日 01:54:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/75793695-4.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定