Обнаружение нажатия на и вне PopupButton в WPF - PullRequest
0 голосов
/ 13 марта 2020

У меня проблема с PopupButton в WPF. Когда я нажимаю его один раз, появляется всплывающее окно с некоторыми элементами для выбора (ElementTreeControl). После того, как я нажимаю ту же кнопку Popup, она должна закрыться - но она закрывается и открывается снова. Я решил это, и это работало, но когда я щелкаю за пределами этого элемента управления, и он закрывается (StayOpen = false), у меня возникает проблема с повторным открытием снова - нужно нажать PopupButton два раза.

Существует ли какое-либо свойство или обходной путь, позволяющий определить, когда был нажат элемент управления и когда он находился вне его?

Я хочу, чтобы PopupButton был:

При закрытии:

  • открывается одним щелчком мыши

При открытии:

  • закрывается при втором щелчке по popupButton
  • закрывается при щелчке по внешней области

Действие по нажатию всплывающей кнопки:

private Popup rtWindowBoundsPopup;

private async void ButtonClickAsync(object pmSender, RoutedEventArgs pmE)
{
    if (pmSender is PopupButton lcButton)
    {
        if (lcButton.Tag is StarElement lcStarElement)
        {
            if (!string.IsNullOrEmpty(DatabaseName))
            {
                lcStarElement = await rtStarHelper.AssureMetaData(lcStarElement, DatabaseName);
            }
            else
            {
                StarDim lcStarDim = rtStarHelper.GetDimFromDimId(lcStarElement.Dim.DimId, true);
                lcStarElement.Dim = await rtStarHelper.AssureMetaData(lcStarDim);
            }
            ShowTreeViewPopup(lcButton, lcStarElement);
        }
    }
}


private void ShowTreeViewPopup(PopupButton pmButton, StarElement pmStarElement)
 {
     ElementTreeControl lcElementTreeControl;
     if (rtWindowBoundsPopup == null)
     {
         rtWindowBoundsPopup = new Popup();// { IsLightDismissEnabled = true };
        rtWindowBoundsPopup.Opened += WindowBoundsPopupOpened;
     }

     if (rtWindowBoundsPopup.Child is ElementTreeControl lcTreeControl)
     {
         lcElementTreeControl = lcTreeControl;
         lcElementTreeControl.HideAddionalCols();
     }
     else
     {
         lcElementTreeControl = new ElementTreeControl { Tag = pmButton };
         rtWindowBoundsPopup.Child = lcElementTreeControl;
         lcElementTreeControl.SelectionChanged += PopupListBoxSelectionChangedAsync;
     }

     Point lcPoint = UiHelper.CalcOffsets(pmButton);
     Rect lcCurrentwindowbounds = CurrentWindow.RestoreBounds;
     if (lcPoint.Y < lcCurrentwindowbounds.Height / 2)
     {
         lcElementTreeControl.MaxHeight = lcCurrentwindowbounds.Height - lcPoint.Y - pmButton.ActualHeight;
     }
     else
     {
         lcElementTreeControl.MaxHeight = lcPoint.Y - pmButton.ActualHeight;
     }
     lcElementTreeControl.Width = Math.Max(pmButton.ActualWidth, 400);
     lcElementTreeControl.MaxWidth = lcCurrentwindowbounds.Width;
     lcElementTreeControl.MinHeight = 150;
     lcElementTreeControl.Init(rtStarCube, pmStarElement, rtStarHelper);
     lcElementTreeControl.CaptionColWidth = lcElementTreeControl.Width;
     rtWindowBoundsPopup.PlacementTarget = pmButton;
     rtWindowBoundsPopup.Placement = PlacementMode.Bottom;
     rtWindowBoundsPopup.StaysOpen = false;//false;
     rtWindowBoundsPopup.Closed -= WindowBoundsPopupOnClosed;
     rtWindowBoundsPopup.Closed += WindowBoundsPopupOnClosed;

     rtWindowBoundsPopup.IsOpen = true;
}

