1

I am trying to understand why my WPF popup appears to align to the right edge of its PlacementTarget:

enter image description here

Normally, a popup would be expected to align to the left side.

This is in a larger project I interited with lots of code, styles, etc. I have tried to simplify and extract an example and - to my surprise - if that runs in an otherwise blank separate application, then it aligns correctly:

enter image description here

I have searched through the code to exclude:

  • there is no HorizontalOffset anywhere in the code or resources
  • there is no FlowDirection="RightToLeft" anywhere in the code or resources

I pretty much tried to remove all ResourceDictionaries from the project except for those where the project would not even build or start.

I also investigated the popup via snoop and it has the correct placement target, no offset and the correct placement.

I am lost here. Any theory of what could cause this?

Here is the example code (which may look a little complicated, since the original code has this as a user control):

<Window x:Class="PopupIssue.client.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="80" />
            <ColumnDefinition Width="3" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <StackPanel Orientation="Vertical">
            <Border Background="Blue" Height="40"/>

            <Button x:Name="DropdownParent" Click="Button_OnClick" Focusable="True" KeyboardNavigation.AcceptsReturn="False" >
                <Button.Template>
                    <ControlTemplate TargetType="Button">
                        <StackPanel>
                            <Border Margin="2" Background="AliceBlue">
                                <Grid x:Name="DropdownMain" >
                                    <!-- Background="#FFE3E8EA">-->
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*"/>
                                        <ColumnDefinition Width="20" MaxWidth="20" />
                                    </Grid.ColumnDefinitions>
                                    <Border Grid.Column="0" Grid.ColumnSpan="2">
                                        <TextBlock Text="" />
                                    </Border>
                                    <Border Grid.Column="1" Margin="2" Padding="5" Background="LightSalmon">
                                        <Path x:Name="Arrow" Fill="Gray" HorizontalAlignment="Center" VerticalAlignment="Center" Data="M 0 0 L 4 4 L 8 0 Z" />
                                    </Border>
                                </Grid>
                            </Border>
                            <Popup  Placement="Bottom" IsOpen="{Binding ShowPopup}" PlacementTarget="{Binding ElementName=DropdownParent}" PopupAnimation="Fade" StaysOpen="True" MinWidth="{Binding ActualWidth, ElementName=DropdownParent}" Closed="Popup_OnClosed"  Opened="Popup_OnOpened" KeyDown="Popup_OnKeyDown">
                                <Border  Background="White" Padding="5,2,5,2" BorderBrush="Red" BorderThickness="1">
                                    <StackPanel>
                                        <StackPanel.Resources>
                                            <Style TargetType="{x:Type RadioButton}">
                                                <Setter Property="Margin" Value="0,10,0,0"/>
                                            </Style>
                                        </StackPanel.Resources>

                                        <RadioButton Content="Lorem Ipsum Dolor Ahmed Quid Prio" GroupName="rbGroup" ></RadioButton>
                                        <RadioButton Content="Lorem Ipsum Dolor Ahmed Quid Prio" GroupName="rbGroup" ></RadioButton>
                                        <RadioButton Content="Lorem Ipsum Dolor Ahmed Quid Prio" GroupName="rbGroup" ></RadioButton>
                                        <RadioButton Content="Lorem Ipsum Dolor Ahmed Quid Prio" GroupName="rbGroup" ></RadioButton>
                                        <RadioButton Content="Lorem Ipsum Dolor Ahmed Quid Prio" GroupName="rbGroup" ></RadioButton>
                                    </StackPanel>
                                </Border>
                            </Popup>
                        </StackPanel>
                    </ControlTemplate>
                </Button.Template>
            </Button>

            <Border Background="Blue" Height="40"/>
        </StackPanel>


        <GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
        <Border Background="Beige" Grid.Column="2" />
    </Grid>
</Window>
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Threading;

namespace PopupIssue.client
{
    /// <summary>
    /// Interaktionslogik für Window1.xaml
    /// </summary>
    public partial class Window1 : Window, INotifyPropertyChanged
    {
        public Window1()
        {
            this.DataContext = this;
            InitializeComponent();
        }

        private bool showPopup;
        public bool ShowPopup { get => showPopup; set { showPopup = value; NotifyPropertyChanged("ShowPopup"); } }

        public event PropertyChangedEventHandler PropertyChanged;


        private void NotifyPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }


        private DateTime lastClosed = DateTime.MinValue;

        private void Popup_OnClosed(object sender, EventArgs e)
        {
            lastClosed = DateTime.Now;
            var tmp = OnPopupClosed;
            if (tmp != null)
                tmp(this, new EventArgs());

            Action a = () => this.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
            Dispatcher.BeginInvoke(DispatcherPriority.Background, a);
        }

        public event EventHandler<EventArgs> OnPopupClosed;
        public event EventHandler<EventArgs> OnPopupOpened;



        private void Button_OnClick(object sender, RoutedEventArgs e)
        {
            // When button is clicked while the popup is already open, then the popup is closed automatically by the system before this event handler.
            // So when this method is executed, the popup is always closed. 
            // However if the user clicked the button while the popup was open, then he expects the popup to close (which the system does automatically) and not to immediately re-open.

            // The only viable way is to check if the popup closed immediately (<200 ms) before the click and not open the popup then
            var millisSinceClose = (DateTime.Now - lastClosed).TotalMilliseconds;

            if (millisSinceClose > 200)
                ShowPopup = true;
        }

        private void Popup_OnOpened(object sender, EventArgs e)
        {
            var f = Content as FrameworkElement;
            if (f != null)
            {
                Action a = () => f.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
                Dispatcher.BeginInvoke(DispatcherPriority.Background, a);

                var tmp = OnPopupOpened;
                if (tmp != null)
                    tmp(this, new EventArgs());
            }
        }

        private void Popup_OnKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
            {
                ((Popup)sender).IsOpen = false;
                e.Handled = true;
            }
        }

    }
}
user1211286
  • 681
  • 5
  • 17
  • You have set `Placement=Bottom` and Placement Target to `DropdownParent` and it is showing like that . so what's wrong ? – Hammas Apr 14 '20 at 13:13
  • and the reason it seems to be rightly aligned could be `` you have set basically width of `DropdownParent` parent to 80 and now popup not finding enough space to be completely left aligned and it's overflowing it .. – Hammas Apr 14 '20 at 13:16
  • The exact same XAML+cs in a separate project aligns differently -- I have added an image above. So it cannot be something in the source I posted, I guess. I am therefore looking for hints for what else to search. – user1211286 Apr 14 '20 at 13:44
  • i still see in first picture window margin from left so popup pushes itself to left where it find space. in sec pic you shown it's unclear if window have same space on left side ? – Hammas Apr 14 '20 at 13:47
  • Actually no ! you are right i've tried and it always shows like you have shown ! you may use `HorizontalOffset` to manually show it like you want. – Hammas Apr 14 '20 at 14:01

1 Answers1

3

I believe I found the reason for the different behavior: There seems to be a system-wide setting for how Windows aligns popups (mostly menus), which is specifically intended for touch. You can set it to left-handed or right-handed (search for MenuDropAlignment).

The WPF Popup appears to honor this setting and either aligns one way or the other. This is pretty much not documented under the WPF Popup documentation.

I changed the setting in Windows and now my real application behaves as expected.

user1211286
  • 681
  • 5
  • 17