Popup 放置行为

Popup 控件在一个独立的窗口中显示内容,该窗口浮动于应用程序之上。 可以使用 PlacementTargetPlacementPlacementRectangleHorizontalOffsetVerticalOffset 属性指定 Popup 相对于控件、鼠标或屏幕的位置。 这些属性协同工作,让你灵活地指定 Popup的位置。

说明

ToolTipContextMenu 类还定义了这五个属性,行为类似。

定位 Popup

Popup 的位置可以相对于 UIElement 或整个屏幕。 以下示例创建四个相对于 UIElementPopup 控件,在本例中为图像。 所有 Popup 控件都将 PlacementTarget 属性设置为 image1,但每个 Popup 都有不同的放置属性值。

<Canvas Width="200" Height="150">
  <Image Name="image1"
         Canvas.Left="75" 
         Source="Water_lilies.jpg" Height="200" Width="200"/>
  <Popup IsOpen="True" PlacementTarget="{Binding ElementName=image1}"
         Placement="Bottom">
    <TextBlock FontSize="14" Background="LightGreen">Placement=Bottom</TextBlock>

  </Popup>
  <Popup IsOpen="True" PlacementTarget="{Binding ElementName=image1}"
         Placement="Top">
    <TextBlock FontSize="14" Background="LightGreen">Placement=Top</TextBlock>

  </Popup>
  <Popup IsOpen="True" PlacementTarget="{Binding ElementName=image1}"
         Placement="Left">
    <TextBlock FontSize="14" Background="LightGreen">Placement=Left</TextBlock>

  </Popup>
  <Popup IsOpen="True" PlacementTarget="{Binding ElementName=image1}"
         Placement="Right">
    <TextBlock FontSize="14" Background="LightGreen">Placement=Right</TextBlock>

  </Popup>
</Canvas>

下面的插图显示了图像和 Popup 控件

包含四个弹出窗口控件的图像

本简单示例演示如何设置 PlacementTargetPlacement 属性,但通过使用 PlacementRectangleHorizontalOffsetVerticalOffset 属性,可以更好地控制 Popup 的位置。

![注意] 根据与左右手使用习惯相关的 Windows 设置,弹出窗口在顶部或底部显示时可能会左对齐或右对齐。 上图演示了与右手使用习惯相关的对齐方式,这会将弹出窗口置于左侧。

术语定义:弹出窗口的结构解析

以下术语有助于了解 PlacementTargetPlacementPlacementRectangleHorizontalOffset以及 VerticalOffset 属性彼此和 Popup的关系:

  • 目标对象

  • 目标区域

  • 目标原点

  • Popup 对齐点

这些术语为引用 Popup 的各个方面及其关联的控件提供了一种便捷的方法。

目标对象

目标对象 是与 Popup 关联的元素。 如果设置了 PlacementTarget 属性,则指定目标对象。 如果未设置 PlacementTarget,并且 Popup 具有父级,则父级对象为目标对象。 如果没有 PlacementTarget 值和父对象,则没有目标对象,并且 Popup 相对于屏幕进行定位。

以下示例创建了一个作为 Canvas 的子级的 Popup。 该示例未在 Popup上设置 PlacementTarget 属性。 Placement 的默认值为 PlacementMode.Bottom,因此 Popup 显示在 Canvas下方。

<Canvas Margin="5" Background="Red" Width="200" Height="150" >

  <Ellipse Canvas.Top="60" Canvas.Left="50"
           Height="85" Width="60" 
           Fill="Black"/>

  <Popup IsOpen="True" >
    <TextBlock Background="LightBlue" FontSize="18">This is a Popup</TextBlock>
  </Popup>
</Canvas>

下图显示 Popup 相对于 Canvas的定位。

没有 PlacementTarget 的 Popup 控件

以下示例创建了一个作为 Canvas 的子级的 Popup,但这一次将 PlacementTarget 设置为 ellipse1,因此 Popup 在 Ellipse 下方显示。

<Canvas Margin="5" Background="Red" Width="200" Height="150" >

  <Ellipse Name="ellipse1"
           Canvas.Top="60" Canvas.Left="50"
           Height="85" Width="60" 
           Fill="Black"/>

  <Popup IsOpen="True" PlacementTarget="{Binding ElementName=ellipse1}">
    <TextBlock Background="LightBlue" FontSize="18">This is a Popup</TextBlock>
  </Popup>
</Canvas>

下图显示 Popup 相对于 Ellipse的定位。

