locked
Popup Positioning like PopupMenu RRS feed

  • Question

  • Hi Everyon,

    I need to open a popup when my user clicks on a button in the AppBar.

    I don't think this is some special requirement but I can't find out how to position my popup so that it look like the popup is some kind of menu opened by this button.

    To explain myself simplier: I want the popup position to be relative to the one of the button.

    How is this to be achieve? I tried to compute the position before opening the popup but without significant result.

    Many thanks for your help,

    Bruno

    Friday, February 1, 2013 5:12 PM

Answers

  • Here's how I finaly got this working.

    First I noticed that my Popup always had the whole screen's height as ActualHeight value. I tricked it by using the ActualHeight of its child, that is a border in my case.

    The XAML:

    <Page.BottomAppBar>
            <AppBar x:Name="AppBarBottom"
                    Style="{StaticResource AppBarStyle}"
                    Padding="10,0,10,0"
                    IsOpen="True">
                <Grid>
                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                        <Grid VerticalAlignment="Center">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>
    
                            <Popup x:Name="MyPopup"
                                   Grid.Row="0">
                                <Border Style="{StaticResource PopupMenuBorder}"
                                        x:Name="MyPopupBorder"
                                        SizeChanged="MyPopupBorder_SizeChanged_1">
                                    <ListBox x:Name="MyListBox"
                                         ItemsSource="{Binding MyDataSource}"                                      Tapped="MyListBox_Tapped_1"/>
                                </Border>
                            </Popup>
    
                            <Button x:Name="AppBar_MyButton" 
                                    Grid.Row="1"
                                    Click="AppBar_MyButton_Click" 
                                    Content="Tap Me!"/>
                        </Grid>

    And here the required code:

    private void AppBar_MyButton_Click_1(object sender, RoutedEventArgs e)
            {
                if (!MyPopup.IsOpen)
                {
                    MyPopup.IsOpen = true;
                    UpdatePopupMenuVerticalOffset(MyPopup,
                                                  MyPopupBorder,
                                                  AppBar_MyButton);
                }
            }
    
            private void MyListBox_Tapped_1(object sender, TappedRoutedEventArgs e)
            {
                MyPopup.IsOpen = false;
            }
    
            private void MyPopup_SizeChanged_1(object sender, SizeChangedEventArgs e)
            {
                if (!MyPopup.IsOpen)
                    return;
    
                UpdatePopupMenuVerticalOffset(MyPopup, 
                                              MyPopupBorder, 
                                              MyButton);
            }
    
            private void UpdatePopupMenuVerticalOffset(Popup popup,
                                                       FrameworkElement popupChild,
                                                       FrameworkElement menuButton)
            {
                // Update the layout so that all ActualHeight values are correct
                UpdateLayout();
    
                // TODO Compute distance between the top of the button and the top of the app bar
                double verticalOffset = (popupChild.ActualHeight * -1) - 10; // 10 = margin
                popup.VerticalOffset = verticalOffset;
            }

    • Marked as answer by Bruno Knittel Monday, February 4, 2013 8:33 PM
    Monday, February 4, 2013 8:33 PM

