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

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

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, 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-2.html
匿名

发表评论

匿名网友

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

确定