相对于椭圆定位的 Popup 的弹出窗口

说明

对于 ToolTipPlacement 的默认值为 Mouse。 对于 ContextMenuPlacement 的默认值为 MousePoint。 稍后将在“属性如何协同工作”中解释这些值。

目标区域

目标区域是屏幕上与 Popup 相对的区域。 在前面的示例中,Popup 与目标对象的边界对齐,但在某些情况下,即使 Popup 具有目标对象,Popup 也会与其他边界对齐。 如果设置了 PlacementRectangle 属性,则目标区域与目标对象的边界不同。

以下示例创建两个 Canvas 对象,每个对象包含一个 Rectangle 和一个 Popup。 在这两种情况下,Popup 的目标对象是 Canvas。 第一个 Canvas 中的 Popup 具有 PlacementRectangle 集,其 XYWidthHeight 属性分别设置为 50、50、50 和 100。 第二个 Canvas 中的 Popup 未设置 PlacementRectangle。 因此,第一个 Popup 位于 PlacementRectangle 下方,第二个 Popup 位于 Canvas下方。 每个 Canvas 还包含一个 Rectangle,其与第一个 PopupPlacementRectangle 的范围相同。 请注意,PlacementRectangle 不会在应用程序中创建可见元素;该示例创建一个表示 PlacementRectangleRectangle

<StackPanel Orientation="Horizontal" Margin="50,50,0,0">

  <Canvas Width="200" Height="200" Background="Red">
    <Rectangle Canvas.Top="50" Canvas.Left="50" 
               Width="50" Height="100"
               Stroke="White" StrokeThickness="3"/>
    <Popup IsOpen="True" PlacementRectangle="50,50,50,100">
      <TextBlock FontSize="14" Background="Yellow"
                 Width="140" TextWrapping="Wrap">
        This is a popup with a PlacementRectangle.
      </TextBlock>
    </Popup>
  </Canvas>
  
  <Canvas Width="200" Height="200" Background="Red" Margin="30,0,0,0">
    <Rectangle Canvas.Top="50" Canvas.Left="50" 
               Width="50" Height="100"
               Stroke="White" StrokeThickness="3"/>
    <Popup IsOpen="True">
      <TextBlock FontSize="14" Background="Yellow"
                 Width="140" TextWrapping="Wrap">
        This is a popup without a PlacementRectangle.
      </TextBlock>
    </Popup>
  </Canvas>
  
</StackPanel>

下图显示了前面的示例的结果。

具有和没有 PlacementRectangle 的 Popup

目标原点和目标对齐点

目标原点弹出窗口对齐点 分别是目标区域和弹出窗口上的参考点,分别用于定位。 可以使用 HorizontalOffsetVerticalOffset 属性来偏移目标区域的弹出窗口。 HorizontalOffsetVerticalOffset 相对于目标原点和 Popup 对齐点。 Placement 属性的值确定目标原点和弹出对齐点的位置。

以下示例创建一个 Popup,并将 HorizontalOffsetVerticalOffset 属性设置为 20。 Placement 属性设置为 Bottom(默认值),因此目标原点是目标区域的左下角,弹出窗口对齐点是 Popup的左上角。

<Canvas Width="200" Height="200" Background="Yellow" Margin="20">
  <Popup IsOpen="True" Placement="Bottom"
         HorizontalOffset="20" VerticalOffset="20">
    <TextBlock FontSize="14" Background="#42F3FD">
      This is a popup.
    </TextBlock>
  </Popup>
</Canvas>

下图显示了前面的示例的结果。

使用目标原点对齐点的 Popup 放置

属性如何协同工作

需要一起考虑 PlacementTargetPlacementRectanglePlacement 的值,以确定正确的目标区域、目标原点和弹出窗口对齐点。 例如,如果 Placement 的值 Mouse,则没有目标对象,则忽略 PlacementRectangle,并且目标区域是鼠标指针的边界。 另一方面,如果 PlacementBottom,则 PlacementTarget 或父级确定目标对象,PlacementRectangle 确定目标区域。

下表描述了目标对象、目标区域、目标原点和弹出窗口对齐点,并指示 PlacementTargetPlacementRectangle 是否用于每个 PlacementMode 枚举值。

