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

C++/WinRT WinUI3中如何获取自定义标题栏元素实时坐标

C++/WinRT WinUI3 获取元素坐标的问题

问题描述

  • 调用m_mainWindow.MyBut().ActualOffset()或m_mainWindow.MyBut().TransformToVisual()获取的坐标值始终为0,仅当窗口尺寸改变时,坐标属性值才会更新为正确值。
  • 已查阅Button相关官方文档,希望了解如何正确获取元素坐标,不确定是否存在C++/WinRT与WinUI的适配问题。

相关代码

#include "pch.h"
#include "TitlebarPage.xaml.h"
#if __has_include("TitlebarPage.g.cpp")
#include "TitlebarPage.g.cpp"
#endif

using namespace winrt;
using namespace Windows::UI;
using namespace Microsoft::UI::Xaml;
using namespace Microsoft::UI::Xaml::Controls;
using namespace Microsoft::UI::Xaml::Navigation;
using namespace Microsoft::UI::Input;


namespace winrt::SampleApp::implementation
{

    winrt::hstring defaultTitle = L"WinUI C++ Desktop sample App";

    TitlebarPage::TitlebarPage()
    {
        InitializeComponent();
        TitlebarCustomBtn().PointerPressed({this, 
        &TitlebarPage::TitlebarCustomBtn_Click});
        TitlebarCustomBtn().PointerPressed({ this, 
        &TitlebarPage::TitlebarCustomBtn_Click });
    }
  
    void TitlebarPage::OnNavigatedTo(NavigationEventArgs const& e)
    {
            m_mainWindow = e.Parameter().as<MainWindow>();
            
            AppWindow appWindow = m_mainWindow.MyAppWindow();
            m_appWindow = appWindow;
            m_changedToken = m_appWindow.Changed({ this, 
            &TitlebarPage::AppWindowChangedHandler });          
    }

    void TitlebarPage::AppWindowChangedHandler(winrt::Windows::Foundation::IInspectable 
    const& sender, winrt::Microsoft::UI::Windowing::AppWindowChangedEventArgs const& e)
    {
        if (e.DidSizeChange() 
         && sender.as<AppWindow>().TitleBar().ExtendsContentIntoTitleBar())
        {
            SetCustomTitleBarDragRegion();
        }
    }

    void TitlebarPage::TitlebarBrandingBtn_Click(
    winrt::Windows::Foundation::IInspectable const& /*sender*/, 
    winrt::Microsoft::UI::Xaml::RoutedEventArgs const& /*e*/)
    {
        m_appWindow.TitleBar().ResetToDefault();
        m_mainWindow.MyTitleBar().
        Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);

        m_brandTitleBar = !m_brandTitleBar;
   
        if (AppWindowTitleBar::IsCustomizationSupported() && m_brandTitleBar)
        {
            m_appWindow.Title(L"Default titlebar with custom color customization");
            m_appWindow.TitleBar().ForegroundColor(Colors::Transparent());
            m_appWindow.TitleBar().BackgroundColor(Colors::Transparent());
            m_appWindow.TitleBar().InactiveBackgroundColor(Colors::Transparent());
            m_appWindow.TitleBar().InactiveForegroundColor(Colors::Transparent());

            //Buttons
            m_appWindow.TitleBar().ButtonBackgroundColor(Colors::Transparent());
            m_appWindow.TitleBar().ButtonForegroundColor(Colors::Transparent());
            m_appWindow.TitleBar().
            ButtonInactiveBackgroundColor(Colors::Transparent());
            m_appWindow.TitleBar().
            ButtonInactiveForegroundColor(Colors::Transparent());
            m_appWindow.TitleBar().ButtonHoverBackgroundColor(Colors::Transparent());
            m_appWindow.TitleBar().ButtonHoverForegroundColor(Colors::Transparent());
            m_appWindow.TitleBar().ButtonPressedBackgroundColor(Colors::Transparent());
            m_appWindow.TitleBar().ButtonPressedForegroundColor(Colors::Transparent());   
        }
        else
        {
            m_appWindow.TitleBar().ResetToDefault();
            m_appWindow.Title(defaultTitle);
        }
    }

    void TitlebarPage::TitlebarCustomBtn_Click(winrt::Windows::Foundation::IInspectable 
     const& /*sender*/, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& /*e*/)
    {

        if (AppWindowTitleBar::IsCustomizationSupported() && !m_customTitleBar) {
            m_customTitleBar = true;
            m_appWindow.TitleBar().ExtendsContentIntoTitleBar(true);

            // Show the custom titlebar
            m_mainWindow.MyTitleBar().
            Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);

            // Set Button colors to match the custom titlebar
            m_appWindow.TitleBar().ButtonBackgroundColor(Colors::Transparent());
            m_appWindow.TitleBar().ButtonForegroundColor(Colors::Gray());
            m_appWindow.TitleBar().
            ButtonInactiveBackgroundColor(Colors::Transparent());
            m_appWindow.TitleBar().ButtonInactiveForegroundColor(Colors::Gray());
            m_appWindow.TitleBar().ButtonHoverBackgroundColor(Colors::Transparent());
            m_appWindow.TitleBar().ButtonHoverForegroundColor(Colors::Transparent());
            m_appWindow.TitleBar().ButtonPressedBackgroundColor(Colors::Transparent());
            m_appWindow.TitleBar().ButtonPressedForegroundColor(Colors::Gray());               
            m_appWindow.TitleBar().
            IconShowOptions(IconShowOptions::HideIconAndSystemMenu);
            //Set the drag region for the custom titlebar
            SetCustomTitleBarDragRegion();
        }
        else
        {
            // Bring back the default titlebar
            m_customTitleBar = false;
        m_mainWindow.MyTitleBar().
        Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
            m_appWindow.TitleBar().ResetToDefault();
        }
    }

    void TitlebarPage::ResetTitlebarBtn_Click(winrt::Windows::Foundation::IInspectable 
    const& /*sender*/, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& /*e*/)
    {       
    m_mainWindow.MyTitleBar().Visibility(
    winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
        m_appWindow.TitleBar().ResetToDefault();
        m_appWindow.Title(defaultTitle);
    }

    void TitlebarPage::SetCustomTitleBarDragRegion()
    {

        double scaleAdjustment = 
        m_mainWindow.MyTitleBar().XamlRoot().RasterizationScale();
        double scaleAdjustment2 =m_mainWindow.MyBut().XamlRoot().RasterizationScale();

        GeneralTransform transform = m_mainWindow.MyBut().TransformToVisual(nullptr);
        
        static Rect bunds;
         bunds = transform.TransformBounds(Rect(0, 0, 
                                                m_mainWindow.MyBut().ActualWidth(), 
                                                m_mainWindow.MyBut().ActualHeight()));

        std::vector<Windows::Graphics::RectInt32> dragRects;
        Windows::Graphics::RectInt32 MyButRect;

        MyButRect.X = static_cast<int>(bunds.X * scaleAdjustment);
        MyButRect.Y = static_cast<int>(bunds.Y * scaleAdjustment);
        MyButRect.Height = static_cast<int>(bunds.Height * scaleAdjustment);
        MyButRect.Width = static_cast<int>(bunds.Width * scaleAdjustment);
        dragRects.push_back(MyButRect);

        InputNonClientPointerSource nonClientInputSrc = 
        InputNonClientPointerSource::GetForWindowId(m_appWindow.Id());
        nonClientInputSrc.SetRegionRects(NonClientRegionKind::Passthrough, dragRects);
    }
}

