c# WPF - Как распознать щелчок мыши вне элемента управления, не блокируя нажатие других элементов управления - PullRequest
0 голосов
/ 14 марта 2020

Я сейчас читаю много тем и пробовал и так далее. Я не понимаю.

Вопрос При использовании MouseCapture другие элементы управления не перемещаются. Невозможно что-то щелкнуть. Подсветка не происходит при наведении мыши. MouseCaption блокирует это. Нажав дважды, необходимо. Как избежать этого?

В основном Я создал пользовательский autocompletebox, который состоит из текстового поля для свободного ввода и раскрывающегося списка в виде текстового блока, содержащего предложенный элемент-результат при заданном вводе. Это очень похоже на стандартный ComboBox.

Из Combobox мы знаем, что когда он развернут и один щелчок где-то еще, свернутый раскрывающийся список.

Я хочу точно такое же поведение, как и в комбобоксе.

Поскольку я не первый, кто спрашивает об этом, я попробовал несколько вещей, но не получил их полностью работоспособными.

То, что я все еще пытался, и у меня не получилось.

  1. Добавление события OnLostFocus в текстовое поле не распознает щелчки мыши по элементам без фокуса.
  2. Использование Mous.Caption(this) с PreviewMouseLeftButtonDown для получения любых щелчков мыши по любому место на окне.
    • Да, это работает! Я могу свернуть раскрывающийся список, снова снять мышь.
    • Но: заголовок мыши не позволяет мне щелкнуть другой элемент UIElement. Флажки и радиобоксы не будут переключаться. Просто наведите курсор мыши на флажок или что-то еще, этот элемент больше не выделяется. Вместо этого мне теперь нужно дважды щелкнуть, чтобы проверить текстовое поле.
    • Я не могу понять, как решить эту проблему.
  3. Кроме того, не работало то, что когда событие захвата мыши было запущено, я не могу понять, где был сделан щелчок.
    • source, а также e.OriginalSource равны моему пользовательскому элементу управления
    • Возможно получить позицию мыши. Но не нашел, чтобы получить позицию моего элемента управления, связанную с позицией мыши. любые свойства возвращаемого элемента управления NaN.
  4. Сначала я не смог распознать разницу между PreviewMouseLeftButtonDown и MouseLeftButtonDown.
    • Я думал, что первый, при непосредственном отпускании захвата мыши, будет вызывать событие mouseclick до его первоначального назначения без захвата мыши больше. Это не так.
    • Я получил его с помощью Hittest. Это так?

Какой-то код

XAML AutoCompleteBox

<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;
}

Редактировать

К сожалению, Popup не является (afaik) членом VisualTree. Таким образом, самый популярный для Popup не работает. Поэтому я попытался проверить положение Popup с помощью Mouse-position.

Метод 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));

Это ошибка?

1 Ответ

0 голосов
/ 15 марта 2020

Я не сдался и сделал несколько шагов вперед.

Ответы на вопросы:

1) Либо сделать окно фокусируемым, либо даже не делайте так.

2) Использование правильного CaptureMode с помощью Mouse.Capture(this, CaptureMode.SubTree); по крайней мере решает, что DropDown продолжает взаимодействовать с наведением мыши и т. д. c. (конечно, потому что это дочерний элемент контроля захвата).

Остальная часть Окна продолжает блокироваться захватом мыши. Можно просто понять, что это также нормальное поведение для стандартного 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)

Редактировать) Нет ответа на этот вопрос. Но это работает любым способом.

...