Popup

弹出窗口在应用中的其他 UI 顶部显示内容。 命名空间中的 Microsoft.UI.Xaml.Controls.Primitives 类是该内容的通用容器。 它没有默认视觉模板,因此需要由你提供内容、外观以及控制其打开和关闭的逻辑。

Popup 是一个底层构建基块。 WinUI 还为常见类型的暂时性 UI(如浮出控件、对话框、教学提示和工具提示)提供控件。 这些控件包括自己的样式、放置和关闭行为。

一个标记为“显示弹出窗口(使用偏移量)”的按钮,旁边是一个打开的弹出窗口,其中包含文本“简单弹出窗口”和“关闭”按钮。

这是正确的控制吗?

如果更高级别控件适合你的方案,请不要使用 Popup 。 请改用以下控件:

Scenario 控制
在用户单击其外部时关闭的 UI 中显示信息或收集与某个项相关的用户输入。 浮出控件
显示命令或选项的列表。 MenuFlyout
提出问题,或显示用户在继续之前必须回应的信息。 ContentDialog
指出某项新功能或重要功能,无论是否针对特定元素。 TeachingTip
当用户将鼠标悬停在元素上或将焦点移到元素上时,显示元素的简短说明。 提示

当你需要在 UI 上方浮动的自定义内容,并且这些控件都无法满足你的需求时,请使用 Popup。 例如,可以将弹出窗口用于由您自己设置样式和定位的叠加层。

弹出窗口不是模态的,因此用户仍可与其后面的 UI 交互。 如果用户在继续之前必须做出响应,请使用 ContentDialog。

Recommendations

  • 为弹出窗口内容提供背景、边框和圆角,以便它从其后面的 UI 中脱颖而出。 WinUI 3 Gallery 示例使用 AcrylicBackgroundFillColorDefaultBrush、SurfaceStrokeColorDefaultBrush 和 OverlayCornerRadius 资源。 有关详细信息,请参阅 Acrylic 材料 和 Popups 中的 ThemeShadow。
  • 如果未启用 轻扫关闭,请包含用于关闭弹出窗口的按钮或其他控件。
  • 保持弹出窗口内容简短,并专注于单个任务。

创建弹出窗口

WinUI 3 示例集图标 WinUI 3 示例库应用包含 WinUI 控件和功能的交互式示例。 从 Microsoft Store 或浏览 GitHub 上的源代码获取应用。

弹出窗口承载一个元素,您将该元素设置为其 Child。 子级通常是一个面板,或者是一个定义弹出窗口内容的 UserControl。 若要显示弹出窗口,请将 IsOpen 设置为 true。 若要隐藏它,请将 IsOpen 设置为 false。

此示例基于 WinUI 3 Gallery 示例。 它在打开它的按钮旁边声明一个弹出窗口,弹出窗口内容包括一个关闭该弹出窗口的按钮。

<Grid HorizontalAlignment="Left" VerticalAlignment="Top">
    <Button Content="Show popup" Click="ShowPopupClicked" />
    <Popup x:Name="StandardPopup" HorizontalOffset="200" VerticalOffset="0">
        <Grid MinWidth="240"
              Padding="16"
              Background="{ThemeResource AcrylicBackgroundFillColorDefaultBrush}"
              BorderBrush="{ThemeResource SurfaceStrokeColorDefaultBrush}"
              BorderThickness="1"
              CornerRadius="{StaticResource OverlayCornerRadius}">
            <StackPanel Spacing="8">
                <TextBlock FontSize="16" Text="Simple popup" />
                <Button Content="Close" Click="ClosePopupClicked" />
            </StackPanel>
        </Grid>
    </Popup>
</Grid>
// Opens the popup if it isn't already open.
private void ShowPopupClicked(object sender, RoutedEventArgs e)
{
    if (!StandardPopup.IsOpen)
    {
        StandardPopup.IsOpen = true;
    }
}

// Closes the popup when the user clicks the Close button inside it.
private void ClosePopupClicked(object sender, RoutedEventArgs e)
{
    if (StandardPopup.IsOpen)
    {
        StandardPopup.IsOpen = false;
    }
}

在代码中创建弹出窗口

还可以在代码中创建弹出窗口,而无需将其添加到可视化树。 在 XAML 中声明的弹出窗口,或添加到可视化树中的面板里的弹出窗口,会从其父元素获取其 XamlRoot。 没有父级的弹出窗口不会这样,因此在打开它之前必须设置其 XamlRoot 属性。

