Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invisible opened popup

Tags:

c#

wpf

popup

Second day fighting this issue.

To reproduce, create new WPF application, xaml

<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
    <Button Width="100" Height="100" MouseMove="Button_MouseMove"/>
    <Popup x:Name="popup" StaysOpen="False" AllowsTransparency="True" Placement="Center">
        <TextBlock>Some random text</TextBlock>
    </Popup>
    <CheckBox IsChecked="{Binding (Popup.IsOpen), ElementName=popup}">Popup</CheckBox>
</StackPanel>

and code

private void Button_MouseMove(object sender, MouseEventArgs e)
{
    popup.IsOpen = true;
}

Mouseover button to open popup, click elsewhere to close. Click button to have bug: popup is IsOpen == true (can be seen on checkbox or with breakpoint in handler), while it is invisible.

WTF?

And my original problem is, it seems, what setting IsOpen is not instant. To example, when I try to set it to false in Popup's MouseMove event, then I get MouseEnter and MouseMove events of Button fired right during that

IsOpen = true;

Same with setting it to true, there are 2 (!) MouseMove events occurs, put this line into event handler to see it

System.Diagnostics.Trace.WriteLine("M");

There will be 2 M in the Output window of VS, while Popup (when StayOpen=false) suppose to capture mouse events and it does, but not immediately.

Can someone explain me what it going on? I want no events to occurs during (or shortly after? how to check if this is true?) setting IsOpen. Tried already dozens of things: Dispatcher.InvokeAsync, variables, timers, etc.

like image 748
Sinatr Avatar asked Jun 30 '14 13:06

Sinatr


2 Answers

I think you are right with the asynchronous assumption. During the focus loss, the value of IsOpen is set to false, but the MouseMove of the button triggers to set it open again. Some strange magic inside then breaks the code.

2 possible solutions I found, depending on your needs:

  1. Explicitly set IsOpen to false, after the popup closed (bash async)
  2. #1 + Disable the button during the popup IsOpen == true

The first approach will hide the popup during clicking on the button. The second approach will come along with a slight flickering - when rapidly clicking the button - but it keeps the popup open:

For the first approach, use the following event handlers (you might not check the property first):

private void Button_MouseMove(object sender, MouseEventArgs e)
{
    popup.IsOpen = true;
}

private void Popup_OnClosed(object sender, EventArgs e)
{
    if (popup.IsOpen)
        popup.IsOpen = false;
}

For the second approach, use a BoolInvertConverter and bind it one way to the popup:

IsEnabled="{Binding (Popup.IsOpen), ElementName=popup, Converter={StaticResource BoolInvertConverter}, Mode=OneWay}"
like image 106
Herdo Avatar answered Nov 15 '22 12:11

Herdo


Well, MouseMove and MouseEnter are both called when you press the button (even if with Spacebar key), so this leads to situation when Popup is trying to close while having IsOpen set to true at the same time. The simple solution could be to divide this two events.

One possible way can be to stick to MouseEnter and open Popup just once while hovering over given Button. Just as example:

private Button currentPopupHolder;
private void Button_MouseEnter(object sender, MouseEventArgs e)
{
   var btn = sender as Button;
   if (currentPopupHolder != btn)
   {
      popup.IsOpen = true;
      currentPopupHolder = btn;
   }
}

private void Button_MouseLeave(object sender, MouseEventArgs e)
{
   currentPopupHolder = null;
}

While the same Button generates this events Popup should not be opened more than once (including the time when the button is pressed).

like image 29
icebat Avatar answered Nov 15 '22 11:11

icebat