All replies

  • Please download the XAML Popup sample.

    In Scenario1.XAML, replace the bottom <Grid "Output" ... with the following; it shows how you can 'play' with the Grid and Popup-related variables to position the Popup:

    <Grid x:Name="Output" HorizontalAlignment="Left" VerticalAlignment="Top" 
                  Grid.Row="1" Background="Red">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                    <Button Grid.Row="0" Grid.Column="0" Content="Show Popup (using Offset)" 
                            Click="ShowPopupOffsetClicked" />
                <!-- You can arrange the Grid to your liking -->
                <!-- You can play with these values: Grid.Row, Grid.Column, VerticalOffset, HorizontalOffset -->
                <Popup Grid.Row="1" Grid.Column="1" VerticalOffset="+5" HorizontalOffset="+5" 
                       x:Name="StandardPopup">
                    <Border BorderBrush="{StaticResource ApplicationForegroundThemeBrush}" 
                            BorderThickness="2" 
                            Background="{StaticResource ApplicationPageBackgroundThemeBrush}" 
                            Width="200" Height="200">
                        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
                            <TextBlock Text="Simple Popup" FontSize="24.667" HorizontalAlignment="Center" />
                            <Button Content="Close" Click="ClosePopupClicked" HorizontalAlignment="Center" />
                        </StackPanel>
                    </Border>
                </Popup>
            </Grid>
    Please note to post the XAML C# VB language-related questions on the corresponding forum
      
    Friday, February 1, 2013 7:13 PM
  • Hello ForInfo,

    Thank you for your answer, may I continue in this thread or should I open one new in the other forum? Or maybe can a moderator move the thread? Thanks in advance.

    I tried the provided code and it got me closer to the solution. My problem is now that the Popup's height changes (possible while it is often). The popup correctly resizes itself but I thus need the VerticalOffset to be computed based on the popup height.

    I tried to use binding here but it seems that there is no PropertyChanged fired for the ActualHeight property.

    Below my current code:

    <Grid VerticalAlignment="Center">
       <Grid.RowDefinitions>
          <RowDefinition Height="Auto" />
          <RowDefinition Height="Auto" />
       </Grid.RowDefinitions>
    
       <Popup x:Name="DevicesPopup"
              Grid.Row="0"
              Style="{StaticResource PopupMenu}"
              VerticalOffset="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight, Converter={StaticResource AppBarPopupOffsetConverter}}">
     

    AppBarPopupOffsetConverter changes the sign of the input value and removes a fixed offset to the value (here 10).

    Do you have any idea of I might achieve my goal?

    I though about inheriting of Popup and fire the PropertyChanged myself when the SizeChanged event is fired but Popup is a sealed class.

    Many thanks for your help;

     

    Sunday, February 3, 2013 9:13 AM
  • 1.- ActualHeight/Width:

    Though you are binding to 'Self', "ActualHeight/Width" does not guarantee to update any 'ElementName' bound property with their last actual value: there is this warning for ElementName Binding: FrameworkElement.ActualHeight property (Windows)

    "For purposes of ElementName binding, ActualHeight does not post updates when it changes (due to its asynchronous and run-time calculated nature). Do not attempt to use ActualHeight as a binding source for an ElementName binding. If you have a scenario that requires updates based on ActualHeight, use a SizeChanged handler."

    2.- SizeChanged event [yes] but with [e.g. UserControl in Popup] like in How to pass object from popup to its parent ?

    Sunday, February 3, 2013 9:56 AM
  • Here's how I finaly got this working.

    First I noticed that my Popup always had the whole screen's height as ActualHeight value. I tricked it by using the ActualHeight of its child, that is a border in my case.

    The XAML:

    <Page.BottomAppBar>
            <AppBar x:Name="AppBarBottom"
                    Style="{StaticResource AppBarStyle}"
                    Padding="10,0,10,0"
                    IsOpen="True">
                <Grid>
                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                        <Grid VerticalAlignment="Center">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>
    
                            <Popup x:Name="MyPopup"
                                   Grid.Row="0">
                                <Border Style="{StaticResource PopupMenuBorder}"
                                        x:Name="MyPopupBorder"
                                        SizeChanged="MyPopupBorder_SizeChanged_1">
                                    <ListBox x:Name="MyListBox"
                                         ItemsSource="{Binding MyDataSource}"                                      Tapped="MyListBox_Tapped_1"/>
                                </Border>
                            </Popup>
    
                            <Button x:Name="AppBar_MyButton" 
                                    Grid.Row="1"
                                    Click="AppBar_MyButton_Click" 
                                    Content="Tap Me!"/>
                        </Grid>

    And here the required code:

    private void AppBar_MyButton_Click_1(object sender, RoutedEventArgs e)
            {
                if (!MyPopup.IsOpen)
                {
                    MyPopup.IsOpen = true;
                    UpdatePopupMenuVerticalOffset(MyPopup,
                                                  MyPopupBorder,
                                                  AppBar_MyButton);
                }
            }
    
            private void MyListBox_Tapped_1(object sender, TappedRoutedEventArgs e)
            {
                MyPopup.IsOpen = false;
            }
    
            private void MyPopup_SizeChanged_1(object sender, SizeChangedEventArgs e)
            {
                if (!MyPopup.IsOpen)
                    return;
    
                UpdatePopupMenuVerticalOffset(MyPopup, 
                                              MyPopupBorder, 
                                              MyButton);
            }
    
            private void UpdatePopupMenuVerticalOffset(Popup popup,
                                                       FrameworkElement popupChild,
                                                       FrameworkElement menuButton)
            {
                // Update the layout so that all ActualHeight values are correct
                UpdateLayout();
    
                // TODO Compute distance between the top of the button and the top of the app bar
                double verticalOffset = (popupChild.ActualHeight * -1) - 10; // 10 = margin
                popup.VerticalOffset = verticalOffset;
            }

    • Marked as answer by Bruno Knittel Monday, February 4, 2013 8:33 PM
    Monday, February 4, 2013 8:33 PM