尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

WinUI XAML 的 OneCoreTransforms 模式:visual-relative 坐标系与轻量化合成下的坐标空间设计

发布时间:2026/9/16 10:59:29

资讯中心
01
ARTICLE

WinUI XAML 的 OneCoreTransforms 模式:visual-relative 坐标系与轻量化合成下的坐标空间设计

WinUI XAML 的 OneCoreTransforms 模式:visual-relative 坐标系与轻量化合成下的坐标空间设计
WinUI XAML 的 OneCoreTransforms 模式visual-relative 坐标系与轻量化合成下的坐标空间设计【免费下载链接】microsoft-ui-xamlWinUI: a modern UI framework with a rich set of controls and styles to build dynamic and high-performing Windows applications.项目地址: https://gitcode.com/GitHub_Trending/mi/microsoft-ui-xaml本文基于 WinUImicrosoft-ui-xaml仓库中的设计文档 OneCoreTransforms.md深入讲解 XAML 运行时中OneCoreTransformsOCT模式的来龙去脉它为什么存在、visual-relative 坐标系如何成为 XAML 缩放体系北极星、桌面版与轻量化合成lightweight compositing版 Windows 之间坐标/缩放的差异矩阵以及当前源码中 OCT 分支的真实形态。读完后你将能够定位 XAML 中所有与 OCT 相关的开关函数理解XamlOneCoreTransforms::IsEnabled()桩化背后的工程决策并掌握文档提出的保留 OCT 路径、机会性收敛的路线图。什么是 OneCoreTransforms按设计文档的叙述2020 年 4 月在 RS4Release 4时期XAML 团队开始为一套新的标准窗口坐标空间做铺垫。团队创建了一个名为OneCoreTransforms的模式当该模式开启时会告诉开明的组件XAML 即是其中之一在彼此之间改用visual-relative coordinates视觉相对坐标来工作。具体而言不再使用屏幕空间不再调用GetClientRect、ClientToScreen这类 Win32 函数换算坐标坐标改由一个composition visual合成视觉节点及其相对该 visual 原点的一个 X、Y 偏移来定义。当时的目标是让所有版本的 Windows 最终都使用这套 visual-relative 坐标空间但由于优先级调整该模式如今只在部分轻量化合成lightweight compositingSKU 上开启。在 System XAML 中OCT 模式下视觉树的挂载方式也不同运行环境视觉树挂载方式System XAML Desktop 版 CoreWindow基于 CoreWindow 的 HWND 取 composition targetSystem XAML Lightweight Compositing CoreWindow从 CoreWindow 获取一个Composition Island把 XAML 视觉树 parent 到该 Composition Island 上设计文档指出代码库中到处散落着大量if (OneCoreTransforms)开关文档撰写时约 50 处。在没有轻量化合成环境做测试的情况下持续演进 lifted XAML这些代码路径很可能没有被正确维护从而形成技术债。该文档的目的就是梳理这些差异并讨论处理策略。当前源码的印证在今天的仓库中OCT 已经完成了文档路线图之外的一个演进——它被整体桩化了。专门承载该模式的组件 XamlOneCoreTransforms 头部注释写得很直白// _ONECORETRANSFORMS_REMOVED_ // In the past we had a special mode called OneCoreTransforms to help us support // Windows 10x. We expect it to come back in some form when we support 10x gain. // Please see /design-notes/OneCoreTransforms.md for more information. class XamlOneCoreTransforms { public: enum class InitMode { Normal, ForceDisable }; static void EnsureInitialized(InitMode mode); static bool IsEnabled(); static void FailFastIfEnabled(); private: static bool s_enabled; static bool s_initialized; };而 XamlOneCoreTransforms.cpp 中的实现只剩下空壳void XamlOneCoreTransforms::EnsureInitialized(InitMode) { } bool XamlOneCoreTransforms::IsEnabled() { return false; // 恒为 falseOCT 永远关闭 } void XamlOneCoreTransforms::FailFastIfEnabled() { }也就是说当前构建中 OCT 恒为关闭所有调用点保留了如果 OCT 开启应该怎么做的分支逻辑dead path这与文档执行摘要中该模式目前始终关闭但未来支持类似版本的 Windows 时这段代码可以帮我们指路的判断一致且注释进一步说明团队预期它在支持 10x 相关场景时以某种形式回归。类设计本身也保留了工程上的安全带FailFastIfEnabled()在依赖 Win32 屏幕空间 API如ClientToScreen、GetWindowRect的调用入口做快速失败断言防止 OCT 一旦重新启用后这些路径在不知情下错误地运行。术语OCT、lightweight compositing 与 visual-relative coordinates 可以互换设计文档明确在该语境下OneCoreTransformsOCT、lightweight compositing轻量化合成、visual-relative coordinates视觉相对坐标三个术语几乎总是可以互相替换——因为今天 OCT 模式只在基于轻量化合成的版本上启用。理解这一点后读源码时看到这三个词出现的位置指的都是同一套机制。Visual-relative 坐标XAML 缩放的北极星XAML 的长期目标是尽可能多地使用 visual-relative 坐标只在真正需要的那一刻才转换到其他坐标空间。在 XAML 框架内部visual-relative coordinates一般指以 XAML 显示区域左上角为 (0,0) 的坐标空间其刻度单位是所谓的 logical pixels逻辑像素通常等于当前显示器的显示缩放。这一原则在源码中留下了清晰的痕迹。例如 CContentRoot::ShouldUseVisualRelativePixels 是 CContentRoot 对是否使用 visual-relative 像素的表态bool CContentRoot::ShouldUseVisualRelativePixels() { if (XamlOneCoreTransforms::IsEnabled()) { return true; } return false; }当前IsEnabled()恒为 false因此桌面路径返回 false屏幕/物理像素语义但分支结构完整保留OCT 回归时可自动切回 visual-relative 语义。XAML 运行时内部的缩放不一致的 Scale 应用方式设计文档中最有信息密度的一张表列出了 XAML 各种部署形态下缩放是如何被应用的以及 XAML 内部各关键 API 在每种形态下收到的是物理像素还是逻辑像素。这张表是理解 XAML DPI 类 bug 的核心参考完整继承如下表Scale 是不一致的Scales are Inconsistent场景XAML 如何应用缩放PresentTarget 尺寸单位GetGlobalBounds 与 TransformToWorldSpaceCUIElement::TransformToRootSystem XAML Desktop CoreWindowScaleRenderTransform 设置在 XamlRootElement 上Physical物理像素PhysicalPhysicalSystem XAML Lightweight Compositing CoreWindowScale 设置在 XAML 之上的 comp visual 上XAML 继承缩放Logical逻辑像素LogicalLogicalSystem XAML DesktopWindowXamlSourceScale 设置在 XAML 之上的 comp visual 上XAML 继承缩放n/aPhysicalLogicalLifted XAML Desktop CoreWindowScale 设置在 XAML 之上的 comp visual 上XAML 继承缩放XamlRootElement 上设置了 XAML RasterizationScalePhysicalPhysicalLogicalLifted XAML Lightweight Compositing CoreWindow尚未设计Not designedTBDTBDTBDLifted XAML DesktopWindowXamlSourceScale 设置在 XAML 之上的 comp visual 上XAML 继承缩放n/aPhysicalLogical从这张表可以读出两条重要信息同一个 API如TransformToRoot在不同宿主形态下返回的像素语义不同Physical vs Logical这正是文档说的这些差异使得编写正确代码变得困难——任何跨形态复用的坐标换算代码都必须显式知道自己处于哪一行。Lifted XAML即当前 WinUI 3 的 dxaml 架构在 Lightweight Compositing 下的行为标注为尚未设计/TBD印证了执行摘要中目前无法在 lightweight compositing 模式上测试 WinUI 3的说法。源码印证VisualTree 构造时根据 OCT 开关选择完全不同的根缩放策略见 VisualTree.cppif (m_coreContentRoot.GetType() CContentRoot::Type::CoreWindow) { const auto config XamlOneCoreTransforms::IsEnabled() ? RootScaleConfig::ParentApply : RootScaleConfig::ParentInvert; m_rootScale std::make_sharedCoreWindowRootScale(config, m_pCoreNoRef, this); }ParentApplyOCT父级 visual 已携带缩放XAML 直接继承与ParentInvert桌面缩放要反转处理由 XAML 自己用 ScaleRenderTransform/RasterizationScale 承担两种模式正是上表中System XAML Desktop CoreWindow行与Lightweight Compositing CoreWindow行差异的落地实现。如何在 XAML 代码中定位 OCT 开关设计文档给出了在 XAML 代码中查找 OCT 分支的四把钥匙这四处表示法都仍然存在于当前代码库中函数含义当前位置XamlOneCoreTransforms::IsEnabled轻量化合成模式下为 true当前恒为 falseXamlOneCoreTransforms.hShouldUseVisualRelativePixelsXAML 中 CContentRoot 的表示ContentRoot.cppShouldUseVisualPixelsXAML 中 CTextBoxBase 的表示TextBoxBase.cppTxGetShouldUseVisualPixelsXAML 中又一种表示TextServicesHostTextServicesHost.cpp后三者的实现都收敛到同一个源头例如 CTextBoxBase::ShouldUseVisualPixelsbool CTextBoxBase::ShouldUseVisualPixels() // TSF3 and UIA coordinates are based on visual relative pixel { // We should be calling contentRoot-ShouldUseVisualRelativePixels but there is a AppWindow on desktop bug for IME candidate window // For now, only apply visual pixels for TSF3 on WCOS. return XamlOneCoreTransforms::IsEnabled(); }注释透露了历史包袱文本代码本应查询contentRoot-ShouldUseVisualRelativePixels()但因为桌面 AppWindow 下 IME 候选窗口的一个 bug暂且只对 WCOS 上的 TSF3 应用 visual 像素。文本子系统内部则通过 TextServicesHost::TxGetShouldUseVisualPixels 转发给 TextBox形成第三层表示——这就是文档所说多套表示法并存的实据。按文档的说法在 XAML 测试基础设施中部分代码会调用一些私有 API如MapVisualRelativePoints把坐标换算成屏幕位置以便注入输入某些测试特别是可访问性测试在 OCT 模式下期望不同的坐标还有部分测试在 OCT 开或关时直接退出。从当前源码结构看MapVisualRelativePoints这类私有 API 调用点在当前公开仓库的测试代码中已检索不到可以推断它随着测试基建的重写和 OCT 桩化而不再出现但OCT 开启时坐标期望值不同这一测试设计约束仍是将来启用 OCT 前必须回归验证的事项。特殊子系统UIA、DirectManipulation 与文本UIA轻量化合成模式下XAML 通过一个特殊的私有 UIA 接口以 visual-relative 坐标通信该接口未公开文档标注需在内部确认后才能依赖。文档路线图中给出的方案是不要删除支持这些私有接口的代码而是创建一个名字略不同的自有契约使其与现有 UIA 接口 1:1 对齐从而解除对私有接口的直接依赖同时更新那些在 OCT 模式下期望不同坐标结果的自动化测试。DirectManipulation按代码注释DirectManipulation 在 Desktop 版上以屏幕坐标工作在 OCT/轻量化合成版上以visual-relative 坐标工作。这一差异在当前源码 InputServices.cpp 中仍完整保留if (XamlOneCoreTransforms::IsEnabled()) { // In XamlOneCoreTransforms mode we talk to DManip in visual-relative coordinates, no need // to apply the zoom scale. } else { const auto scale RootScale::GetRasterizationScaleForElement(pDMContainer); // Note that this uniform scaling does not affect the determinant test above. inputTransform.Scale(scale, scale); } IFC(pDirectManipulationService-SetViewportInputTransform(pViewport, inputTransform));即OCT 下直接以 visual-relative 坐标与 DManip 通信、无需施加 zoom 缩放桌面下则按元素的 RasterizationScale 缩放 viewport 输入变换。附录还提到OCT 分支曾通过IDirectManipulationManagerPartner::EnableOneCoreTransforms让 DirectManipulation 进入 strict 模式lifted DirectManipulation 预期默认即以 strict 模式运行。从当前源码看EnableOneCoreTransforms的调用点已随 OCT 移除而消失strict 模式成为唯一路径——这正符合附录中内部确认后即可删除相关代码的预判。文本缩放文档对文本代码留了一个明确的 TODO坐标空间处理仍需进一步调研——尤其是传给 TSFText Services Framework的是哪些坐标、TSF 期望哪种坐标空间如果解决了Scaling Today一节列出的坐标变换 API 差异GetGlobalBounds、TransformToWorldSpace、TransformToRoot文本代码可以大幅简化。从源码结构看这条 TODO 并未完全落地文本路径中仍有大量XamlOneCoreTransforms::IsEnabled()分支如 TextBoxView.cpp、RichEditGripperChild.cpp、textrangeadapter.cpp、TextServicesHost.cpp 等说明文本子系统的坐标统一仍是未完成的工作。初始化入口Normal 与 ForceDisableOCT 的初始化有两个入口对应文档Other分类中的两条记录DXamlCore.cppXamlOneCoreTransforms::EnsureInitialized(XamlOneCoreTransforms::InitMode::Normal)—— 正常初始化CoreWindow 路径WindowsXamlManager_Partial.cppXamlOneCoreTransforms::EnsureInitialized(XamlOneCoreTransforms::InitMode::ForceDisable)—— 对应文档附录Force disable in win32 mode (WindowXamlManager_partial.cpp)即 XAML Islandswin32 托管场景下强制关闭 OCT。当前EnsureInitialized是空实现InitMode枚举作为 API 形态保留保证将来 OCT 回归时调用方代码无需改动。建议路线图保留 OCT 路径机会性收敛文档对 WinUI 3.0 的提案是保留 XAML 中的 OneCoreTransforms 代码路径并在可能的时机机会性地向它们收敛——收敛时优先采用 OCT 路径而非非 OCT 路径。这会在发布版本中留下一些 dead code理由是3.0 会在尚无法测试 OCT 平台之前发布但团队希望发布后尽快翻转开关支持这些平台由于当前只能在 OCT OFF 的平台上测试任何重构都无法保证足以支撑 OCT ON 的平台。文档列出的具体计划完整继承与 lifted 组件通信时尽量使用 client-logical 坐标为将来更多使用 visual-relative 坐标铺路。这需要内部协调在 win32 上下文中运行时可能不可行。涉及DirectManipulationTouchHitTesting在合适场景机会性优先 OCT 代码路径。有些场景下 OCT 路径本身就是等价的 WinRT 函数而旧代码的保留理由XamlPresenter 支持、微妙的兼容性顾虑等已不再那么重要。例如OCT 下调用CoreWindow.Bounds获取 CoreWindow 尺寸桌面下调用GetClientRect收敛方式是始终使用CoreWindow.Bounds。机会性减少 XAML 对未收敛缩放函数的使用对应Scale 不一致表。XAML 运行时内部尽可能全部使用逻辑像素停止使用GetGlobalBounds/TransformToWorldSpace改用类似GetGlobalBoundsLogical的接口停止使用CUIElement::TransformToRoot调研将 WindowPresentTarget 尺寸从物理像素改为逻辑像素回顾近期的 DPI 类 bug找出其他有问题的函数。UIA移除对私有 UIA 接口的直接使用创建改名的等价接口内部确认在代码中保留一份私有接口副本可能使用不同 GUID暂不使用是否可接受与其删除支持 UIA 接口的代码不如创建一个名字略有差异、与当前 UIA 接口 1:1 对齐的自有契约更新那些在 OCT 模式下期望不同坐标结果的自动化测试。测试代码在探索使用公开 API 做输入注入的过程中考虑保持 visual-relative 坐标空间更干净的方式。其他轻量化合成考量链接库文档还指出一个容易被忽视的约束XAML 当前链接的某些库在轻量化合成模式下不可用例如user32.lib。这意味着即使所有坐标逻辑都收敛完毕OCT 回归前还需清理依赖面——任何仍在 OCT 开启路径上调 user32 的代码都会被FailFastIfEnabled()类断言暴露出来当前代码库中这类断言注释明确写着// Due to ClientToScreen call、// Due to GetWindowRect等见 DXamlCore.cpp 与 UIAHostEnvironmentInfo.cpp。附录XAML 对 OCT 模式的使用清单2020 年 4 月快照以下按文档附录的分类完整列出 XAML 使用 OCT 模式的位置清单并标注其中在当前源码中仍能直接找到调用点的代表性位置以dxaml/xcp/为前缀的仓库相对路径。这份清单本身就是约 50 个 OCT 开关的技术债台账。UIA未公开暴露依赖此信息前请先内部确认。使用 visual-relative 的 UIA 接口。Direct Manipulationlifted DirectManipulation 预期默认以 strict 模式运行删除代码前请内部确认。告知 DirectManipulation 使用 strict 模式IDirectManipulationManagerPartner::EnableOneCoreTransforms告知 DirectManipulation 使用 strict 模式CInputServices::CreateViewportInteractionForRootVisualTouchHitTestingTouchHitTesting 使用不同的坐标空间既然已 lifted该怎么办——当前代码中 TouchHitTestingHandler.cpp 仍以bool isVisualRelative XamlOneCoreTransforms::IsEnabled();分支处理。坐标空间差异缩放/偏移此处有些东西可以拆解哪些是因为其他组件在不同坐标空间工作造成的哪些是因为 XAML 以不同方式安排树对于 XAML 安排树不同的场景OCT 与 islands 模式下不应当一致吗缩放处理不同CJupiterControl::UpdateHdr转换为正确缩放ListViewBaseHeaderItemAutomationPeer_partial.cpp缩放处理不同TextCTextRangeAdapter::GetBoundingRectanglestextrangeadapter.cpp缩放处理不同TextCRichEditGripperChild::UpdateCenterWorldCoordinateRichEditGripperChild.cpp缩放处理不同TextCTextServicesHost::ShowGripperTextServicesHost.cpp其中applyRasterizationScale !XamlOneCoreTransforms::IsEnabled()缩放处理不同TextCTextBoxView::TxGetViewportRectTextBoxView.cpp缩放处理不同TextCTextBox::GetRectFromCharacterIndexTextBox.cpp缩放处理不同TextHyperlinkAutomationPeer::GetBoundingRectangleCore缩放处理不同UIAFrameworkElementAutomationPeer::GetBoundingRectangleCoreFrameworkElementAutomationPeer_partial.cpp缩放处理不同UIACUIElement::GetClickablePointRasterizedClientuielement.cpp缩放处理不同、配置根缩放VisualTree::VisualTreeVisualTree.cpp缩放处理不同CUIElement::GetRedirectionTransformsAndParentCompNode不获取桌面偏移AutoSuggestBox::GetAdjustedLayoutBoundsAutoSuggestBox_Partial.cpp窗口化 popup 不施加偏移DXamlCore::GetTranslationFromTargetWindowToRootWindow缩放处理不同DmanipCInputServices::UpdateManipulationViewportInputServices.cpp缩放处理不同软件键盘CInputPaneHandler::BringFocusedElementIntoViewBringIntoViewHandler.cppIsland 中应用缩放的方式不同注册缩放变更通知DXamlCore修复 OCT 的 scale/bounds 同步问题DXamlCore::UpdateScaleFactorDXamlCore.cpp在根元素上设置正确缩放HWWalk::RenderPropertieshwwalk.cppisRootAndOneCoreStrict挂接到 CoreWindow 的 comp island获取/连接 CoreWindow 的 islandCJupiterWindow::SetCoreWindowJupiterWindow.cpp获取/连接 CoreWindow 的 islandDCompTreeHostDCompTreeHost.cpp使用 WinRT API 代替 Win32 API看上去其中大部分都可以迁到 WinRT 选项不是吗可能是当时担心兼容性破坏而 WinRT 版本其实是没问题的如果它向下兼容到 RS4。用 CoreWindow bounds 而非GetClientRect取尺寸CJupiterWindow::GetJupiterWindowPhysicalSize订阅CoreWindow.Activated桌面下使用WM_ACTIVATECJupiterWindow::RegisterCoreWindowEventsHDR 使用 DisplayInformationCJupiterControl::UpdateHdrJupiterControl.cpp使用 WinRT PointerPoint 而非 Win32POINTER_INFOCInputServices::ProcessPointerExitedEventByPointerEnteredElementStateChange决定使用 WinRT DManip 命中测试CJupiterWindow::UseDirectManipulationHitTestEvent软件键盘软件键盘使用FrameworkInputView而非 InputPaneCInputPaneInteractionHelper::RegisterInputPaneHandlerInputPaneInteractionHelper.cpp仍以ShouldUseVisualRelativePixels()分支以useVisualRelativePixelstrue初始化 CInputPaneHandlerInputPaneProcessorInputPaneProcessor.cpp禁用与 SIP 的遮挡检测ContentDialog::AdjustVisualStateForInputPan其他测试 hookShrinkApplicationViewVisibleBounds用于检测 win32xaml islands托管SemanticZoom_partial.cppSemanticZoom_Partial.cpp桌面版确保 OCT 关闭Page_partial.cpp不配置 CoreWindow bounds 行为DXamlCore判断是否全屏CMediaElement::InvokeImpl不同的 SetCapture/ReleaseCapture 方式PointerInputProcessorPointerInputProcessor.cppwin32 模式下强制禁用WindowXamlManager_partial.cpp即上文ForceDisable初始化入口拖放XamlMobileDragOperation.cpp文本代码调用CTextBoxBase::ShouldUseVisualPixels以不同方式处理缩放总结这篇设计文档对今天的价值把文档与当前源码对照可以勾勒出 OneCoreTransforms 的完整生命周期起源RS4 时期为统一的 visual-relative 标准坐标空间而设计最终只在 lightweight compositing SKU 上启用策略WinUI 3.0 决定保留全部 OCT 分支、机会性向 OCT 路径收敛宁可留下 dead code 也不赌不可测试的重构现状OCT 已被整体桩化IsEnabled()恒为 false_ONECORETRANSFORMS_REMOVED_标记但约 30 个源文件中仍完整保留着如果 OCT 开启的分支逻辑从根缩放配置RootScaleConfig::ParentApply/ParentInvert、DManip 坐标约定、文本/TSF 坐标空间到 UIA 边界矩形换算对开发者的实际意义如果你在 XAML 运行时中调试 DPI/缩放/输入坐标类问题XamlOneCoreTransforms::IsEnabled()、ShouldUseVisualRelativePixels、ShouldUseVisualPixels、TxGetShouldUseVisualPixels这四个函数是所有坐标空间分叉点的总索引FailFastIfEnabled()调用点则标出了所有隐性依赖 Win32 屏幕空间的位置——这两类标记共同构成了将来 OCT或任何 visual-relative 化重构回归时的最小回归面。需要说明的适用前提本仓库是 lifted XAMLdxaml架构对应 WinUI 3设计文档写于 2020 年 4 月部分细节如私有 UIA 接口、MapVisualRelativePoints测试基建、EnableOneCoreTransforms调用点在当前公开代码中已检索不到属于已演进/移除的部分文中均已按当前源码事实与文档历史快照区分表述。【免费下载链接】microsoft-ui-xamlWinUI: a modern UI framework with a rich set of controls and styles to build dynamic and high-performing Windows applications.项目地址: https://gitcode.com/GitHub_Trending/mi/microsoft-ui-xaml创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。