private Popup? _popup;

private void ShowPopupFromCodeClicked(object sender, RoutedEventArgs e)
{
    _popup ??= new Popup
    {
        // PopupContent is a UserControl in your app that defines the popup content.
        Child = new PopupContent(),
        IsLightDismissEnabled = true,

        // Required because this popup isn't in the visual tree. Use the XamlRoot
        // of an element that's already loaded, such as the button that was clicked.
        XamlRoot = ((UIElement)sender).XamlRoot
    };

    _popup.IsOpen = true;
}

Important

在 WinUI 3 中,打开既没有父元素也没有 XamlRoot 的弹出窗口会失败。 在元素添加到实时可视化树之前,元素的 XamlRoot 为 null,因此请从已加载的元素获取该值,而不是在页面构造函数中获取。 Window在类中,可以使用 this.Content.XamlRoot。 有关详细信息,请参阅 ContentDialog 和 Popup。

Note

当触摸键盘出现时,不在可视化树中的弹出窗口不会重新定位自身,因此键盘可以覆盖它。 如果弹出窗口包含文本输入控件(例如 TextBox),请在 XAML 中声明该弹出窗口,或将其添加到可视化树中的面板中。

通过轻触关闭弹出窗口

默认情况下,弹出窗口将保持打开状态,直到你的代码将 IsOpen 设置为 false。 将 IsLightDismissEnabled 设置为 true,以便在用户单击或点击其外部时关闭弹出窗口,或按 Esc 键。 此行为称为 轻松解除。

默认情况下,点击外部即可关闭的弹出窗口之外的区域不会变暗。 若要将其变暗,请将 LightDismissOverlayMode 设置为 On. 当false为IsLightDismissEnabled时,此属性无效。

<Popup x:Name="LightDismissPopup"
       IsLightDismissEnabled="True"
       LightDismissOverlayMode="On">
    <Border Padding="16"
            Background="{ThemeResource AcrylicBackgroundFillColorDefaultBrush}"
            BorderBrush="{ThemeResource SurfaceStrokeColorDefaultBrush}"
            BorderThickness="1"
            CornerRadius="{StaticResource OverlayCornerRadius}">
        <TextBlock Text="Click outside this popup or press Esc to close it." />
    </Border>
</Popup>

定位弹出窗口

可以使用偏移量或相对于另一个元素定位弹出窗口。

使用偏移量

HorizontalOffset 和 VerticalOffset 设置弹出窗口的位置(以像素为单位)。 从中测量偏移的点取决于弹出窗口是否具有父级:

  • 可视化树中的弹出窗口相对于弹出窗口自身在其父级布局中的位置发生了偏移。 例如,在垂直 StackPanel方向中,该位置位于弹出窗口前的元素下方。 在第 一个示例中,弹出窗口与按钮共享一个单元格 Grid,因此它显示在 Grid 的左上角右侧 200 像素处。
  • 没有父级的弹出窗口(如 在代码中创建的弹出窗口)会相对于其 XAML 根的左上角发生偏移,而其 XAML 根通常是应用窗口的内容区域。

将弹出窗口放在元素旁边

若要显示特定元素旁边的弹出窗口,请将 PlacementTarget 设置为该元素。 然后将 DesiredPlacement 设置为 PopupPlacementMode 中除 Auto 之外的值,例如 Bottom 或 RightEdgeAlignedTop。

当弹出窗口打开时,框架将相对于目标元素定位它。 如果弹出窗口无法放入首选位置,框架会将它移到目标的另一侧。 HorizontalOffset 和 VerticalOffset 仍然适用,并将弹出窗口偏离计算得出的位置。

ActualPlacement 报告弹出窗口显示的位置。 当弹出窗口处于打开状态且该值发生更改时,会发生 ActualPlacementChanged 事件。

<StackPanel Spacing="8">
    <Button x:Name="DetailsButton" Content="Show details" Click="ShowDetailsClicked" />
    <TextBlock x:Name="PlacementStatus" />
    <Popup x:Name="DetailsPopup"
           PlacementTarget="{x:Bind DetailsButton}"
           DesiredPlacement="BottomEdgeAlignedLeft"
           IsLightDismissEnabled="True"
           ActualPlacementChanged="DetailsPopup_ActualPlacementChanged">
        <Border Padding="16"
                Background="{ThemeResource AcrylicBackgroundFillColorDefaultBrush}"
                BorderBrush="{ThemeResource SurfaceStrokeColorDefaultBrush}"
                BorderThickness="1"
                CornerRadius="{StaticResource OverlayCornerRadius}">
            <TextBlock Text="This popup appears below the button when there's room." />
        </Border>
    </Popup>