PlacementMode 目标对象 目标区域 目标原点 Popup 对齐点
Absolute 不適用。 PlacementTarget 将被忽略。 屏幕或 PlacementRectangle(如果已设置)。 PlacementRectangle 相对于屏幕。 目标区域的左上角。 Popup 的左上角。
AbsolutePoint 不適用。 PlacementTarget 将被忽略。 屏幕或 PlacementRectangle(如果已设置)。 PlacementRectangle 相对于屏幕。 目标区域的左上角。 Popup 的左上角。
Bottom PlacementTarget 或父级。 目标对象,或 PlacementRectangle(如果已设置)。 PlacementRectangle 相对于目标对象。 目标区域的左下角。 Popup 的左上角。
Center PlacementTarget 或父级。 目标对象,或 PlacementRectangle(如果已设置)。 PlacementRectangle 相对于目标对象。 目标区域的中心。 Popup 的中心。
Custom PlacementTarget 或父级。 目标对象,或 PlacementRectangle(如果已设置)。 PlacementRectangle 相对于目标对象。 CustomPopupPlacementCallback 定义。 CustomPopupPlacementCallback 定义。
Left PlacementTarget 或父级。 目标对象,或 PlacementRectangle(如果已设置)。 PlacementRectangle 相对于目标对象。 目标区域的左上角。 Popup 的右上角。
Mouse 不適用。 PlacementTarget 将被忽略。 鼠标指针的边界。 PlacementRectangle 将被忽略。 目标区域的左下角。 Popup 的左上角。
MousePoint 不適用。 PlacementTarget 将被忽略。 鼠标指针的边界。 PlacementRectangle 将被忽略。 目标区域的左上角。 Popup 的左上角。
Relative PlacementTarget 或父级。 目标对象,或 PlacementRectangle(如果已设置)。 PlacementRectangle 相对于目标对象。 目标区域的左上角。 Popup 的左上角。
RelativePoint PlacementTarget 或父级。 目标对象,或 PlacementRectangle(如果已设置)。 PlacementRectangle 相对于目标对象。 目标区域的左上角。 Popup 的左上角。
Right PlacementTarget 或父级。 目标对象,或 PlacementRectangle(如果已设置)。 PlacementRectangle 相对于目标对象。 目标区域的右上角。 Popup 的左上角。
Top PlacementTarget 或父级。 目标对象,或 PlacementRectangle(如果已设置)。 PlacementRectangle 相对于目标对象。 目标区域的左上角。 Popup 的左下角。

下图显示了每个 PlacementMode 值的 Popup、目标区域、目标原点和 Popup 对齐点。 在每个图中,目标区域为黄色,Popup 为蓝色。

采用 Absolute 或 AbsolutePoint 定位的 Popup

采用 Bottom 定位的 Popup

采用 Center 定位的 Popup

采用 Left 定位的 Popup

采用 Mouse 定位的 Popup

采用 MousePoint 定位的 Popup

采用 Relative 或 RelativePoint 定位的 Popup

采用 Right 定位的 Popup

采用 Top 定位的 Popup

弹出窗口遇到屏幕边缘时

出于安全原因,屏幕边缘无法隐藏 Popup。 当 Popup 遇到屏幕边缘时,会发生以下三件事之一:

  • Popup 沿着将遮挡 Popup 的屏幕边缘重新自行对齐。

  • Popup 使用其他 Popup 对齐点。

  • 弹出窗口使用不同的目标原点和弹出窗口对齐点。

本部分稍后将进一步介绍这些选项。

遇到屏幕边缘时 Popup 的行为取决于 Placement 属性的值以及弹出窗口遇到的屏幕边缘。 下表汇总了针对每个 PlacementMode 值,Popup 到达屏幕边缘时的行为。

PlacementMode 上边缘 下边缘 左边缘 右边缘
Absolute 与上边缘对齐。 与下边缘对齐。 与左边缘对齐。 与右边缘对齐。
AbsolutePoint 与上边缘对齐。 Popup 对齐点更改为 Popup 的左下角。 与左边缘对齐。 Popup 对齐点更改为 Popup 的右上角。
Bottom 与上边缘对齐。 目标原点将更改为目标区域的左上角,弹出窗口对齐点将更改为 Popup的左下角。 与左边缘对齐。 与右边缘对齐。
Center 与上边缘对齐。 与下边缘对齐。 与左边缘对齐。 与右边缘对齐。
Left 与上边缘对齐。 与下边缘对齐。 目标原点将更改为目标区域的右上角,弹出窗口对齐点将更改为 Popup的左上角。 与右边缘对齐。
Mouse 与上边缘对齐。 目标原点将更改为目标区域的左上角(鼠标指针的边界),弹出窗口对齐点将更改为 Popup的左下角。 与左边缘对齐。 与右边缘对齐。
MousePoint 与上边缘对齐。 Popup 对齐点更改为 Popup 的左下角。 与左边缘对齐。 弹出窗口对齐点将更改为弹出窗口的右上角。
Relative 与上边缘对齐。 与下边缘对齐。 与左边缘对齐。 与右边缘对齐。
RelativePoint 与上边缘对齐。 Popup 对齐点更改为 Popup 的左下角。 与左边缘对齐。 弹出窗口对齐点将更改为弹出窗口的右上角。
Right 与上边缘对齐。 与下边缘对齐。 与左边缘对齐。 目标原点将更改为目标区域的左上角,弹出窗口对齐点将更改为 Popup的右上角。
Top 目标原点将更改为目标区域的左下角,弹出窗口对齐点将更改为 Popup的左上角。 实际上,这与 BottomPlacement 时相同。 与下边缘对齐。 与左边缘对齐。 与右边缘对齐。

