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

MAUI Blazor自定义标题栏拖动窗口问题:释放鼠标后仍持续移动

MAUI Blazor窗口拖动后释放鼠标仍持续移动的问题解决

你的问题核心是同步阻塞的while循环卡死了UI线程,导致MouseUp事件无法及时更新isDown的状态,循环会一直执行直到资源耗尽或者程序异常。以下是具体的问题分析和修复方案:

问题根源

DragWindow里的while(isDown)是同步执行的,会独占UI线程:

  1. 鼠标释放时触发的MouseUp事件因为UI线程被阻塞,无法及时将isDown设为false
  2. 循环没有任何延迟,会高频调用窗口移动API,即使isDown后续被修改,循环也无法立刻感知

修复方案

将同步循环改为异步执行,同时优化拖动逻辑的精准度:

修改后的ViewModel代码

private bool isDown;
private Windows.Graphics.PointInt32 _windowStartPos;
private System.Drawing.Point _mouseStartPos;

public async void MouseDown()
{
    isDown = true;
#if WINDOWS
    var mauiWindow = App.Current.Windows.First();
    var nativeWindow = mauiWindow.Handler.PlatformView;
    IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(nativeWindow);
    WindowId windowId = Win32Interop.GetWindowIdFromWindow(windowHandle);
    AppWindow appWindow = AppWindow.GetFromWindowId(windowId);
    
    // 记录拖动起始状态:窗口位置、鼠标位置
    _windowStartPos = appWindow.Position;
    _mouseStartPos = GetCursorPosition();
#endif
    await DragWindow();
}

public void MouseUp()
{
    isDown = false;
}

private async Task DragWindow()
{
#if WINDOWS
    var mauiWindow = App.Current.Windows.First();
    var nativeWindow = mauiWindow.Handler.PlatformView;
    IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(nativeWindow);
    WindowId windowId = Win32Interop.GetWindowIdFromWindow(windowHandle);
    AppWindow appWindow = AppWindow.GetFromWindowId(windowId);

    while (isDown)
    {
        var currentMousePos = GetCursorPosition();
        // 通过偏移量计算新窗口位置,避免固定值导致的拖动错位
        int offsetX = currentMousePos.X - _mouseStartPos.X;
        int offsetY = currentMousePos.Y - _mouseStartPos.Y;
        appWindow.Move(new Windows.Graphics.PointInt32(_windowStartPos.X + offsetX, _windowStartPos.Y + offsetY));
        
        // 延迟16ms(约60帧/秒),给UI线程留处理事件的时间,同时降低资源消耗
        await Task.Delay(16);
    }
#endif
}

// Win32 API 获取鼠标位置
private System.Drawing.Point GetCursorPosition()
{
    Win32.GetCursorPos(out POINT lpPoint);
    return new System.Drawing.Point(lpPoint.X, lpPoint.Y);
}

[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
private struct POINT
{
    public int X;
    public int Y;
}

private static class Win32
{
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    public static extern bool GetCursorPos(out POINT lpPoint);
}

Razor代码(保持不变)

<div @onpointerdown="() => topbarVM.MouseDown()" @onpointerup="() => topbarVM.MouseUp()" class="flex-row fixed-top p-0 m-0">
    <!-- 标题栏内容 -->
</div>

关键修改点说明

  1. 异步执行拖动逻辑:用async/await将拖动循环放到后台执行,避免阻塞UI线程,确保MouseUp事件能及时修改isDown状态
  2. 精准偏移计算:记录鼠标按下时的窗口和鼠标初始位置,通过偏移量移动窗口,拖动体验更自然
  3. 添加循环延迟:Task.Delay(16)既保证拖动流畅性,又让UI线程有机会处理用户输入事件,循环能及时感知isDown的变化

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 10:10:35