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

如何在UWP中创建浮动ContentDialog及可拖动的MessageBox?

嘿,这个需求我之前做项目的时候刚好研究过,给你分享两种靠谱的实现思路:一种是改造原生ContentDialog让它能拖动,另一种是用Popup做非模态的浮动窗口,按需选就行~

一、方案1:自定义可拖动的ContentDialog(模态)

原生ContentDialog默认是模态的(会阻塞主窗口操作),但我们可以修改它的模板,给标题栏加上拖动逻辑,让用户能拖动它的位置。

步骤1:自定义ContentDialog的模板

先在你的页面资源或者全局ResourceDictionary里定义一个带可拖动标题栏的模板:

<ControlTemplate x:Key="DraggableContentDialogTemplate" TargetType="ContentDialog">
    <Border x:Name="Container" Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}"
            CornerRadius="8">
        <Grid>
            <!-- 可拖动的标题栏 -->
            <Grid x:Name="TitleBar" Height="48" Background="Transparent">
                <TextBlock x:Name="Title" Margin="16,0"
                           VerticalAlignment="Center"
                           FontSize="18"
                           Text="{TemplateBinding Title}"
                           FontWeight="SemiBold"/>
                <Button x:Name="CloseButton" HorizontalAlignment="Right"
                        Margin="0,8,8,8" Style="{StaticResource CloseButtonStyle}"/>
            </Grid>
            <!-- 内容滚动区域 -->
            <ScrollViewer x:Name="ContentScrollViewer" Margin="0,48,0,0"
                          HorizontalScrollBarVisibility="Auto"
                          VerticalScrollBarVisibility="Auto">
                <ContentPresenter x:Name="ContentPresenter"
                                  Content="{TemplateBinding Content}"
                                  Margin="16"
                                  ContentTemplate="{TemplateBinding ContentTemplate}"/>
            </ScrollViewer>
            <!-- 底部按钮栏 -->
            <StackPanel x:Name="ButtonPanel" Orientation="Horizontal"
                        HorizontalAlignment="Right" Margin="16,0,16,16"
                        VerticalAlignment="Bottom">
                <Button x:Name="PrimaryButton" Style="{StaticResource AccentButtonStyle}"
                        Content="{TemplateBinding PrimaryButtonText}"
                        IsEnabled="{TemplateBinding IsPrimaryButtonEnabled}"/>
                <Button x:Name="SecondaryButton" Margin="8,0,0,0"
                        Content="{TemplateBinding SecondaryButtonText}"
                        IsEnabled="{TemplateBinding IsSecondaryButtonEnabled}"/>
            </StackPanel>
        </Grid>
    </Border>
</ControlTemplate>

步骤2:写拖动逻辑的代码

创建一个继承自ContentDialog的自定义类,把拖动逻辑封装进去:

public class DraggableContentDialog : ContentDialog
{
    private Point _dragStartPoint;
    private bool _isDragging;

    public DraggableContentDialog()
    {
        // 应用自定义模板
        Template = Application.Current.Resources["DraggableContentDialogTemplate"] as ControlTemplate;
    }

    protected override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        // 找到标题栏控件,绑定鼠标事件
        var titleBar = GetTemplateChild("TitleBar") as Grid;
        if (titleBar == null) return;

        titleBar.PointerPressed += TitleBar_PointerPressed;
        titleBar.PointerMoved += TitleBar_PointerMoved;
        titleBar.PointerReleased += TitleBar_PointerReleased;
        titleBar.PointerExited += TitleBar_PointerExited;
    }

    private void TitleBar_PointerPressed(object sender, PointerRoutedEventArgs e)
    {
        var pointerPoint = e.GetCurrentPoint(this);
        _dragStartPoint = pointerPoint.Position;
        _isDragging = true;
        // 捕获鼠标,避免拖动时失去焦点
        (sender as UIElement).CapturePointer(pointerPoint.Pointer);
        e.Handled = true;
    }

    private void TitleBar_PointerMoved(object sender, PointerRoutedEventArgs e)
    {
        if (!_isDragging) return;

        var pointerPoint = e.GetCurrentPoint(this);
        var currentPos = pointerPoint.Position;
        // 计算偏移量
        var offsetX = currentPos.X - _dragStartPoint.X;
        var offsetY = currentPos.Y - _dragStartPoint.Y;

        // 更新对话框的Margin来实现移动
        Margin = new Thickness(
            Margin.Left + offsetX,
            Margin.Top + offsetY,
            Margin.Right - offsetX,
            Margin.Bottom - offsetY);
        e.Handled = true;
    }

    private void TitleBar_PointerReleased(object sender, PointerRoutedEventArgs e)
    {
        _isDragging = false;
        (sender as UIElement).ReleasePointerCapture(e.Pointer);
        e.Handled = true;
    }

    private void TitleBar_PointerExited(object sender, PointerRoutedEventArgs e)
    {
        // 鼠标离开标题栏时停止拖动
        _isDragging = false;
        (sender as UIElement).ReleasePointerCapture(e.Pointer);
        e.Handled = true;
    }
}

