0

我现在阅读了很多线程并尝试了等等。我不明白。

问题 使用 MouseCapture 时,任何其他控件都没有动作。无法单击某些内容。鼠标悬停时没有突出显示。MouseCaption 阻止了这一点。单击两次是必要的。如何避免这种情况?

基本上 ,我创建了一个自定义自动完成框,它由一个用于自由输入的文本框和一个作为文本块的下拉列表组成,其中包含给定输入的建议结果元素。这很像一个标准的组合框。

从 Combobox 我们知道,当它展开并单击其他地方时,下拉列表会折叠。

我想要与组合框使用的完全相同的行为。

因为我不是第一个这样问的人,所以我尝试了几件事,但没有让它们完全正常工作。

我仍然尝试过但失败了。

  1. 向文本框添加OnLostFocus事件无法识别对不可聚焦元素的任何鼠标单击。
  2. 使用Mous.Caption(this)withPreviewMouseLeftButtonDown接收在窗口上任何位置的任何鼠标点击。
    • 是的,这行得通!我可以折叠我的下拉菜单,再次取消捕获鼠标。
    • 但是:鼠标标题阻止我点击其他 UIElement。复选框和 RadioBoxes 不会被切换。只需将鼠标悬停在复选框或其他任何内容上,就不会再突出显示该元素。相反,我现在需要单击两次以检查文本框。
    • 我想不通,如何解决。
  3. 还有什么不起作用的是,当鼠标捕获事件被触发时,我无法弄清楚点击是在哪里进行的。
    • source以及e.OriginalSource等于我的自定义控件
    • 获取鼠标位置可能是一种选择。但是没有找到我的控件与鼠标位置相关的位置。控件上的任何属性都返回 NaN。
  4. 起初我无法识别PreviewMouseLeftButtonDown和之间的任何区别MouseLeftButtonDown
    • 我认为第一个,当直接释放鼠标捕获时,会将 mouseclick 事件触发到其原始目标,而不再捕获鼠标。它没有。
    • 我是通过使用 Hittest 得到的。是这样吗?

一些代码

自动完成框的 XAML

<UserControl x:Class="MyProject.Wpf.Application.Control.AutoCompleteBoxControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             x:Name="AutoCompleteBox"
             PreviewMouseLeftButtonDown="AutoComplete_MouseLeftButtonDown">
    <Grid>
        <StackPanel>
            <TextBox Name="AutoCompleteTextBox" Text="{Binding ElementName=AutoCompleteBox, Path=TextBoxText, Mode=TwoWay}" Height="{Binding ElementName=AutoCompleteBox, Path=TextBoxHeight}" Width="{Binding ElementName=AutoCompleteBox, Path=TextBoxWidth}" Padding="5, 3, 5, 3" KeyUp="AutoCompleteTextBoxControl_KeyUp" LostKeyboardFocus="AutoCompleteTextBox_OnLostKeyboardFocus"/>
            <Popup Name="ResultStackPopup" IsOpen="True" PlacementTarget="{Binding ElementName=AutoCompleteTextBox}" Placement="Custom">
                <Border Name="ResultStackBorder" Width="{Binding ElementName=AutoCompleteBox, Path=SuggestionListWidth}" Height="{Binding ElementName=AutoCompleteBox, Path=SuggestionListHeight}" BorderBrush="Black" BorderThickness="1" Visibility="Collapsed" Background="White" Margin="1,0,1,0" HorizontalAlignment="Left">
                    <ScrollViewer VerticalScrollBarVisibility="Auto">
                        <StackPanel Name="ResultStack"></StackPanel>
                    </ScrollViewer>
                </Border>
            </Popup>
        </StackPanel>
    </Grid>
</UserControl>

后面的相关代码:

//Whenever the dropdown is expanded, the mouse caption is started:
this.CaptureMouse();

鼠标按下事件:

private void AutoComplete_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (this.IsMouseCaptured)
        {
            if (!this.IsMouseInBounds())
            {
                //Release MouseCaption
                this.ReleaseMouseCapture();

                //Collapse SuggestionList
                var border = (this.ResultStack.Parent as ScrollViewer)?.Parent as Border;
                if (border != null)
                {
                    border.Visibility = Visibility.Collapsed;
                }
            }
        }
    }

