views:

30

answers:

2

I've been struggling with this for quite some time and I can't seem to find a proper solution. This is the scenario stripped down. Imagine you have the following XAML:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid x:Name="Host" Width="200" Height="200">
        <Popup IsOpen="True">
            <Button Content="Some Button" Click="Button_Click" />
        </Popup>
    </Grid>
</Grid>

In the Button_Click event handler all I do is collapse the grid with name Host.

private void Button_Click(object sender, RoutedEventArgs e)
{
    this.Host.Visibility = System.Windows.Visibility.Collapsed;
}

What I expected was that the Popup would close therefore hiding the Button. I understand that Popups are not in the same VisualTree and I understand why this might not be working the way I expect it to, but there should be some kind of mechanism for this to happen automatically. The only workaround that comes to my mind is on LayoutUpdated to traverse the visual tree up and ask each Button's parent if it is visible and if I meet a collapsed parent -> close the Popup. However, imagine the performance hit having a HUGE visual tree. It's insane to traverse the visual tree on every layout pass. I'm open to any sort of suggestions.

EDIT: It seems that I did not explain fully my scenario. The case is to collapse the Popup if ANY of its parent gets collapsed (not just the immediate one). In WPF there is a useful property called IsVisible which is different than Visibility. For example, Visibility might still be Visible, but IsVisible will be false in this scenario.

Best Regards, K

A: 

If you just want to close the popup, why don't you set the IsOpen at the popup to false.

<Grid x:Name="LayoutRoot" Background="White">
    <Grid x:Name="Host" Width="200" Height="200">
        <Popup x:Name="HostPopup" IsOpen="True">
            <Button Content="Some Button" Click="Button_Click" />
        </Popup>
    </Grid>
</Grid>

private void Button_Click(object sender, RoutedEventArgs e)
{
   this.HostPopup.IsOpen = false;
}

This closes the popup.

TerenceJackson
My main goal is to be able to close the popup when any of its parents collapses.
Kiril
Ok, sry, then my answer is not what you need.How huge is the visual tree (how many user controls are there "wrapping" the popup)?You would add some kind of listener to each wrapping element, and if the visibility changes, you could throw an event or go to a state...Maybe you could do something with behaviours or states...
TerenceJackson
A: 

I think you found a bug, or at least a "weirdness" in the popup control - check this out:

My initial thought was to simply Bind the Visibility of the Popup to the Host. This SHOULD work.

    <Grid x:Name="LayoutRoot" Background="White">
    <Grid x:Name="Host" Width="200" Height="200" Background="Aqua">
        <Popup IsOpen="True" Visibility="{Binding ElementName=Host, Path=Visibility}" Height="100" Width="100">
            <Button Content="Some Button" Click="Button_Click"/>
        </Popup>
    </Grid>
</Grid>

But it does not work. The "Host" grid disappears, but I still see the button. This confused me, so I fired up Silverlight Spy, and check this out - setting the Visibility of the Popup does NOT hide the button!

See Demo Video

Anyway, in order to make this work, you just need to massage things a little bit in order tie the Host Visibility to the IsOpen property. I used a converter here:

<UserControl.Resources>
    <Junk:VisibilityToBoolConverter x:Key="VisibilityToBoolConverter"/>
</UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
    <Grid x:Name="Host" Width="200" Height="200" Background="Aqua">
        <Popup IsOpen="{Binding ElementName=Host, Path=Visibility, Converter={StaticResource VisibilityToBoolConverter}}" Height="100" Width="100">
            <Button Content="Some Button" Click="Button_Click"/>
        </Popup>
    </Grid>
</Grid>

private void Button_Click(object sender, RoutedEventArgs e)
{
    this.Host.Visibility = System.Windows.Visibility.Collapsed;

}


public class VisibilityToBoolConverter : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ((Visibility) value) == Visibility.Visible;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

That's working for me.

Todd Davis
Thanks for the reply. Actually, this will work only in the case when I collapse Host. However, if Host has a parent itself and I collapse Host's parent, nothing will happen. The whole problem is that I don't know which parent of the PopUp will be collapsed.
Kiril
I don't think you can, at least, not the way you want it to work. It "should" work that way, but as you saw in the video, even setting the visibility of the Popup itself to Collapsed doesn't hide the Content of the Popup.I also tried Binding the IsOpen to the Visibility of the Popup control, but I learned that setting the Parent property of a control to Collapsed doesn't collapse the child controls - not sure how that works to be honest. I think the only way to make this work - for now - is to know what the parent control is, and bind it.
Todd Davis