</StackPanel>
private void ShowDetailsClicked(object sender, RoutedEventArgs e)
{
    DetailsPopup.IsOpen = true;
}

private void DetailsPopup_ActualPlacementChanged(object sender, object e)
{
    // ActualPlacement differs from DesiredPlacement when the popup
    // doesn't fit in the preferred position.
    PlacementStatus.Text = $"Actual placement: {DetailsPopup.ActualPlacement}";
}

在应用窗口外显示弹出窗口

默认情况下,弹出窗口受限于其 XAML 根的边界,该边界通常是应用窗口的内容区域。 从 Windows 应用 SDK 1.4 开始,可以将 ShouldConstrainToRootBounds 设置为 false,以允许弹出窗口扩展到窗口之外。 例如,当弹出窗口比小窗口还大时,可以执行此操作。

首次打开弹出窗口之前,设置 ShouldConstrainToRootBounds。 打开弹出窗口后,无法更改它。

<Popup x:Name="UnconstrainedPopup" ShouldConstrainToRootBounds="False">
    <!-- Popup content -->
</Popup>

为弹出窗口添加动画

若要在显示时对弹出内容进行动画处理,请向 ChildTransitions 集合添加过渡效果。 PopupThemeTransition 为弹出 UI 提供动画,如工具提示式内容在显示时。

<Popup x:Name="AnimatedPopup" IsLightDismissEnabled="True">
    <Popup.ChildTransitions>
        <TransitionCollection>
            <PopupThemeTransition />
        </TransitionCollection>
    </Popup.ChildTransitions>
    <Border Padding="16"
            Background="{ThemeResource AcrylicBackgroundFillColorDefaultBrush}"
            BorderBrush="{ThemeResource SurfaceStrokeColorDefaultBrush}"
            BorderThickness="1"
            CornerRadius="{StaticResource OverlayCornerRadius}">
        <TextBlock Text="This content animates when the popup opens." />
    </Border>
</Popup>

若要改为通过故事板控制显示和隐藏动画,请使用 PopInThemeAnimation 和 PopOutThemeAnimation。 有关详细信息,请参阅 “显示/隐藏弹出窗口”。

当弹出窗口打开或关闭时做出响应

当 IsOpen 设置为 时,会发生 true 事件。 当 IsOpen 设置为 false 时,会发生 Closed 事件,包括弹出窗口通过点击外部区域关闭时。 将 Closed 用于无论弹出窗口如何关闭都需要执行的工作。 例如,WinUI 3 库示例使用 Closed 来重新启用在弹出窗口打开时被禁用的设置。

这两个事件都使用 EventHandler<object>,因此处理程序接受 object 发送方和 object 事件参数。

<StackPanel Spacing="8">
    <TextBlock x:Name="StatusText" />
    <Popup x:Name="StatusPopup"
           IsLightDismissEnabled="True"
           Opened="StatusPopup_Opened"
           Closed="StatusPopup_Closed">
        <!-- Popup content -->
    </Popup>
</StackPanel>
private void StatusPopup_Opened(object sender, object e)
{
    StatusText.Text = "The popup is open.";
}

private void StatusPopup_Closed(object sender, object e)
{
    // Closed occurs for every close, including light dismiss.
    StatusText.Text = "The popup is closed.";
}

键盘、焦点和辅助功能

  • 启用轻型消失后,打开弹出窗口会将焦点移动到其中,关闭弹出窗口会将焦点返回到之前具有焦点的元素。 用户还可以按 Esc 关闭弹出窗口。
  • 如果未启用轻量关闭,弹出窗口在打开时不会移动焦点。 打开弹出窗口后,将焦点移动到弹出窗口中的元素,包括键盘用户可以访问的 关闭 按钮,并在弹出窗口关闭后将焦点返回到打开该弹出窗口的元素。
  • 弹出窗口不会引发路由事件,例如 KeyDown 和 PointerPressed。 若要处理这些事件,请改为将处理程序附加到弹出窗口的子元素。
  • 确保弹出窗口中的每个交互式元素都有可访问的名称。 有关详细信息,请参阅 键盘辅助功能 并 公开基本辅助功能信息。