I am trying to understand why my WPF popup appears to align to the right edge of its PlacementTarget:
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:
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;
}
}
}
}