Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Popup hiding problem

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));
like image 884
theSpyCry Avatar asked Jan 23 '23 12:01

theSpyCry


2 Answers

I found the solution on this post : https://stackoverflow.com/a/5821819/651161

Using the following class will permit to handle the click before the togglebutton is pressed. The popup is closed because of the click but the click is then handled, so it doesn't trigger the ToggleButton click.

public class MyPopup : Popup {
    protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) {
        bool isOpen = this.IsOpen;
        base.OnPreviewMouseLeftButtonDown(e);

        if (isOpen && !this.IsOpen)
            e.Handled = true;
    }
}
like image 184
Bast73 Avatar answered Feb 04 '23 17:02

Bast73


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">
like image 33
Nir Avatar answered Feb 04 '23 16:02

Nir