You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF Popup位置异常:设置PlacementMode.Left却显示在右侧

问题:WPF Popup 定位与预期相反的异常现象

我通过C#代码创建WPF Popup,期望将其放置在MyUserControl右侧,但实际只有设置popup.Placement = PlacementMode.Left;时,Popup才会出现在UserControl右侧;若设置popup.Placement = PlacementMode.Right;,Popup反而显示在左侧,现象异常。

场景说明

  • MainWindow中存在名为MyGrid的Grid控件,MyUserControl嵌入其中;
  • 点击MyUserControl内的按钮时,通过委托将MyGrid作为Popup的PlacementTarget传递。

相关代码

MyUserControl的XAML代码

<Grid Background="AliceBlue" Width="270" Height="100">
    <Button Width="50" 
            Height="20" 
            HorizontalAlignment="Right" 
            VerticalAlignment="Center"
            Click="TestButton_Click"/>
</Grid>

MyUserControl的C#代码

public partial class MyUserControl : UserControl
 {
     UIElement Parent = null;
     public delegate void TestButtonMouseClick(UIElement target);
     public static event TestButtonMouseClick ETestButtonMouseClick;

     public MyUserControl(UIElement parent)
     {
         InitializeComponent();
         this.Parent = parent;
     }

     private void TestButton_Click(object sender, RoutedEventArgs e)
     {
         ETestButtonMouseClick?.Invoke(Parent);
     }
 }

MainWindow的XAML代码

<Grid>
    <Grid x:Name="MyGrid" 
          HorizontalAlignment="Center" 
          VerticalAlignment="Center" 
          Width="270"
          Height="100"/>
</Grid>

MainWindow的C#代码

public MainWindow()
{
    InitializeComponent();
    MyUserControl userControl = new MyUserControl(MyGrid);
    MyGrid.Children.Add(userControl);
    MyUserControl.ETestButtonMouseClick += MyUserControl_ETestButtonMouseClick;
}

private void MyUserControl_ETestButtonMouseClick(UIElement target)
{
    Application.Current.Dispatcher.InvokeAsync(new Action(() =>
    {
        Popup popup = new Popup();
        popup.AllowsTransparency = true;
        popup.PlacementTarget = target;
        popup.HorizontalOffset = 10;
        popup.VerticalOffset = 41;
        popup.Placement = PlacementMode.Left;
        popup.StaysOpen = false;

        TextBlock myTextBlock = new TextBlock();
        myTextBlock.HorizontalAlignment = HorizontalAlignment.Center;
        myTextBlock.VerticalAlignment = VerticalAlignment.Center;
        myTextBlock.Width = 166;
        myTextBlock.Height = 170;
        myTextBlock.Text = "Just a test.";
        myTextBlock.FontSize = 22;
        myTextBlock.Foreground = new SolidColorBrush(Colors.Red);
        myTextBlock.Background = new SolidColorBrush(Colors.Yellow);
        popup.Child = myTextBlock;
        popup.IsOpen = true;
    }));
}

测试环境

  • Visual Studio 2022 社区版
  • 多环境测试均出现相同现象

问题原因

这个异常源于两个核心问题:

  1. 错误的PlacementTarget:你将MyGrid(MyUserControl的父容器)设置为Popup的定位目标,而非MyUserControl本身;
  2. 偏移量与PlacementMode的交互逻辑误解:WPF Popup的HorizontalOffset是基于PlacementMode的基准点进行偏移的:
    • 当设置PlacementMode.Right时,Popup的左侧边缘默认对齐目标控件的右侧边缘,此时正值的HorizontalOffset会让Popup向左偏移(与直觉相反);
    • 当设置PlacementMode.Left时,Popup的右侧边缘默认对齐目标控件的左侧边缘,正值的HorizontalOffset会让Popup向右偏移目标控件的宽度+偏移量的距离,刚好出现在MyUserControl的右侧(因为MyGrid和MyUserControl尺寸完全一致)。

解决方案

方案1:修正PlacementTarget为MyUserControl

直接将MyUserControl本身作为Popup的定位目标,这是最符合直觉的做法:

修改MyUserControl代码:

public partial class MyUserControl : UserControl
{
    public delegate void TestButtonMouseClick(UIElement target);
    public static event TestButtonMouseClick ETestButtonMouseClick;

    public MyUserControl()
    {
        InitializeComponent();
    }

    private void TestButton_Click(object sender, RoutedEventArgs e)
    {
        ETestButtonMouseClick?.Invoke(this); // 传递当前控件作为定位目标
    }
}

修改MainWindow初始化逻辑:

public MainWindow()
{
    InitializeComponent();
    MyUserControl userControl = new MyUserControl();
    MyGrid.Children.Add(userControl);
    MyUserControl.ETestButtonMouseClick += MyUserControl_ETestButtonMouseClick;
}

然后调整Popup的参数:

popup.Placement = PlacementMode.Right;
popup.HorizontalOffset = 10; // 此时正值会让Popup向右偏移目标控件右侧边缘10px,符合预期

方案2:保留现有PlacementTarget,调整参数

如果必须以MyGrid作为定位目标,可调整PlacementMode和HorizontalOffset的组合:

popup.Placement = PlacementMode.Right;
popup.HorizontalOffset = 0; // 或设置为MyGrid.ActualWidth + 10来增加间距

这样Popup会正确显示在MyGrid(即MyUserControl)的右侧。


内容的提问来源于stack exchange,提问作者Tom Xue

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 11:40:07