步骤3:使用自定义对话框

在页面里直接实例化调用就行,和原生ContentDialog用法一致:

var dialog = new DraggableContentDialog
{
    Title = "可拖动的对话框",
    Content = "试试拖动标题栏移动我呀~",
    PrimaryButtonText = "确定",
    SecondaryButtonText = "取消"
};

// 显示对话框(还是模态的,会阻塞主窗口)
await dialog.ShowAsync();
二、方案2:用Popup实现非模态浮动窗口

如果想要完全浮动、不阻塞主窗口操作的效果,用Popup会更合适,它可以自由控制位置,而且是非模态的,体验更像独立的MessageBox。

步骤1:定义Popup的UI结构

在你的Page里添加Popup和内部的对话框内容:

<Popup x:Name="FloatingMsgBox" IsOpen="False" Placement="Center">
    <Border Width="320" Height="220" Background="White" CornerRadius="8"
            BorderBrush="#E0E0E0" BorderThickness="1"
            ShadowEffect.Color="#33000000" ShadowEffect.BlurRadius="12">
        <Grid>
            <!-- 可拖动的标题栏 -->
            <Grid x:Name="PopupTitleBar" Height="44" Background="#F8F8F8">
                <TextBlock Margin="16,0" VerticalAlignment="Center"
                           Text="浮动提示框" FontSize="16" FontWeight="SemiBold"/>
                <Button x:Name="ClosePopupBtn" Width="36" Height="36"
                        HorizontalAlignment="Right" Style="{StaticResource TransparentButtonStyle}"
                        Click="ClosePopupBtn_Click">
                    <SymbolIcon Symbol="Close" Foreground="#666"/>
                </Button>
            </Grid>
            <!-- 内容区域 -->
            <StackPanel Margin="16" VerticalAlignment="Center">
                <TextBlock Text="这是一个可以自由拖动的非模态浮动窗口~"
                           TextWrapping="Wrap"/>
            </StackPanel>
        </Grid>
    </Border>
</Popup>

步骤2:添加拖动逻辑

在Page的代码-behind里写拖动相关的事件:

private Point _popupDragStart;
private bool _isPopupDragging;

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    // 绑定标题栏的鼠标事件
    PopupTitleBar.PointerPressed += PopupTitleBar_PointerPressed;
    PopupTitleBar.PointerMoved += PopupTitleBar_PointerMoved;
    PopupTitleBar.PointerReleased += PopupTitleBar_PointerReleased;
}

private void PopupTitleBar_PointerPressed(object sender, PointerRoutedEventArgs e)
{
    var pointerPoint = e.GetCurrentPoint(FloatingMsgBox.Child);
    _popupDragStart = pointerPoint.Position;
    _isPopupDragging = true;
    (sender as UIElement).CapturePointer(pointerPoint.Pointer);
    e.Handled = true;
}

private void PopupTitleBar_PointerMoved(object sender, PointerRoutedEventArgs e)
{
    if (!_isPopupDragging || !FloatingMsgBox.IsOpen) return;

    var pointerPoint = e.GetCurrentPoint(Window.Current.Content as UIElement);
    var currentPos = pointerPoint.Position;
    // 计算偏移量
    var offsetX = currentPos.X - _popupDragStart.X;
    var offsetY = currentPos.Y - _popupDragStart.Y;

    // 更新Popup的位置
    FloatingMsgBox.HorizontalOffset = offsetX;
    FloatingMsgBox.VerticalOffset = offsetY;
    e.Handled = true;
}

private void PopupTitleBar_PointerReleased(object sender, PointerRoutedEventArgs e)
{
    _isPopupDragging = false;
    (sender as UIElement).ReleasePointerCapture(e.Pointer);
    e.Handled = true;
}

// 关闭Popup的方法
private void ClosePopupBtn_Click(object sender, RoutedEventArgs e)
{
    FloatingMsgBox.IsOpen = false;
}

// 打开浮动窗口的方法
private void OpenFloatingMsgBox()
{
    // 初始化到屏幕中心
    var screenBounds = Window.Current.Bounds;
    FloatingMsgBox.HorizontalOffset = (screenBounds.Width - 320) / 2;
    FloatingMsgBox.VerticalOffset = (screenBounds.Height - 220) / 2;
    FloatingMsgBox.IsOpen = true;
}
三、额外小技巧

如果你的应用是Windows 10 1809及以上的桌面桥接应用,还可以用AppWindow来创建完全独立的浮动窗口,它自带系统级的拖动、最小化/最大化功能,体验和原生MessageBox几乎一样,而且完全非模态,适合更复杂的场景。


内容的提问来源于stack exchange,提问作者Shakir Ahmed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:19:26