对屏幕边缘对齐

Popup 可以通过重新定位自身来与屏幕边缘对齐,使整个 Popup 在屏幕上可见。 发生这种情况时,目标原点和弹出对齐点之间的距离可能与 HorizontalOffsetVerticalOffset的值不同。 当 PlacementAbsoluteCenterRelative时,Popup 将自身与每个屏幕边缘对齐。 例如,假定 PopupPlacement 设置为 Relative 并将 VerticalOffset 设置为 100。 如果屏幕底部边缘隐藏 Popup的所有或部分,则 Popup 将自身重新定位到屏幕底部边缘,目标原点和弹出对齐点之间的垂直距离小于 100。 下图演示了这一点。

弹出窗口与屏幕边缘对齐,

更改 Popup 对齐点

如果 PlacementAbsolutePointRelativePointMousePoint,当弹出窗口碰到底部或右侧屏幕边缘时,其对齐点将发生更改。

下图演示了当底部屏幕边缘隐藏所有或部分 Popup时,弹出窗口对齐点是 Popup的左下角。

屏幕截图显示具有 Popup 对齐点的目标区域经过左下角的屏幕边缘。

下图说明当 Popup 被右屏幕边缘隐藏时,弹出对齐点是 Popup的右上角。

由于屏幕边缘而产生的新 Popup 对齐点

如果 Popup 到达屏幕的下边缘和右边缘,则 Popup 对齐点为 Popup 的右下角。

更改目标原点和 Popup 对齐点

PlacementBottomLeftMouseRightTop时,如果遇到特定的屏幕边缘,则目标坐标和弹出窗口的对齐点会发生变化。 导致位置更改的屏幕边缘取决于 PlacementMode 值。

Placement 等于 Bottom 并且 Popup 遇到屏幕底部边缘时,其目标原点位于目标区域的左上角,而弹出窗口的对齐点位于 Popup的左下角。

显示屏幕上半部分中目标区域的屏幕截图,其中 Popup 对齐点位于屏幕下半部分,垂直偏移值为 5。

下图演示了当 PlacementLeft 并且 Popup 到达屏幕左边缘时,目标原点是目标区域的右上角,而 Popup 对齐点是 Popup 的左上角。

由于左侧屏幕边缘而产生的新对齐点

下图演示了当 PlacementRight 并且 Popup 到达屏幕右边缘时,目标原点是目标区域的左上角,而 Popup 对齐点是 Popup 的右上角。

由于右侧屏幕边缘而产生的新对齐点

下图演示了,当 PlacementTop 并且 Popup 遇到顶部屏幕边缘时,目标原点位于目标区域的左下角,而弹出窗口的对齐点在 Popup的左上角。

由于顶部屏幕边缘而产生的新对齐点

下图演示了当 PlacementMouse 并且 Popup 到达屏幕底边时,目标原点位于目标区域的左上角(鼠标指针的边界),弹窗对齐点位于 Popup的左下角。

由于鼠标靠近屏幕边缘放置是鼠标,弹出窗口遇到屏幕底部边缘,因此 新的对齐点。

自定义 Popup 放置

可通过将 Placement 属性设置为 Custom 来自定义目标原点和 Popup 对齐点。 然后定义一个 CustomPopupPlacementCallback 委托,该委托返回 Popup 的一组可能的放置点和主轴(按优先顺序排列)。 显示 Popup 最大部分的点被选中。 当 Popup 被屏幕边缘遮挡时,Popup 的位置会自动调整。 有关示例,请参阅指定自定义 Popup 位置

另请参阅