Reputation: 187
I have a problem with PopupButton in WPF. When I press it once the popup appears with some items to select (ElementTreeControl). After i press the same PopupButton it should close - yet it closes and opens again. I solved that and it was working but when i click outside of this control and it closes (StayOpen = false) I have problem with reopening it again - need to press PopupButton two times.
Is there some property or workaround on how to detect when the control was pressed and when it was area outside of it?
I want the PopupButton to be:
When closed:
When open:
Popup button click action:
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;
}
In WindowBoundsPopupOnClosed nothing happens, i have tried to make it work there but didn't menage to do so.
Upvotes: 0
Views: 252
Reputation: 28968
Where do you actually close the Popup
? I only see you setting IsOpen
to true
. Current behavior: the first click on PopupButton
will open the Popup
. Now because StaysOpen
is set to false
, clicking the button (which is outside the Popup
) a second time will close the Popup
, as the popup
has lost focus, since it is moved from Popup
to the PopupButton
. IsOpen
returns now false
. This second click then invokes the event handler ButtonClickAsync
which sets IsOpen
back to true again, which reopens the Popup
.
Your code is too complicated, because you are using C# instead of XAML.
The PopupButton
should be a ToggleButton
or based on it.
<Window>
<StackPanel>
<ToggleButton x:Name="PopupButton" />
<Popup IsOpen="{Binding ElementName=PopupButton, Path=IsChecked}">
<ElementTreeControl Tag="pmButton" />
</Popup>
</StackPanel>
</Window>
EventTrigger
An alternative approach is to use EventTrigger
. This can be easier in situations, where you don't have access to the triggering control 'e.g., the PopupButton
, as it might be defined out of scope e.g inside some other template. This example still assumes that PopupButton
is derived from ToggleButton
:
The Window
, which hosts the Popup
(The ToggleButton
, which opens/closes the Popup
, is defined in a separate control ControlWithPopupButton
, see below)
<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>
The UserControl, which contains the PopupButton
<ControlWithPopupButton>
<!--
PopupButton must derive from ToggleButton
or raise both Routed Events ToggleButon.Check and ToggleButton.Unchecked.
-->
<PopupButton x:Name="PopupButton" />
</ControlWithPopupButton>
Then methods like ElementTreeControl.Init
, should be called from the ElementTreeControl.Loaded
event handler inside the ElementTreeControl
class. I don't now what StarElement
is, but it should bind to a DependencyProperty
of ElementTreeControl
. I don't have enough context, but I guess you should add an override
of ElementTreeControl.OnSelectionChanged
, so that you can move the code of PopupListBoxSelectionChangedAsync
to the ElementTreeControl
.
Upvotes: 1