解决方案

问题根源

调用坐标获取方法时,目标元素尚未完成布局渲染,此时获取到的是初始默认值0;窗口尺寸改变会触发UI布局更新,元素完成渲染后才能拿到正确坐标。

具体修复步骤

  1. 延迟到元素加载完成后获取坐标
    在元素的Loaded事件中执行坐标获取逻辑,确保UI已完成初始化:

    // 在TitlebarPage构造函数中添加
    TitlebarPage::TitlebarPage()
    {
        InitializeComponent();
        TitlebarCustomBtn().PointerPressed({this, &TitlebarPage::TitlebarCustomBtn_Click});
        // 注册MyBut的Loaded事件
        m_mainWindow.MyBut().Loaded([this](auto const&, auto const&) {
            this->SetCustomTitleBarDragRegion();
        });
    }
    
  2. 监听布局变化动态更新坐标
    若元素位置可能动态改变,监听LayoutUpdated事件,每次布局更新后重新计算坐标:

    // 在OnNavigatedTo中添加
    void TitlebarPage::OnNavigatedTo(NavigationEventArgs const& e)
    {
        m_mainWindow = e.Parameter().as<MainWindow>();
        // ... 现有代码 ...
        // 注册布局更新事件
        m_mainWindow.MyBut().LayoutUpdated([this](auto const&, auto const&) {
            this->SetCustomTitleBarDragRegion();
        });
    }
    
  3. 修正SetCustomTitleBarDragRegion方法
    移除静态变量避免缓存旧值,统一使用元素自身的缩放比例:

    void TitlebarPage::SetCustomTitleBarDragRegion()
    {
        auto myBut = m_mainWindow.MyBut();
        if (!myBut) return;
    
        auto xamlRoot = myBut.XamlRoot();
        if (!xamlRoot) return;
    
        double scaleAdjustment = xamlRoot.RasterizationScale();
    
        GeneralTransform transform = myBut.TransformToVisual(nullptr);
        Rect bunds = transform.TransformBounds(Rect(0, 0, 
            myBut.ActualWidth(), 
            myBut.ActualHeight()));
    
        std::vector<Windows::Graphics::RectInt32> dragRects;
        Windows::Graphics::RectInt32 MyButRect;
    
        MyButRect.X = static_cast<int>(bunds.X * scaleAdjustment);
        MyButRect.Y = static_cast<int>(bunds.Y * scaleAdjustment);
        MyButRect.Height = static_cast<int>(bunds.Height * scaleAdjustment);
        MyButRect.Width = static_cast<int>(bunds.Width * scaleAdjustment);
        dragRects.push_back(MyButRect);
    
        InputNonClientPointerSource nonClientInputSrc = 
            InputNonClientPointerSource::GetForWindowId(m_appWindow.Id());
        if (nonClientInputSrc) {
            nonClientInputSrc.SetRegionRects(NonClientRegionKind::Passthrough, dragRects);
        }
    }
    
  4. 切换标题栏后强制触发布局更新
    在显示自定义标题栏后,手动触发UI布局更新,再调用坐标计算方法:

    void TitlebarPage::TitlebarCustomBtn_Click(...)
    {
        if (AppWindowTitleBar::IsCustomizationSupported() && !m_customTitleBar) {
            // ... 现有代码 ...
            m_mainWindow.MyTitleBar().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
            // 强制更新布局
            m_mainWindow.MyTitleBar().UpdateLayout();
            m_mainWindow.MyBut().UpdateLayout();
            // 调用坐标计算方法
            SetCustomTitleBarDragRegion();
        }
        // ... 现有代码 ...
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 16:30:58