WPF Popup hiding problem

PaN1C_Showt1Me picture PaN1C_Showt1Me · Feb 25, 2010 · Viewed 11.6k times · Source

Suppose you have a ToggleButton for opening a Popup, same behaviour as all known elements as ComboBox etc.

... which is this code:

<ToggleButton x:Name="PART_OpenToggleButton"
    Focusable="False"   
    IsChecked="False"
    Template="{StaticResource MyToggleButton}"> 
    <Grid>                                           
        <Popup x:Name="PART_PopupControl"
               Style="{StaticResource MyPopupStyle}"
               StaysOpen="False"
               VerticalAlignment="Bottom"
               IsOpen="False"
               PlacementTarget="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ToggleButton, AncestorLevel=1}}" />
    </Grid>
</ToggleButton>

Then in code behind you work with .IsOpen for Popup and .IsChecked for ToggleButton. Everything works, but the problem arrives when you Open the Popup and click outside the borders. The Popup will be closed but the ToggleButton stays checked.

You cannot set in the PopupOnClosed Handler that ToggleButton.IsChecked = false, because when you Click on the ToggleButton to close the Popup, the Popup closes itself, sets ToggleButton.IsChecked = false but at the sime time you clicked on the ToggleButton and it tries to Open the Popup again. So you cannot close it.

1st ToggleButtonClick:

-> ToggleButton IsChecked = true

2nd ToggleButtonClick:

-> ToggleButton IsChecked = false
-> ToggleButton IsChecked = true

So if you click on the Toggle Button while Popup being open, it blinks but stays open.

How would you solve this problem, please ?

EDITED:

Try this in a MyWindow.XAML and add the dependency property IsDropDownOpen in the code behind, please:

<Grid>
        <ToggleButton x:Name="PART_OpenToggleButton"
                      Focusable="False"                          
                      Height="20"
                      Width="50"
                      IsChecked="{Binding ElementName=TestWindow, Mode=TwoWay, Path=IsDropDownOpen}">
            <Grid>

                <Popup x:Name="PART_PopupControl"
                       Width="100"
                       Height="100"
                       StaysOpen="False"
                       Focusable="False"
                       VerticalAlignment="Bottom"
                       IsOpen="{Binding ElementName=TestWindow, Path=IsDropDownOpen}"
                       PlacementTarget="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ToggleButton, AncestorLevel=1}}">                  
                </Popup>
            </Grid>
        </ToggleButton>
    </Grid>

public bool IsDropDownOpen
        {
            get { return (bool)GetValue(IsDropDownOpenProperty); }
            set { SetValue(IsDropDownOpenProperty, value); }
        }        
        public static readonly DependencyProperty IsDropDownOpenProperty =
            DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(Window), new UIPropertyMetadata(false));

Answer

Nir picture Nir · Mar 2, 2010

Ok, here is some code that works for me (those are copy-pasted from working code with some of the not-interesting parts removed):

Here's the content of a ComboBox-like UserControl:

<ToggleButton x:Name="Button" Height="19"> 
   <Grid>
        <Label Name="DisplayList" Content="Whatever" />
        <Popup Name="SelectionPopup" MinHeight="100" MinWidth="200"
                    StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=Button}">
        </Popup>
     </Grid>
</ToggleButton>

And from a custom template to an actual ComboBox:

<ToggleButton
      Name="ToggleButton"
      Template="{StaticResource ComboBoxToggleButton}"
      Grid.Column="2"
      Focusable="false"
      IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
      ClickMode="Press">
 </ToggleButton>
 <Popup
      Name="Popup"
      Placement="Bottom"
      IsOpen="{TemplateBinding IsDropDownOpen}"
      AllowsTransparency="True"
      Focusable="False"
      PopupAnimation="Slide">