private bool IsMouseInBounds()
{
    Point point = Mouse.GetPosition(this);

    // Perform the hit test against a given portion of the visual object tree.
    HitTestResult result = VisualTreeHelper.HitTest(this, point);

    if (result != null)
    {
        return true;
    }

    return false;
}

编辑

不幸的是,弹出窗口(afaik)不是visualTree的成员。所以 Popup 的 hittest 不起作用。所以我试图让弹出窗口的位置来检查鼠标位置。

正如大家所说,TransformToAncestor 方法将被使用。但这似乎无法正常工作:

以下三个调用确实返回完全相同的点:

    Window parentWindow = Window.GetWindow(this);
    Point relativePointThis = this.TransformToAncestor(parentWindow).Transform(new Point(0, 0));
    Point relativePointPopup = this.ResultStackPopup.TransformToAncestor(parentWindow).Transform(new Point(0, 0));
    Point relativePointBorder = this.ResultStackBorder.TransformToAncestor(parentWindow).Transform(new Point(0, 0));

这是一个错误吗?

4

1 回答 1

0

我没有放弃,向前走了几步。

答案:

1) 要么使窗口可聚焦,甚至不这样做。

2) 使用正确的 CaptureModeMouse.Capture(this, CaptureMode.SubTree);至少可以解决 DropDown 与鼠标悬停等交互(当然,因为它是捕获控件的子项)。

窗口的其余部分一直被鼠标捕获阻止。人们可能刚刚意识到,这也是标准 ComboBox 的正常行为。所以一个人可以忍受这一点。

3) 通过鼠标位置获取入站点击检查可以使用以下代码处理。请注意,鼠标位置是相对于给定元素给出的。在这种情况下,AutoCompleteBox 是通过this. 所以左上角将是{0.0, 0.0}

private bool IsMouseInBounds()
{
    //Get MousePosition relative to the AutoCompleteBox
    Point point = Mouse.GetPosition(this);

    //Actual Width and Heigth of AutoCompleteBox
    var widthAuto = this.ActualWidth;
    var heightAuto = this.ActualHeight;
    var upLeftXAuto = 0.0;
    var upLeftYAuto = 0.0;
    var downRightXAuto = upLeftXAuto + widthAuto;
    var downRightYAuto = upLeftYAuto + heightAuto;

    //Actual Width and Height of DropDown
    var widthDropDown = this.ResultStackBorder.ActualWidth;
    var heightDropDown = this.ResultStackBorder.ActualHeight;
    double upLeftXDropDown;
    double upLeftYDropDown;

    //Actual Position of DropDown (may be aligned right or center)
    CalculateAlignmentForPopUp(out upLeftXDropDown, out upLeftYDropDown, 0.0, 0.0, widthAuto, heightAuto, widthDropDown);

    var downRightXDropDown = upLeftXDropDown + widthDropDown;
    var downRightYDropDown = upLeftYDropDown + heightDropDown;

    //Calc IsInbound
    return (
             point.X >= upLeftXAuto && point.X <= downRightXAuto &&
             point.Y >= upLeftYAuto && point.Y <= downRightYAuto
         ) || (
             point.X >= upLeftXDropDown && point.X <= downRightXDropDown &&
             point.Y >= upLeftYDropDown && point.Y <= downRightYDropDown
         );
}



private void CalculateAlignmentForPopUp(out double newX, out double newY, double xAuto, double yAuto, double widthAuto, double heightAuto, double widthPopup)
{
    newX = 0.0;
    newY = 0.0;

    switch (this.ListHorizontalAlignment)
    {
        case "Right":
            newX = xAuto + widthAuto - widthPopup;
            newY = yAuto + heightAuto;
            break;
        case "Center":
            newX = xAuto + widthAuto / 2 - widthPopup / 2;
            newY = yAuto + heightAuto;
            break;
        default:
            newY = yAuto + heightAuto;
            break;
    }
}

4) 没有答案。但是当使用 3) 中的解决方案时,这现在已经过时了

编辑) 对此没有答案。但它正在以任何方式工作。

于 2020-03-15T11:13:45.220 回答