В WindowBoundsPopupOnClosed ничего не происходит, я пытался заставить его работать там, но мне не удалось это сделать.

1 Ответ

1 голос
/ 13 марта 2020

Где вы на самом деле закрываете Popup? Я вижу только, что вы устанавливаете IsOpen в true. Текущее поведение: первый щелчок по PopupButton откроет Popup. Теперь, когда для StaysOpen установлено значение false, повторное нажатие кнопки (которая находится за пределами Popup) приведет к закрытию Popup, поскольку popup потерял фокус, поскольку он перемещен из Popup до PopupButton. IsOpen возвращается сейчас false. Этот второй щелчок затем вызывает обработчик события ButtonClickAsync, который снова устанавливает IsOpen в значение true, что снова открывает Popup.

Ваш код слишком сложен, потому что вы используете C# вместо XAML.

PopupButton должен быть ToggleButton или основан на нем.

<Window>
  <StackPanel>
    <ToggleButton x:Name="PopupButton" />
    <Popup IsOpen="{Binding ElementName=PopupButton, Path=IsChecked}">
      <ElementTreeControl Tag="pmButton" />
    </Popup>
  </StackPanel>
</Window>

Использование EventTrigger

Альтернативный подход заключается в использовании EventTrigger. Это может быть проще в ситуациях, когда у вас нет доступа к управляющему элементу запуска, например, PopupButton, поскольку он может быть определен вне области видимости, например, внутри какого-либо другого шаблона. В этом примере все еще предполагается, что PopupButton является производным от ToggleButton:

Window, в котором находится Popup
(ToggleButton, который открывает / закрывает Popup, определяется в отдельном элементе управления ControlWithPopupButton, см. ниже)

<Window>
  <Window.Triggers>

    <!-- 
      EventTriggers must be defined in the scope of the Popup "RtWindowBoundsPopup" 
      and in the routing path of the raised event.
    -->
    <EventTrigger RoutedEvent="ToggleButton.Unchecked" 
                  Sourcename="PopupButton">      
      <BeginStoryboard>
        <Storyboard>
          <BooleanAnimationUsingKeyFrames Storyboard.TargetName="RtWindowBoundsPopup"
                                          Storyboard.TargetProperty="IsOpen"
                                          Duration="0">
            <DiscreteBooleanKeyFrame Value="False" />
          </BooleanAnimationUsingKeyFrames>
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>

    <EventTrigger RoutedEvent="ToggleButton.Checked" 
                  Sourcename="PopupButton">      
      <BeginStoryboard>
        <Storyboard>
          <BooleanAnimationUsingKeyFrames Storyboard.TargetName="RtWindowBoundsPopup"
                                          Storyboard.TargetProperty="IsOpen"
                                          Duration="0">
            <DiscreteBooleanKeyFrame Value="True" />
          </BooleanAnimationUsingKeyFrames>
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Window.Triggers>

  <StackPanel>
    <ControlWithPopupButton />
    <Popup x:Name="RtWindowBoundsPopup">
      <ElementTreeControl Tag="pmButton" />
    </Popup>
  </StackPanel>
</Window>

UserControl, который содержит PopupButton

<ControlWithPopupButton>

  <!-- 
    PopupButton must derive from ToggleButton
    or raise both Routed Events ToggleButon.Check and ToggleButton.Unchecked. 
  -->
  <PopupButton x:Name="PopupButton" />
</ControlWithPopupButton>

Замечания

Затем такие методы, как ElementTreeControl.Init, должны вызываться из обработчика событий ElementTreeControl.Loaded внутри класса ElementTreeControl. Я не знаю, что такое StarElement, но оно должно связываться с DependencyProperty из ElementTreeControl. У меня недостаточно контекста, но я думаю, вам следует добавить override из ElementTreeControl.OnSelectionChanged, чтобы вы могли переместить код PopupListBoxSelectionChangedAsync в ElementTreeControl.


Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...