[{"data":1,"prerenderedAt":4465},["ShallowReactive",2],{"page-\u002Fcsharp\u002Favalonia\u002Favalonia的渲染系统":3},{"id":4,"title":5,"body":6,"description":12,"extension":4459,"meta":4460,"navigation":935,"path":4461,"seo":4462,"stem":4463,"__hash__":4464},"content\u002Fcsharp\u002FAvalonia\u002FAvalonia的渲染系统.md","Avalonia的渲染系统",{"type":7,"value":8,"toc":4439},"minimark",[9,13,30,37,97,102,105,111,114,117,207,210,229,239,243,248,454,457,467,537,540,549,552,558,561,565,568,917,921,1045,1057,1085,1088,1096,1099,1117,1120,1139,1151,1154,1158,1316,1339,1342,1375,1378,1386,1390,1401,1414,1471,1475,1487,1493,1498,1539,1553,1557,1628,1644,1647,1665,1668,1681,1687,1690,1698,1703,1722,1772,1775,1783,1786,1790,1794,1803,1806,1864,1877,1902,1907,1939,1950,1960,1964,1970,1973,1977,1983,1987,1996,1999,2003,2012,2015,2018,2022,2208,2211,2214,2218,2238,2295,2297,2301,2353,2356,2447,2449,2453,2516,2577,2579,2583,2621,2716,2719,2730,2772,2775,2997,3012,3028,3042,3046,3053,3056,3123,3126,3144,3154,3157,3193,3196,3216,3219,3250,3253,3281,3284,3294,3297,3307,3310,3322,3325,3337,3341,3352,3356,3369,3373,3382,3386,3395,3398,3407,3446,3450,3467,3470,3474,3485,3489,3500,3504,3515,3519,3526,3529,3533,3538,3562,3566,3571,3593,3597,3605,3609,3626,3629,3640,3660,3689,3706,3713,3727,3730,3749,3753,3760,3767,3900,3910,4035,4038,4062,4073,4084,4157,4160,4163,4167,4174,4178,4185,4223,4230,4327,4330,4340,4359,4369,4400,4435],[10,11,12],"p",{},"​\t在了解avalonia的渲染系统之前我们需要先了解一下avlaonia的组件系统，所有的组加都继承于一个叫做Control的基类，代码为：",[14,15,20],"pre",{"className":16,"code":17,"language":18,"meta":19,"style":19},"language-csharp shiki shiki-themes github-light github-dark","public class Control : InputElement, IDataTemplateHost, IVisualBrushInitialize, ISetterValue\n","csharp","",[21,22,23],"code",{"__ignoreMap":19},[24,25,28],"span",{"class":26,"line":27},"line",1,[24,29,17],{},[10,31,32,33,36],{},"这表明 ",[21,34,35],{},"Control"," 具有以下几个关键特性：",[38,39,40,54,66,85],"ol",{},[41,42,43,50,51,53],"li",{},[44,45,46,47],"strong",{},"继承自 ",[21,48,49],{},"InputElement",": 这意味着 ",[21,52,35],{}," 具备处理输入（如鼠标、键盘、触摸）的能力，这是所有可交互 UI 元素的基础。",[41,55,56,62,63,65],{},[44,57,58,59],{},"实现 ",[21,60,61],{},"IDataTemplateHost",": 这与数据模板（Data Templates）相关，意味着 ",[21,64,35],{}," 或其派生类可以作为数据模板的应用宿主，例如用于显示数据集合的容器控件。",[41,67,68,73,74,77,78,80,81,84],{},[44,69,58,70],{},[21,71,72],{},"IVisualBrushInitialize",": 这可能与 ",[21,75,76],{},"VisualBrush"," 的初始化过程有关，",[21,79,76],{}," 是一种特殊的画刷，它使用另一个控件（",[21,82,83],{},"Visual","）的内容来绘制区域。",[41,86,87,92,93,96],{},[44,88,58,89],{},[21,90,91],{},"ISetterValue",": 这可能与样式系统（Styles）中的 ",[21,94,95],{},"Setter"," 设置值的能力有关，允许控件在样式系统中作为可设置值的目标或值的来源。",[98,99,101],"h3",{"id":100},"inputelment","InputElment",[10,103,104],{},"这个关于组件的输入处理，采用了分层架构，从硬件到高层的控件事件形成了完整的处理链条：",[106,107],"img",{"src":108,"alt":109,"style":110},".\u002Fassets\u002Fimage-20251022225827879.png","image-20251022225827879 20","zoom:50%;",[10,112,113],{},"这个叫做输入元素基类，而InputManager则是输入事件的调度中心。",[10,115,116],{},"Avalonia提供了丰富的键盘事件支持：",[14,118,120],{"className":16,"code":119,"language":18,"meta":19,"style":19},"\u002F\u002F 键盘事件定义示例\npublic static readonly RoutedEvent\u003CKeyEventArgs> KeyDownEvent =\n    RoutedEvent.Register\u003CInputElement, KeyEventArgs>(\n        nameof(KeyDown),\n        RoutingStrategies.Tunnel | RoutingStrategies.Bubble);\n \npublic static readonly RoutedEvent\u003CKeyEventArgs> KeyUpEvent =\n    RoutedEvent.Register\u003CInputElement, KeyEventArgs>(\n        nameof(KeyUp),\n        RoutingStrategies.Tunnel | RoutingStrategies.Bubble);\n \npublic static readonly RoutedEvent\u003CTextInputEventArgs> TextInputEvent =\n    RoutedEvent.Register\u003CInputElement, TextInputEventArgs>(\n        nameof(TextInput),\n        RoutingStrategies.Tunnel | RoutingStrategies.Bubble);\n",[21,121,122,127,133,139,145,151,157,163,168,174,179,184,190,196,202],{"__ignoreMap":19},[24,123,124],{"class":26,"line":27},[24,125,126],{},"\u002F\u002F 键盘事件定义示例\n",[24,128,130],{"class":26,"line":129},2,[24,131,132],{},"public static readonly RoutedEvent\u003CKeyEventArgs> KeyDownEvent =\n",[24,134,136],{"class":26,"line":135},3,[24,137,138],{},"    RoutedEvent.Register\u003CInputElement, KeyEventArgs>(\n",[24,140,142],{"class":26,"line":141},4,[24,143,144],{},"        nameof(KeyDown),\n",[24,146,148],{"class":26,"line":147},5,[24,149,150],{},"        RoutingStrategies.Tunnel | RoutingStrategies.Bubble);\n",[24,152,154],{"class":26,"line":153},6,[24,155,156],{}," \n",[24,158,160],{"class":26,"line":159},7,[24,161,162],{},"public static readonly RoutedEvent\u003CKeyEventArgs> KeyUpEvent =\n",[24,164,166],{"class":26,"line":165},8,[24,167,138],{},[24,169,171],{"class":26,"line":170},9,[24,172,173],{},"        nameof(KeyUp),\n",[24,175,177],{"class":26,"line":176},10,[24,178,150],{},[24,180,182],{"class":26,"line":181},11,[24,183,156],{},[24,185,187],{"class":26,"line":186},12,[24,188,189],{},"public static readonly RoutedEvent\u003CTextInputEventArgs> TextInputEvent =\n",[24,191,193],{"class":26,"line":192},13,[24,194,195],{},"    RoutedEvent.Register\u003CInputElement, TextInputEventArgs>(\n",[24,197,199],{"class":26,"line":198},14,[24,200,201],{},"        nameof(TextInput),\n",[24,203,205],{"class":26,"line":204},15,[24,206,150],{},[10,208,209],{},"通过源码我们可以看到输入事件是可以支持隧道和冒泡这两个阶段的。",[10,211,212,213,216,217,220,221,224,225,228],{},"隧道： 事件从 UI 树的",[44,214,215],{},"根元素","（例如 ",[21,218,219],{},"Window"," 或最顶层的容器）开始，",[44,222,223],{},"向下","传播到触发事件的",[44,226,227],{},"目标元素","。",[10,230,231,232,234,235,238],{},"冒泡：事件从",[44,233,227],{},"（实际获得焦点的控件）开始，",[44,236,237],{},"向上","传播到 UI 树的根元素。",[106,240],{"src":241,"alt":242,"style":110},".\u002Fassets\u002Fimage-20251022230234926.png","image-20251022230234926 ",[244,245,247],"h4",{"id":246},"键盘事件处理示例","键盘事件处理示例：",[14,249,251],{"className":16,"code":250,"language":18,"meta":19,"style":19},"public class CustomInputControl : InputElement\n{\n    public CustomInputControl()\n    {\n        \u002F\u002F 注册事件处理器\n        AddHandler(KeyDownEvent, OnKeyDown);\n        AddHandler(KeyUpEvent, OnKeyUp);\n        AddHandler(TextInputEvent, OnTextInput);\n    }\n \n    private void OnKeyDown(object? sender, KeyEventArgs e)\n    {\n        \u002F\u002F 处理按键按下事件\n        if (e.Key == Key.Enter)\n        {\n            e.Handled = true; \u002F\u002F 标记为已处理\n            OnEnterPressed();\n        }\n    }\n \n    private void OnKeyUp(object? sender, KeyEventArgs e)\n    {\n        \u002F\u002F 处理按键释放事件\n        Debug.WriteLine($\"Key released: {e.Key}\");\n    }\n \n    private void OnTextInput(object? sender, TextInputEventArgs e)\n    {\n        \u002F\u002F 处理文本输入事件\n        Debug.WriteLine($\"Text input: {e.Text}\");\n    }\n \n    protected override void OnKeyDown(KeyEventArgs e)\n    {\n        \u002F\u002F 重写基类方法处理按键\n        base.OnKeyDown(e);\n    }\n}\n",[21,252,253,258,263,268,273,278,283,288,293,298,302,307,311,316,321,326,332,338,344,349,354,360,365,371,377,382,387,393,398,404,410,415,420,426,431,437,443,448],{"__ignoreMap":19},[24,254,255],{"class":26,"line":27},[24,256,257],{},"public class CustomInputControl : InputElement\n",[24,259,260],{"class":26,"line":129},[24,261,262],{},"{\n",[24,264,265],{"class":26,"line":135},[24,266,267],{},"    public CustomInputControl()\n",[24,269,270],{"class":26,"line":141},[24,271,272],{},"    {\n",[24,274,275],{"class":26,"line":147},[24,276,277],{},"        \u002F\u002F 注册事件处理器\n",[24,279,280],{"class":26,"line":153},[24,281,282],{},"        AddHandler(KeyDownEvent, OnKeyDown);\n",[24,284,285],{"class":26,"line":159},[24,286,287],{},"        AddHandler(KeyUpEvent, OnKeyUp);\n",[24,289,290],{"class":26,"line":165},[24,291,292],{},"        AddHandler(TextInputEvent, OnTextInput);\n",[24,294,295],{"class":26,"line":170},[24,296,297],{},"    }\n",[24,299,300],{"class":26,"line":176},[24,301,156],{},[24,303,304],{"class":26,"line":181},[24,305,306],{},"    private void OnKeyDown(object? sender, KeyEventArgs e)\n",[24,308,309],{"class":26,"line":186},[24,310,272],{},[24,312,313],{"class":26,"line":192},[24,314,315],{},"        \u002F\u002F 处理按键按下事件\n",[24,317,318],{"class":26,"line":198},[24,319,320],{},"        if (e.Key == Key.Enter)\n",[24,322,323],{"class":26,"line":204},[24,324,325],{},"        {\n",[24,327,329],{"class":26,"line":328},16,[24,330,331],{},"            e.Handled = true; \u002F\u002F 标记为已处理\n",[24,333,335],{"class":26,"line":334},17,[24,336,337],{},"            OnEnterPressed();\n",[24,339,341],{"class":26,"line":340},18,[24,342,343],{},"        }\n",[24,345,347],{"class":26,"line":346},19,[24,348,297],{},[24,350,352],{"class":26,"line":351},20,[24,353,156],{},[24,355,357],{"class":26,"line":356},21,[24,358,359],{},"    private void OnKeyUp(object? sender, KeyEventArgs e)\n",[24,361,363],{"class":26,"line":362},22,[24,364,272],{},[24,366,368],{"class":26,"line":367},23,[24,369,370],{},"        \u002F\u002F 处理按键释放事件\n",[24,372,374],{"class":26,"line":373},24,[24,375,376],{},"        Debug.WriteLine($\"Key released: {e.Key}\");\n",[24,378,380],{"class":26,"line":379},25,[24,381,297],{},[24,383,385],{"class":26,"line":384},26,[24,386,156],{},[24,388,390],{"class":26,"line":389},27,[24,391,392],{},"    private void OnTextInput(object? sender, TextInputEventArgs e)\n",[24,394,396],{"class":26,"line":395},28,[24,397,272],{},[24,399,401],{"class":26,"line":400},29,[24,402,403],{},"        \u002F\u002F 处理文本输入事件\n",[24,405,407],{"class":26,"line":406},30,[24,408,409],{},"        Debug.WriteLine($\"Text input: {e.Text}\");\n",[24,411,413],{"class":26,"line":412},31,[24,414,297],{},[24,416,418],{"class":26,"line":417},32,[24,419,156],{},[24,421,423],{"class":26,"line":422},33,[24,424,425],{},"    protected override void OnKeyDown(KeyEventArgs e)\n",[24,427,429],{"class":26,"line":428},34,[24,430,272],{},[24,432,434],{"class":26,"line":433},35,[24,435,436],{},"        \u002F\u002F 重写基类方法处理按键\n",[24,438,440],{"class":26,"line":439},36,[24,441,442],{},"        base.OnKeyDown(e);\n",[24,444,446],{"class":26,"line":445},37,[24,447,297],{},[24,449,451],{"class":26,"line":450},38,[24,452,453],{},"}\n",[10,455,456],{},"在Avalonia中，InputElment更加偏向于是逻辑组件，但它也继承了许多可视化组件的特性，他在UI元素层次中，专注于 交互 和 输入 处理的基类。它有焦点管理，键盘，指针事件等处理的功能，并且他也具有完整的绘制能力。",[10,458,459,460,462,463,466],{},"根据 Avalonia 的继承关系，你可以看到 ",[21,461,49],{}," 位于功能链的末端，但它",[44,464,465],{},"继承","了所有可视化和布局的特性：\n$$\n\\text{Object} \\to \\text{AvaloniaObject} \\to \\text{Animatable} \\to \\text{StyledElement} \\to \\textbf{Visual} \\to \\text{Layoutable} \\to \\text{Interactive} \\to \\textbf{InputElement}\n$$",[468,469,470,482,505,521],"ul",{},[41,471,472,477,478,481],{},[44,473,474,476],{},[21,475,83],{}," (可视化)："," 提供了",[44,479,480],{},"渲染、变换、命中测试","等所有与屏幕显示相关的基础能力。",[41,483,484,490,491,494,495,494,498,494,501,504],{},[44,485,486,489],{},[21,487,488],{},"Layoutable"," (布局)："," 提供了 ",[21,492,493],{},"Width","、",[21,496,497],{},"Height",[21,499,500],{},"Margin",[21,502,503],{},"HorizontalAlignment"," 等所有与布局计算相关的基础能力。",[41,506,507,513,514,517,518,228],{},[44,508,509,512],{},[21,510,511],{},"Interactive"," (交互)："," 引入了",[44,515,516],{},"路由事件系统","和一些更",[44,519,520],{},"高级的交互概念",[41,522,523,528,529,532,533,536],{},[44,524,525,527],{},[21,526,49],{}," (输入元素)："," 在继承了",[44,530,531],{},"所有可视化和布局能力的基础","上，",[44,534,535],{},"专门添加了","处理输入（键盘、鼠标、焦点）的逻辑。",[10,538,539],{},"需要注意一点的是，Avalonia的容器组件中需要的是一个Contrrol类，因此我们如果直接去继承InputElement的话就无法正常被包含在容器组件里，也就无法被渲染出来。",[10,541,542,544,545,548],{},[21,543,49],{}," 的价值在于",[44,546,547],{},"框架架构层面",",而不是应用开发层面。 它是 Avalonia 实现关注点分离的关键:将输入处理从视觉渲染和控件功能中分离出来。 这种设计使得框架更加模块化和可维护,即使应用开发者通常不直接继承它。",[10,550,551],{},"鼠标事件处理机制\n鼠标事件类型\nAvalonia提供了全面的鼠标\u002F指针事件支持：",[10,553,554],{},[106,555],{"alt":556,"src":557},"image-20251022233527174","image-20251022233527174.png",[244,559,560],{"id":560},"指针事件处理流程",[106,562],{"src":563,"alt":564,"style":110},".\u002Fassets\u002Fimage-20251022233542172.png","image-20251022233542172 ",[10,566,567],{},"处理事件的示例：",[14,569,571],{"className":16,"code":570,"language":18,"meta":19,"style":19},"public class InteractiveControl : InputElement\n{\n    private bool _isDragging = false;\n    private Point _dragStartPoint;\n \n    public InteractiveControl()\n    {\n        \u002F\u002F 注册指针事件处理器\n        PointerPressed += OnPointerPressed;\n        PointerReleased += OnPointerReleased;\n        PointerMoved += OnPointerMoved;\n        PointerEntered += OnPointerEntered;\n        PointerExited += OnPointerExited;\n    }\n \n    private void OnPointerPressed(object? sender, PointerPressedEventArgs e)\n    {\n        var pointer = e.GetCurrentPoint(this);\n        \n        if (pointer.Properties.IsLeftButtonPressed)\n        {\n            _isDragging = true;\n            _dragStartPoint = pointer.Position;\n            e.Pointer.Capture(this); \u002F\u002F 捕获指针\n            e.Handled = true;\n        }\n    }\n \n    private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)\n    {\n        if (_isDragging)\n        {\n            _isDragging = false;\n            e.Pointer.Capture(null); \u002F\u002F 释放指针捕获\n            e.Handled = true;\n        }\n    }\n \n    private void OnPointerMoved(object? sender, PointerEventArgs e)\n    {\n        if (_isDragging)\n        {\n            var currentPoint = e.GetCurrentPoint(this);\n            var delta = currentPoint.Position - _dragStartPoint;\n            \n            \u002F\u002F 处理拖拽逻辑\n            HandleDrag(delta);\n            e.Handled = true;\n        }\n    }\n \n    private void OnPointerEntered(object? sender, PointerEventArgs e)\n    {\n        \u002F\u002F 指针进入时的视觉效果\n        UpdateVisualState(isPointerOver: true);\n    }\n \n    private void OnPointerExited(object? sender, PointerEventArgs e)\n    {\n        \u002F\u002F 指针离开时的视觉效果\n        UpdateVisualState(isPointerOver: false);\n    }\n \n    protected override void OnPointerMoved(PointerEventArgs e)\n    {\n        \u002F\u002F 重写基类方法处理指针移动\n        base.OnPointerMoved(e);\n    }\n}\n",[21,572,573,578,582,587,592,596,601,605,610,615,620,625,630,635,639,643,648,652,657,662,667,671,676,681,686,691,695,699,703,708,712,717,721,726,731,735,739,743,747,753,758,763,768,774,780,786,792,798,803,808,813,818,824,829,835,841,846,851,857,862,868,874,879,884,890,895,901,907,912],{"__ignoreMap":19},[24,574,575],{"class":26,"line":27},[24,576,577],{},"public class InteractiveControl : InputElement\n",[24,579,580],{"class":26,"line":129},[24,581,262],{},[24,583,584],{"class":26,"line":135},[24,585,586],{},"    private bool _isDragging = false;\n",[24,588,589],{"class":26,"line":141},[24,590,591],{},"    private Point _dragStartPoint;\n",[24,593,594],{"class":26,"line":147},[24,595,156],{},[24,597,598],{"class":26,"line":153},[24,599,600],{},"    public InteractiveControl()\n",[24,602,603],{"class":26,"line":159},[24,604,272],{},[24,606,607],{"class":26,"line":165},[24,608,609],{},"        \u002F\u002F 注册指针事件处理器\n",[24,611,612],{"class":26,"line":170},[24,613,614],{},"        PointerPressed += OnPointerPressed;\n",[24,616,617],{"class":26,"line":176},[24,618,619],{},"        PointerReleased += OnPointerReleased;\n",[24,621,622],{"class":26,"line":181},[24,623,624],{},"        PointerMoved += OnPointerMoved;\n",[24,626,627],{"class":26,"line":186},[24,628,629],{},"        PointerEntered += OnPointerEntered;\n",[24,631,632],{"class":26,"line":192},[24,633,634],{},"        PointerExited += OnPointerExited;\n",[24,636,637],{"class":26,"line":198},[24,638,297],{},[24,640,641],{"class":26,"line":204},[24,642,156],{},[24,644,645],{"class":26,"line":328},[24,646,647],{},"    private void OnPointerPressed(object? sender, PointerPressedEventArgs e)\n",[24,649,650],{"class":26,"line":334},[24,651,272],{},[24,653,654],{"class":26,"line":340},[24,655,656],{},"        var pointer = e.GetCurrentPoint(this);\n",[24,658,659],{"class":26,"line":346},[24,660,661],{},"        \n",[24,663,664],{"class":26,"line":351},[24,665,666],{},"        if (pointer.Properties.IsLeftButtonPressed)\n",[24,668,669],{"class":26,"line":356},[24,670,325],{},[24,672,673],{"class":26,"line":362},[24,674,675],{},"            _isDragging = true;\n",[24,677,678],{"class":26,"line":367},[24,679,680],{},"            _dragStartPoint = pointer.Position;\n",[24,682,683],{"class":26,"line":373},[24,684,685],{},"            e.Pointer.Capture(this); \u002F\u002F 捕获指针\n",[24,687,688],{"class":26,"line":379},[24,689,690],{},"            e.Handled = true;\n",[24,692,693],{"class":26,"line":384},[24,694,343],{},[24,696,697],{"class":26,"line":389},[24,698,297],{},[24,700,701],{"class":26,"line":395},[24,702,156],{},[24,704,705],{"class":26,"line":400},[24,706,707],{},"    private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)\n",[24,709,710],{"class":26,"line":406},[24,711,272],{},[24,713,714],{"class":26,"line":412},[24,715,716],{},"        if (_isDragging)\n",[24,718,719],{"class":26,"line":417},[24,720,325],{},[24,722,723],{"class":26,"line":422},[24,724,725],{},"            _isDragging = false;\n",[24,727,728],{"class":26,"line":428},[24,729,730],{},"            e.Pointer.Capture(null); \u002F\u002F 释放指针捕获\n",[24,732,733],{"class":26,"line":433},[24,734,690],{},[24,736,737],{"class":26,"line":439},[24,738,343],{},[24,740,741],{"class":26,"line":445},[24,742,297],{},[24,744,745],{"class":26,"line":450},[24,746,156],{},[24,748,750],{"class":26,"line":749},39,[24,751,752],{},"    private void OnPointerMoved(object? sender, PointerEventArgs e)\n",[24,754,756],{"class":26,"line":755},40,[24,757,272],{},[24,759,761],{"class":26,"line":760},41,[24,762,716],{},[24,764,766],{"class":26,"line":765},42,[24,767,325],{},[24,769,771],{"class":26,"line":770},43,[24,772,773],{},"            var currentPoint = e.GetCurrentPoint(this);\n",[24,775,777],{"class":26,"line":776},44,[24,778,779],{},"            var delta = currentPoint.Position - _dragStartPoint;\n",[24,781,783],{"class":26,"line":782},45,[24,784,785],{},"            \n",[24,787,789],{"class":26,"line":788},46,[24,790,791],{},"            \u002F\u002F 处理拖拽逻辑\n",[24,793,795],{"class":26,"line":794},47,[24,796,797],{},"            HandleDrag(delta);\n",[24,799,801],{"class":26,"line":800},48,[24,802,690],{},[24,804,806],{"class":26,"line":805},49,[24,807,343],{},[24,809,811],{"class":26,"line":810},50,[24,812,297],{},[24,814,816],{"class":26,"line":815},51,[24,817,156],{},[24,819,821],{"class":26,"line":820},52,[24,822,823],{},"    private void OnPointerEntered(object? sender, PointerEventArgs e)\n",[24,825,827],{"class":26,"line":826},53,[24,828,272],{},[24,830,832],{"class":26,"line":831},54,[24,833,834],{},"        \u002F\u002F 指针进入时的视觉效果\n",[24,836,838],{"class":26,"line":837},55,[24,839,840],{},"        UpdateVisualState(isPointerOver: true);\n",[24,842,844],{"class":26,"line":843},56,[24,845,297],{},[24,847,849],{"class":26,"line":848},57,[24,850,156],{},[24,852,854],{"class":26,"line":853},58,[24,855,856],{},"    private void OnPointerExited(object? sender, PointerEventArgs e)\n",[24,858,860],{"class":26,"line":859},59,[24,861,272],{},[24,863,865],{"class":26,"line":864},60,[24,866,867],{},"        \u002F\u002F 指针离开时的视觉效果\n",[24,869,871],{"class":26,"line":870},61,[24,872,873],{},"        UpdateVisualState(isPointerOver: false);\n",[24,875,877],{"class":26,"line":876},62,[24,878,297],{},[24,880,882],{"class":26,"line":881},63,[24,883,156],{},[24,885,887],{"class":26,"line":886},64,[24,888,889],{},"    protected override void OnPointerMoved(PointerEventArgs e)\n",[24,891,893],{"class":26,"line":892},65,[24,894,272],{},[24,896,898],{"class":26,"line":897},66,[24,899,900],{},"        \u002F\u002F 重写基类方法处理指针移动\n",[24,902,904],{"class":26,"line":903},67,[24,905,906],{},"        base.OnPointerMoved(e);\n",[24,908,910],{"class":26,"line":909},68,[24,911,297],{},[24,913,915],{"class":26,"line":914},69,[24,916,453],{},[98,918,920],{"id":919},"idatatemplatehost接口","IDataTemplateHost接口",[14,922,924],{"className":16,"code":923,"language":18,"meta":19,"style":19},"using Avalonia.Metadata;\n\nnamespace Avalonia.Controls.Templates\n{\n    \u002F\u002F\u002F \u003Csummary>\n    \u002F\u002F\u002F Defines an element that has a \u003Csee cref=\"DataTemplates\"\u002F> collection.\n    \u002F\u002F\u002F \u003C\u002Fsummary>\n    [NotClientImplementable]\n    public interface IDataTemplateHost\n    {\n        \u002F\u002F\u002F \u003Csummary>\n        \u002F\u002F\u002F Gets the data templates for the element.\n        \u002F\u002F\u002F \u003C\u002Fsummary>\n        DataTemplates DataTemplates { get; }\n\n        \u002F\u002F\u002F \u003Csummary>\n        \u002F\u002F\u002F Gets a value indicating whether \u003Csee cref=\"DataTemplates\"\u002F> is initialized.\n        \u002F\u002F\u002F \u003C\u002Fsummary>\n        \u002F\u002F\u002F \u003Cremarks>\n        \u002F\u002F\u002F The \u003Csee cref=\"DataTemplates\"\u002F> property may be lazily initialized, if so this property\n        \u002F\u002F\u002F indicates whether it has been initialized.\n        \u002F\u002F\u002F \u003C\u002Fremarks>\n        bool IsDataTemplatesInitialized { get; }\n    }\n}\n\n",[21,925,926,931,937,942,946,951,956,961,966,971,975,980,985,990,995,999,1003,1008,1012,1017,1022,1027,1032,1037,1041],{"__ignoreMap":19},[24,927,928],{"class":26,"line":27},[24,929,930],{},"using Avalonia.Metadata;\n",[24,932,933],{"class":26,"line":129},[24,934,936],{"emptyLinePlaceholder":935},true,"\n",[24,938,939],{"class":26,"line":135},[24,940,941],{},"namespace Avalonia.Controls.Templates\n",[24,943,944],{"class":26,"line":141},[24,945,262],{},[24,947,948],{"class":26,"line":147},[24,949,950],{},"    \u002F\u002F\u002F \u003Csummary>\n",[24,952,953],{"class":26,"line":153},[24,954,955],{},"    \u002F\u002F\u002F Defines an element that has a \u003Csee cref=\"DataTemplates\"\u002F> collection.\n",[24,957,958],{"class":26,"line":159},[24,959,960],{},"    \u002F\u002F\u002F \u003C\u002Fsummary>\n",[24,962,963],{"class":26,"line":165},[24,964,965],{},"    [NotClientImplementable]\n",[24,967,968],{"class":26,"line":170},[24,969,970],{},"    public interface IDataTemplateHost\n",[24,972,973],{"class":26,"line":176},[24,974,272],{},[24,976,977],{"class":26,"line":181},[24,978,979],{},"        \u002F\u002F\u002F \u003Csummary>\n",[24,981,982],{"class":26,"line":186},[24,983,984],{},"        \u002F\u002F\u002F Gets the data templates for the element.\n",[24,986,987],{"class":26,"line":192},[24,988,989],{},"        \u002F\u002F\u002F \u003C\u002Fsummary>\n",[24,991,992],{"class":26,"line":198},[24,993,994],{},"        DataTemplates DataTemplates { get; }\n",[24,996,997],{"class":26,"line":204},[24,998,936],{"emptyLinePlaceholder":935},[24,1000,1001],{"class":26,"line":328},[24,1002,979],{},[24,1004,1005],{"class":26,"line":334},[24,1006,1007],{},"        \u002F\u002F\u002F Gets a value indicating whether \u003Csee cref=\"DataTemplates\"\u002F> is initialized.\n",[24,1009,1010],{"class":26,"line":340},[24,1011,989],{},[24,1013,1014],{"class":26,"line":346},[24,1015,1016],{},"        \u002F\u002F\u002F \u003Cremarks>\n",[24,1018,1019],{"class":26,"line":351},[24,1020,1021],{},"        \u002F\u002F\u002F The \u003Csee cref=\"DataTemplates\"\u002F> property may be lazily initialized, if so this property\n",[24,1023,1024],{"class":26,"line":356},[24,1025,1026],{},"        \u002F\u002F\u002F indicates whether it has been initialized.\n",[24,1028,1029],{"class":26,"line":362},[24,1030,1031],{},"        \u002F\u002F\u002F \u003C\u002Fremarks>\n",[24,1033,1034],{"class":26,"line":367},[24,1035,1036],{},"        bool IsDataTemplatesInitialized { get; }\n",[24,1038,1039],{"class":26,"line":373},[24,1040,297],{},[24,1042,1043],{"class":26,"line":379},[24,1044,453],{},[10,1046,1047,1056],{},[44,1048,1049,1051,1052,1055],{},[21,1050,61],{}," 接口定义了一个可以拥有 ",[21,1053,1054],{},"DataTemplates"," 集合的元素。"," 它提供了两个成员:",[38,1058,1059,1070],{},[41,1060,1061,1066,1067],{},[44,1062,1063,1065],{},[21,1064,1054],{}," 属性"," - 获取元素的",[44,1068,1069],{},"数据模板集合",[41,1071,1072,1077,1078,1080,1081,1084],{},[44,1073,1074,1065],{},[21,1075,1076],{},"IsDataTemplatesInitialized"," - 指示 ",[21,1079,1054],{}," 集合",[44,1082,1083],{},"是否已被初始化","(因为它可能是延迟初始化的)",[244,1086,1087],{"id":1087},"使用场景",[14,1089,1094],{"className":1090,"code":1092,"language":1093},[1091],"language-text","IDataTemplateHost` 的主要用途是支持**数据模板查找机制**。 当需要为某个数据对象找到合适的视觉表示时,系统会沿着逻辑树向上查找,检查每个 `IDataTemplateHost` 的 `DataTemplates\n","text",[21,1095,1092],{"__ignoreMap":19},[10,1097,1098],{},"查找顺序如下:",[38,1100,1101,1106,1111],{},[41,1102,1103,1104],{},"首先检查控件自身的 ",[21,1105,1054],{},[41,1107,1108,1109],{},"然后沿着逻辑树向上查找父元素的 ",[21,1110,1054],{},[41,1112,1113,1114],{},"最后查找全局的 ",[21,1115,1116],{},"Application.DataTemplates",[244,1118,1119],{"id":1119},"延迟初始化",[10,1121,1122,1124,1125,1128,1129,1131,1132,1134,1135,1138],{},[21,1123,1076],{}," 属性的存在是为了",[44,1126,1127],{},"性能优化","。 例如在 ",[21,1130,35],{}," 类中,只有在实际访问 ",[21,1133,1054],{}," 属性时才会创建集合: Control.cs:213 同样,",[21,1136,1137],{},"Application"," 类也采用了相同的延迟初始化策略: Application.cs:127 Application.cs:168",[10,1140,1141,1144,1145,1147,1148,1150],{},[21,1142,1143],{},"[NotClientImplementable]"," 特性表明这个接口不应该由用户代码实现,它是框架内部使用的接口。 用户通常通过继承 ",[21,1146,35],{}," 或使用 ",[21,1149,1137],{}," 来间接使用这个接口的功能,而不是直接实现它。",[10,1152,1153],{},"因此，我们可以说，它的作用就是提供一个数据模板的查找连，控件需要为某个数据对象找到合适的视觉表示的时候，系统就会沿着逻辑树向上查找DataTemplates集合。",[98,1155,1157],{"id":1156},"datatemplate类","DataTemplate类",[14,1159,1161],{"className":16,"code":1160,"language":18,"meta":19,"style":19},"using System;\nusing Avalonia.Collections;\n\nnamespace Avalonia.Controls.Templates\n{\n    \u002F\u002F\u002F \u003Csummary>\n    \u002F\u002F\u002F A collection of \u003Csee cref=\"IDataTemplate\"\u002F>s.\n    \u002F\u002F\u002F \u003C\u002Fsummary>\n    public class DataTemplates : AvaloniaList\u003CIDataTemplate>, IAvaloniaListItemValidator\u003CIDataTemplate>\n    {\n        \u002F\u002F\u002F \u003Csummary>\n        \u002F\u002F\u002F Initializes a new instance of the \u003Csee cref=\"DataTemplates\"\u002F> class.\n        \u002F\u002F\u002F \u003C\u002Fsummary>\n        public DataTemplates()\n        {\n            ResetBehavior = ResetBehavior.Remove;\n            Validator = this;\n        }\n\n        void IAvaloniaListItemValidator\u003CIDataTemplate>.Validate(IDataTemplate item)\n        {\n            var valid = item switch\n            {\n                ITypedDataTemplate typed => typed.DataType is not null,\n                _ => true\n            };\n            \n            if (!valid)\n            {\n                throw new InvalidOperationException(\"DataTemplate inside of DataTemplates must have a DataType set. Set DataType property or use ItemTemplate with single template instead.\");\n            }\n        }\n    }\n}\n\n",[21,1162,1163,1168,1173,1177,1181,1185,1189,1194,1198,1203,1207,1211,1216,1220,1225,1229,1234,1239,1243,1247,1252,1256,1261,1266,1271,1276,1281,1285,1290,1294,1299,1304,1308,1312],{"__ignoreMap":19},[24,1164,1165],{"class":26,"line":27},[24,1166,1167],{},"using System;\n",[24,1169,1170],{"class":26,"line":129},[24,1171,1172],{},"using Avalonia.Collections;\n",[24,1174,1175],{"class":26,"line":135},[24,1176,936],{"emptyLinePlaceholder":935},[24,1178,1179],{"class":26,"line":141},[24,1180,941],{},[24,1182,1183],{"class":26,"line":147},[24,1184,262],{},[24,1186,1187],{"class":26,"line":153},[24,1188,950],{},[24,1190,1191],{"class":26,"line":159},[24,1192,1193],{},"    \u002F\u002F\u002F A collection of \u003Csee cref=\"IDataTemplate\"\u002F>s.\n",[24,1195,1196],{"class":26,"line":165},[24,1197,960],{},[24,1199,1200],{"class":26,"line":170},[24,1201,1202],{},"    public class DataTemplates : AvaloniaList\u003CIDataTemplate>, IAvaloniaListItemValidator\u003CIDataTemplate>\n",[24,1204,1205],{"class":26,"line":176},[24,1206,272],{},[24,1208,1209],{"class":26,"line":181},[24,1210,979],{},[24,1212,1213],{"class":26,"line":186},[24,1214,1215],{},"        \u002F\u002F\u002F Initializes a new instance of the \u003Csee cref=\"DataTemplates\"\u002F> class.\n",[24,1217,1218],{"class":26,"line":192},[24,1219,989],{},[24,1221,1222],{"class":26,"line":198},[24,1223,1224],{},"        public DataTemplates()\n",[24,1226,1227],{"class":26,"line":204},[24,1228,325],{},[24,1230,1231],{"class":26,"line":328},[24,1232,1233],{},"            ResetBehavior = ResetBehavior.Remove;\n",[24,1235,1236],{"class":26,"line":334},[24,1237,1238],{},"            Validator = this;\n",[24,1240,1241],{"class":26,"line":340},[24,1242,343],{},[24,1244,1245],{"class":26,"line":346},[24,1246,936],{"emptyLinePlaceholder":935},[24,1248,1249],{"class":26,"line":351},[24,1250,1251],{},"        void IAvaloniaListItemValidator\u003CIDataTemplate>.Validate(IDataTemplate item)\n",[24,1253,1254],{"class":26,"line":356},[24,1255,325],{},[24,1257,1258],{"class":26,"line":362},[24,1259,1260],{},"            var valid = item switch\n",[24,1262,1263],{"class":26,"line":367},[24,1264,1265],{},"            {\n",[24,1267,1268],{"class":26,"line":373},[24,1269,1270],{},"                ITypedDataTemplate typed => typed.DataType is not null,\n",[24,1272,1273],{"class":26,"line":379},[24,1274,1275],{},"                _ => true\n",[24,1277,1278],{"class":26,"line":384},[24,1279,1280],{},"            };\n",[24,1282,1283],{"class":26,"line":389},[24,1284,785],{},[24,1286,1287],{"class":26,"line":395},[24,1288,1289],{},"            if (!valid)\n",[24,1291,1292],{"class":26,"line":400},[24,1293,1265],{},[24,1295,1296],{"class":26,"line":406},[24,1297,1298],{},"                throw new InvalidOperationException(\"DataTemplate inside of DataTemplates must have a DataType set. Set DataType property or use ItemTemplate with single template instead.\");\n",[24,1300,1301],{"class":26,"line":412},[24,1302,1303],{},"            }\n",[24,1305,1306],{"class":26,"line":417},[24,1307,343],{},[24,1309,1310],{"class":26,"line":422},[24,1311,297],{},[24,1313,1314],{"class":26,"line":428},[24,1315,453],{},[10,1317,1318,1319,1322,1323,1326,1327,1330,1331,1334,1335,1338],{},"这是一恶搞专门用于存储IDataTemplate对象的集合类，它继承自",[21,1320,1321],{},"AvaloniaList\u003CIDataTemplate>"," 并实现了 ",[21,1324,1325],{},"IAvaloniaListItemValidator\u003CIDataTemplate>"," 接口,用于在",[44,1328,1329],{},"添加模板时进行验证","。数据模板（Data Templates）在Avalonia中为您提供了定义数据的可视化表示的强大方法。",[44,1332,1333],{},"它们允许您指定数据的展示方式和格式","，从而创建",[44,1336,1337],{},"动态和可定制的用户界面","。本文档将介绍Avalonia中的数据模板概念，并演示如何在应用程序中有效使用它们。",[244,1340,1341],{"id":1341},"核心功能",[38,1343,1344,1356,1366],{},[41,1345,1346,1349,1350,1352,1353,1355],{},[44,1347,1348],{},"集合类型"," - ",[21,1351,1054],{}," 是 ",[21,1354,1321],{}," 的子类,提供了一个可观察的数据模板集合",[41,1357,1358,1361,1362,1365],{},[44,1359,1360],{},"重置行为"," - 构造函数中设置 ",[21,1363,1364],{},"ResetBehavior = ResetBehavior.Remove",",这意味着当集合被清空时,会触发移除事件而不是重置事件",[41,1367,1368,1371,1372,1374],{},[44,1369,1370],{},"验证机制"," - 实现了 ",[21,1373,1325],{}," 接口,在添加模板时自动验证",[10,1376,1377],{},"相关的介绍可以看文档：",[10,1379,1380],{},[1381,1382,1383],"a",{"href":1383,"rel":1384},"https:\u002F\u002Fdocs.avaloniaui.net\u002Fzh-Hans\u002Fdocs\u002Fbasics\u002Fdata\u002Fdata-templates",[1385],"nofollow",[244,1387,1389],{"id":1388},"将数据模板应用于listbox","将数据模板应用于ListBox",[10,1391,1392,1393,1396,1397,1400],{},"要将数据模板应用于",[21,1394,1395],{},"ListBox","，通常使用控件的",[21,1398,1399],{},"ItemTemplate","属性。",[10,1402,1403,1404,1406,1407,1410,1411,1413],{},"例如，如果您有一个",[21,1405,1395],{},"，应使用定义的数据模板来显示",[21,1408,1409],{},"Item","对象的集合，可以像这样设置",[21,1412,1399],{},"属性：",[14,1415,1419],{"className":1416,"code":1417,"language":1418,"meta":19,"style":19},"language-xml shiki shiki-themes github-light github-dark","\u003CListBox ItemsSource=\"{Binding Items}\">\n  \u003CListBox.ItemTemplate>\n    \u003CDataTemplate>\n        \u003CStackPanel Orientation=\"Horizontal\">\n            \u003CTextBlock Text=\"{Binding Name}\" \u002F>\n            \u003CImage Source=\"{Binding ImageSource}\" \u002F>\n        \u003C\u002FStackPanel>\n    \u003C\u002FDataTemplate>\n  \u003C\u002FListBox.ItemTemplate>\n\u003C\u002FListBox>\n","xml",[21,1420,1421,1426,1431,1436,1441,1446,1451,1456,1461,1466],{"__ignoreMap":19},[24,1422,1423],{"class":26,"line":27},[24,1424,1425],{},"\u003CListBox ItemsSource=\"{Binding Items}\">\n",[24,1427,1428],{"class":26,"line":129},[24,1429,1430],{},"  \u003CListBox.ItemTemplate>\n",[24,1432,1433],{"class":26,"line":135},[24,1434,1435],{},"    \u003CDataTemplate>\n",[24,1437,1438],{"class":26,"line":141},[24,1439,1440],{},"        \u003CStackPanel Orientation=\"Horizontal\">\n",[24,1442,1443],{"class":26,"line":147},[24,1444,1445],{},"            \u003CTextBlock Text=\"{Binding Name}\" \u002F>\n",[24,1447,1448],{"class":26,"line":153},[24,1449,1450],{},"            \u003CImage Source=\"{Binding ImageSource}\" \u002F>\n",[24,1452,1453],{"class":26,"line":159},[24,1454,1455],{},"        \u003C\u002FStackPanel>\n",[24,1457,1458],{"class":26,"line":165},[24,1459,1460],{},"    \u003C\u002FDataTemplate>\n",[24,1462,1463],{"class":26,"line":170},[24,1464,1465],{},"  \u003C\u002FListBox.ItemTemplate>\n",[24,1467,1468],{"class":26,"line":176},[24,1469,1470],{},"\u003C\u002FListBox>\n",[98,1472,1474],{"id":1473},"ivisualbrushinitialize接口","IVisualBrushInitialize接口",[10,1476,1477,1478,1480,1481,1483,1484,228],{},"它主要用于解决一个特定的渲染问题：当一个控件（",[21,1479,35],{},"）被用作另一个控件的背景或前景（即作为 ",[21,1482,76],{}," 的来源）时，",[44,1485,1486],{},"它必须被正确地初始化和准备好，才能进行绘图",[10,1488,1489,1490,1492],{},"在 Avalonia 中,",[21,1491,35],{}," 类实现了这个接口: Control.cs:27",[10,1494,1495,1497],{},[21,1496,35],{}," 的实现逻辑如下: Control.cs:228-252",[38,1499,1500,1510,1523],{},[41,1501,1502,1505,1506,1509],{},[44,1503,1504],{},"检查是否已附加到视觉树"," - 如果控件的 ",[21,1507,1508],{},"VisualRoot"," 为 null(即未附加到视觉树)",[41,1511,1512,1515,1516,1519,1520],{},[44,1513,1514],{},"初始化控件"," - 如果控件未初始化,遍历自身及所有视觉子元素,调用 ",[21,1517,1518],{},"BeginInit()"," 和 ",[21,1521,1522],{},"EndInit()",[41,1524,1525,1528,1529,1519,1532,1535,1536],{},[44,1526,1527],{},"执行布局"," - 如果布局无效,执行 ",[21,1530,1531],{},"Measure",[21,1533,1534],{},"Arrange"," 以确保控件",[44,1537,1538],{},"有正确的大小和位置",[10,1540,1541,1542,1545,1546,1549,1550,1552],{},"这是一个内部接口(标记为 ",[21,1543,1544],{},"internal","),并且带有 ",[21,1547,1548],{},"[Unstable]"," 特性,表明它是框架内部使用的 API,不应该由用户代码直接实现。 用户通常通过继承 ",[21,1551,35],{}," 来间接获得这个功能。",[98,1554,1556],{"id":1555},"isettervalue接口","ISetterValue接口",[14,1558,1560],{"className":16,"code":1559,"language":18,"meta":19,"style":19},"#nullable enable\n\nnamespace Avalonia.Styling\n{\n    \u002F\u002F\u002F \u003Csummary>\n    \u002F\u002F\u002F Customizes the behavior of a class when added as a value to a \u003Csee cref=\"SetterBase\"\u002F>.\n    \u002F\u002F\u002F \u003C\u002Fsummary>\n    public interface ISetterValue\n    {\n        \u002F\u002F\u002F \u003Csummary>\n        \u002F\u002F\u002F Notifies that the object has been added as a setter value.\n        \u002F\u002F\u002F \u003C\u002Fsummary>\n        void Initialize(SetterBase setter);\n    }\n}\n\n",[21,1561,1562,1567,1571,1576,1580,1584,1589,1593,1598,1602,1606,1611,1615,1620,1624],{"__ignoreMap":19},[24,1563,1564],{"class":26,"line":27},[24,1565,1566],{},"#nullable enable\n",[24,1568,1569],{"class":26,"line":129},[24,1570,936],{"emptyLinePlaceholder":935},[24,1572,1573],{"class":26,"line":135},[24,1574,1575],{},"namespace Avalonia.Styling\n",[24,1577,1578],{"class":26,"line":141},[24,1579,262],{},[24,1581,1582],{"class":26,"line":147},[24,1583,950],{},[24,1585,1586],{"class":26,"line":153},[24,1587,1588],{},"    \u002F\u002F\u002F Customizes the behavior of a class when added as a value to a \u003Csee cref=\"SetterBase\"\u002F>.\n",[24,1590,1591],{"class":26,"line":159},[24,1592,960],{},[24,1594,1595],{"class":26,"line":165},[24,1596,1597],{},"    public interface ISetterValue\n",[24,1599,1600],{"class":26,"line":170},[24,1601,272],{},[24,1603,1604],{"class":26,"line":176},[24,1605,979],{},[24,1607,1608],{"class":26,"line":181},[24,1609,1610],{},"        \u002F\u002F\u002F Notifies that the object has been added as a setter value.\n",[24,1612,1613],{"class":26,"line":186},[24,1614,989],{},[24,1616,1617],{"class":26,"line":192},[24,1618,1619],{},"        void Initialize(SetterBase setter);\n",[24,1621,1622],{"class":26,"line":198},[24,1623,297],{},[24,1625,1626],{"class":26,"line":204},[24,1627,453],{},[10,1629,1630,1639,1640,1643],{},[44,1631,1632,1634,1635,1638],{},[21,1633,91],{}," 接口用于自定义类在被添加为 ",[21,1636,1637],{},"SetterBase"," 的值时的行为。"," 它只有一个方法 ",[21,1641,1642],{},"Initialize(SetterBase setter)",",当对象被添加为 setter 值时会被调用。",[244,1645,1341],{"id":1646},"核心功能-1",[10,1648,1649,1650,1653,1654,1656,1657,1660,1661,1664],{},"该接口允许对象在被用作 setter 值时",[44,1651,1652],{},"执行自定义初始化逻辑","。 当一个实现了 ",[21,1655,91],{}," 的对象被赋值给 ",[21,1658,1659],{},"Setter.Value"," 时,系统会自动调用其 ",[21,1662,1663],{},"Initialize"," 方法。",[244,1666,1087],{"id":1667},"使用场景-1",[10,1669,1670,1671,1673,1674,1677,1678,1680],{},"在 ",[21,1672,95],{}," 类中,当设置 ",[21,1675,1676],{},"Value"," 属性时会检查值是否实现了 ",[21,1679,91],{}," 接口: Setter.cs:57",[10,1682,1683,1684,1686],{},"如果实现了该接口,就会调用 ",[21,1685,1663],{}," 方法并传入当前的 setter 实例。 Setter.cs:57",[244,1688,1689],{"id":1689},"实现示例",[10,1691,1692,1694,1695,1697],{},[21,1693,35],{}," 类实现了 ",[21,1696,91],{}," 接口: Control.cs:216-225",[10,1699,1670,1700,1702],{},[21,1701,35],{}," 的实现中:",[468,1704,1705,1716],{},[41,1706,1707,1708,1711,1712,1715],{},"它检查 setter 是否是为 ",[21,1709,1710],{},"ContextFlyoutProperty"," 设置值,如果是则允许直接使用控件而不需要包装在 ",[21,1713,1714],{},"\u003CTemplate>"," 中 Control.cs:218-220",[41,1717,1718,1719,1721],{},"对于其他属性,会抛出异常,要求将控件包装在 ",[21,1720,1714],{}," 中 Control.cs:223-224",[14,1723,1725],{"className":16,"code":1724,"language":18,"meta":19,"style":19}," void ISetterValue.Initialize(SetterBase setter)\n        {\n            if (setter is Setter s && s.Property == ContextFlyoutProperty)\n            {\n                return; \u002F\u002F Allow ContextFlyout to not need wrapping in \u003CTemplate>\n            }\n\n            throw new InvalidOperationException(\n                \"Cannot use a control as a Setter value. Wrap the control in a \u003CTemplate>.\");\n        }\n",[21,1726,1727,1732,1736,1741,1745,1750,1754,1758,1763,1768],{"__ignoreMap":19},[24,1728,1729],{"class":26,"line":27},[24,1730,1731],{}," void ISetterValue.Initialize(SetterBase setter)\n",[24,1733,1734],{"class":26,"line":129},[24,1735,325],{},[24,1737,1738],{"class":26,"line":135},[24,1739,1740],{},"            if (setter is Setter s && s.Property == ContextFlyoutProperty)\n",[24,1742,1743],{"class":26,"line":141},[24,1744,1265],{},[24,1746,1747],{"class":26,"line":147},[24,1748,1749],{},"                return; \u002F\u002F Allow ContextFlyout to not need wrapping in \u003CTemplate>\n",[24,1751,1752],{"class":26,"line":153},[24,1753,1303],{},[24,1755,1756],{"class":26,"line":159},[24,1757,936],{"emptyLinePlaceholder":935},[24,1759,1760],{"class":26,"line":165},[24,1761,1762],{},"            throw new InvalidOperationException(\n",[24,1764,1765],{"class":26,"line":170},[24,1766,1767],{},"                \"Cannot use a control as a Setter value. Wrap the control in a \u003CTemplate>.\");\n",[24,1769,1770],{"class":26,"line":176},[24,1771,343],{},[10,1773,1774],{},"这个设计防止了直接将控件实例用作 setter 值的常见错误。 在测试代码中可以看到这个验证: SetterTests.cs:21-26",[10,1776,1777,1779,1780,1782],{},[21,1778,91],{}," 是样式系统中的一个重要扩展点,它允许特定类型的对象在被用作样式 setter 值时执行自定义逻辑。 最常见的用例是防止错误使用(如 ",[21,1781,35],{}," 的实现)和支持特殊值类型(如绑定表达式)。 这个接口是框架内部使用的,普通应用开发者很少需要直接实现它。",[10,1784,1785],{},"到这里，我们已经清楚了组件基类Controls的继承关系和作用等，接下来我们就会进去高层组件看一下高层组件的实现等。",[1787,1788,1789],"h2",{"id":1789},"高层组件",[98,1791,1793],{"id":1792},"textblock","TextBlock",[14,1795,1797],{"className":16,"code":1796,"language":18,"meta":19,"style":19},"public class TextBlock : Control, IInlineHost\n",[21,1798,1799],{"__ignoreMap":19},[24,1800,1801],{"class":26,"line":27},[24,1802,1796],{},[10,1804,1805],{},"我们看到他在继承基础的Control类的同时，也继承了IInlineHost接口：",[14,1807,1809],{"className":16,"code":1808,"language":18,"meta":19,"style":19},"using Avalonia.Collections;\nusing Avalonia.LogicalTree;\n\nnamespace Avalonia.Controls.Documents\n{\n    internal interface IInlineHost : ILogical\n    {\n        void Invalidate();\n\n        IAvaloniaList\u003CVisual> VisualChildren { get; }\n    }\n}\n\n",[21,1810,1811,1815,1820,1824,1829,1833,1838,1842,1847,1851,1856,1860],{"__ignoreMap":19},[24,1812,1813],{"class":26,"line":27},[24,1814,1172],{},[24,1816,1817],{"class":26,"line":129},[24,1818,1819],{},"using Avalonia.LogicalTree;\n",[24,1821,1822],{"class":26,"line":135},[24,1823,936],{"emptyLinePlaceholder":935},[24,1825,1826],{"class":26,"line":141},[24,1827,1828],{},"namespace Avalonia.Controls.Documents\n",[24,1830,1831],{"class":26,"line":147},[24,1832,262],{},[24,1834,1835],{"class":26,"line":153},[24,1836,1837],{},"    internal interface IInlineHost : ILogical\n",[24,1839,1840],{"class":26,"line":159},[24,1841,272],{},[24,1843,1844],{"class":26,"line":165},[24,1845,1846],{},"        void Invalidate();\n",[24,1848,1849],{"class":26,"line":170},[24,1850,936],{"emptyLinePlaceholder":935},[24,1852,1853],{"class":26,"line":176},[24,1854,1855],{},"        IAvaloniaList\u003CVisual> VisualChildren { get; }\n",[24,1857,1858],{"class":26,"line":181},[24,1859,297],{},[24,1861,1862],{"class":26,"line":186},[24,1863,453],{},[10,1865,1866,1872,1873,1876],{},[44,1867,1868,1871],{},[21,1869,1870],{},"IInlineHost"," 是一个内部接口,定义了可以承载内联元素(inline elements)的宿主容器。"," 它继承自 ",[21,1874,1875],{},"ILogical",",并提供了两个成员:",[38,1878,1879,1891],{},[41,1880,1881,1887,1888],{},[44,1882,1883,1886],{},[21,1884,1885],{},"Invalidate()"," 方法"," - 用于通知宿主需要",[44,1889,1890],{},"重新测量或重新渲染",[41,1892,1893,1898,1899],{},[44,1894,1895,1065],{},[21,1896,1897],{},"VisualChildren"," - 提供",[44,1900,1901],{},"对宿主的视觉子元素集合的访问",[10,1903,1489,1904,1906],{},[21,1905,1793],{}," 类实现了这个接口:",[468,1908,1909,1924],{},[41,1910,1911,1916,1917,1920,1921],{},[44,1912,1913,1915],{},[21,1914,1885],{}," 实现"," - 调用 ",[21,1918,1919],{},"InvalidateMeasure()"," 来触发重新测量",[24,1922,1923],{},"link to Repo AvaloniaUI\u002FAvalonia: src\u002FAvalonia.Controls\u002FTextBlock.cs:901-904",[41,1925,1926,1930,1931,1933,1934,1080,1936],{},[44,1927,1928,1915],{},[21,1929,1897],{}," - 返回 ",[21,1932,1793],{}," 的 ",[21,1935,1897],{},[24,1937,1938],{},"link to Repo AvaloniaUI\u002FAvalonia: src\u002FAvalonia.Controls\u002FTextBlock.cs:906-906",[10,1940,1941,1943,1944,1946,1947,1949],{},[21,1942,1870],{}," 是一个内部接口(标记为 ",[21,1945,1544],{},"),不应该由用户代码直接实现。 它是 Avalonia 文档系统(Documents)的一部分,用于实现富文本功能,允许在 ",[21,1948,1793],{}," 中混合文本和控件。 这个接口确保了内联元素和宿主之间的松耦合,使得内联元素可以独立于具体的宿主实现。",[10,1951,1952,1953,1956,1957,1959],{},"在 Avalonia 的文档系统中,",[44,1954,1955],{},"内联元素","是指可以在 ",[21,1958,1793],{}," 中使用的富文本元素。 主要包括以下几种:",[244,1961,1963],{"id":1962},"_1-run-文本运行","1. Run - 文本运行",[10,1965,1966,1969],{},[21,1967,1968],{},"Run"," 是最基本的内联元素,用于显示一段文本。 Run.cs:31-51 它可以设置自己的文本内容和样式属性(如字体、颜色等)。",[10,1971,1972],{},"使用示例: TextBlockTests.cs:223-225",[244,1974,1976],{"id":1975},"_2-linebreak-换行符","2. LineBreak - 换行符",[10,1978,1979,1982],{},[21,1980,1981],{},"LineBreak"," 用于强制换行。 LineBreak.cs:9-20 它会在文本中插入一个换行符。 LineBreak.cs:22-31",[244,1984,1986],{"id":1985},"_3-span-内联容器","3. Span - 内联容器",[10,1988,1989,1992,1993,1995],{},[21,1990,1991],{},"Span"," 用于分组其他内联元素。 Span.cs:9-12 它本身也是一个内联元素,但可以包含其他内联元素(包括嵌套的 ",[21,1994,1991],{},")。 Span.cs:23-39",[10,1997,1998],{},"使用示例: TextBlockTests.cs:288",[244,2000,2002],{"id":2001},"_4-inlineuicontainer-嵌入控件","4. InlineUIContainer - 嵌入控件",[10,2004,2005,2008,2009,2011],{},[21,2006,2007],{},"InlineUIContainer"," 是最特殊的内联元素,它允许在文本流中嵌入任意的 ",[21,2010,35],{}," 控件。 InlineUIContainer.cs:9-13",[10,2013,2014],{},"例如,您可以在文本中嵌入按钮、图片等控件: TextBlockTests.cs:357-369",[10,2016,2017],{},"或者嵌入图片: TextBlockTests.cs:392-400",[244,2019,2021],{"id":2020},"textblock的测量逻辑","TextBlock的测量逻辑",[14,2023,2025],{"className":16,"code":2024,"language":18,"meta":19,"style":19},"protected override Size MeasureOverride(Size availableSize)\n        {\n            var scale = LayoutHelper.GetLayoutScale(this);\n            var padding = LayoutHelper.RoundLayoutThickness(Padding, scale);\n            var deflatedSize = availableSize.Deflate(padding);\n\n            if (_constraint != deflatedSize)\n            {\n                \u002F\u002FReset TextLayout when the constraint is not matching.\n                _textLayout?.Dispose();\n                _textLayout = null;\n                _constraint = deflatedSize;\n\n                \u002F\u002FForce arrange so text will be properly alligned.\n                InvalidateArrange();\n            }\n           \n            var inlines = Inlines;\n\n            if (HasComplexContent)\n            {\n                var textRuns = new List\u003CTextRun>();\n\n                foreach (var inline in inlines!)\n                {\n                    inline.BuildTextRun(textRuns);\n                }\n\n                _textRuns = textRuns;\n            }\n\n            \u002F\u002FThis implicitly recreated the TextLayout with a new constraint if we previously reset it.\n            var textLayout = TextLayout;\n\n            \u002F\u002F The textWidth used here is matching that TextPresenter uses to measure the text.\n            var size = LayoutHelper.RoundLayoutSizeUp(new Size(textLayout.WidthIncludingTrailingWhitespace, textLayout.Height).Inflate(padding), 1);\n\n            return size;\n        }\n",[21,2026,2027,2032,2036,2041,2046,2051,2055,2060,2064,2069,2074,2079,2084,2088,2093,2098,2102,2107,2112,2116,2121,2125,2130,2134,2139,2144,2149,2154,2158,2163,2167,2171,2176,2181,2185,2190,2195,2199,2204],{"__ignoreMap":19},[24,2028,2029],{"class":26,"line":27},[24,2030,2031],{},"protected override Size MeasureOverride(Size availableSize)\n",[24,2033,2034],{"class":26,"line":129},[24,2035,325],{},[24,2037,2038],{"class":26,"line":135},[24,2039,2040],{},"            var scale = LayoutHelper.GetLayoutScale(this);\n",[24,2042,2043],{"class":26,"line":141},[24,2044,2045],{},"            var padding = LayoutHelper.RoundLayoutThickness(Padding, scale);\n",[24,2047,2048],{"class":26,"line":147},[24,2049,2050],{},"            var deflatedSize = availableSize.Deflate(padding);\n",[24,2052,2053],{"class":26,"line":153},[24,2054,936],{"emptyLinePlaceholder":935},[24,2056,2057],{"class":26,"line":159},[24,2058,2059],{},"            if (_constraint != deflatedSize)\n",[24,2061,2062],{"class":26,"line":165},[24,2063,1265],{},[24,2065,2066],{"class":26,"line":170},[24,2067,2068],{},"                \u002F\u002FReset TextLayout when the constraint is not matching.\n",[24,2070,2071],{"class":26,"line":176},[24,2072,2073],{},"                _textLayout?.Dispose();\n",[24,2075,2076],{"class":26,"line":181},[24,2077,2078],{},"                _textLayout = null;\n",[24,2080,2081],{"class":26,"line":186},[24,2082,2083],{},"                _constraint = deflatedSize;\n",[24,2085,2086],{"class":26,"line":192},[24,2087,936],{"emptyLinePlaceholder":935},[24,2089,2090],{"class":26,"line":198},[24,2091,2092],{},"                \u002F\u002FForce arrange so text will be properly alligned.\n",[24,2094,2095],{"class":26,"line":204},[24,2096,2097],{},"                InvalidateArrange();\n",[24,2099,2100],{"class":26,"line":328},[24,2101,1303],{},[24,2103,2104],{"class":26,"line":334},[24,2105,2106],{},"           \n",[24,2108,2109],{"class":26,"line":340},[24,2110,2111],{},"            var inlines = Inlines;\n",[24,2113,2114],{"class":26,"line":346},[24,2115,936],{"emptyLinePlaceholder":935},[24,2117,2118],{"class":26,"line":351},[24,2119,2120],{},"            if (HasComplexContent)\n",[24,2122,2123],{"class":26,"line":356},[24,2124,1265],{},[24,2126,2127],{"class":26,"line":362},[24,2128,2129],{},"                var textRuns = new List\u003CTextRun>();\n",[24,2131,2132],{"class":26,"line":367},[24,2133,936],{"emptyLinePlaceholder":935},[24,2135,2136],{"class":26,"line":373},[24,2137,2138],{},"                foreach (var inline in inlines!)\n",[24,2140,2141],{"class":26,"line":379},[24,2142,2143],{},"                {\n",[24,2145,2146],{"class":26,"line":384},[24,2147,2148],{},"                    inline.BuildTextRun(textRuns);\n",[24,2150,2151],{"class":26,"line":389},[24,2152,2153],{},"                }\n",[24,2155,2156],{"class":26,"line":395},[24,2157,936],{"emptyLinePlaceholder":935},[24,2159,2160],{"class":26,"line":400},[24,2161,2162],{},"                _textRuns = textRuns;\n",[24,2164,2165],{"class":26,"line":406},[24,2166,1303],{},[24,2168,2169],{"class":26,"line":412},[24,2170,936],{"emptyLinePlaceholder":935},[24,2172,2173],{"class":26,"line":417},[24,2174,2175],{},"            \u002F\u002FThis implicitly recreated the TextLayout with a new constraint if we previously reset it.\n",[24,2177,2178],{"class":26,"line":422},[24,2179,2180],{},"            var textLayout = TextLayout;\n",[24,2182,2183],{"class":26,"line":428},[24,2184,936],{"emptyLinePlaceholder":935},[24,2186,2187],{"class":26,"line":433},[24,2188,2189],{},"            \u002F\u002F The textWidth used here is matching that TextPresenter uses to measure the text.\n",[24,2191,2192],{"class":26,"line":439},[24,2193,2194],{},"            var size = LayoutHelper.RoundLayoutSizeUp(new Size(textLayout.WidthIncludingTrailingWhitespace, textLayout.Height).Inflate(padding), 1);\n",[24,2196,2197],{"class":26,"line":445},[24,2198,936],{"emptyLinePlaceholder":935},[24,2200,2201],{"class":26,"line":450},[24,2202,2203],{},"            return size;\n",[24,2205,2206],{"class":26,"line":749},[24,2207,343],{},[10,2209,2210],{},"下面是这段测量逻辑的详细分解和讲解：",[2212,2213],"hr",{},[244,2215,2217],{"id":2216},"_1-预处理与尺寸调整-padding-and-scaling","1. 预处理与尺寸调整 (Padding and Scaling)",[14,2219,2221],{"className":16,"code":2220,"language":18,"meta":19,"style":19},"var scale = LayoutHelper.GetLayoutScale(this);\nvar padding = LayoutHelper.RoundLayoutThickness(Padding, scale);\nvar deflatedSize = availableSize.Deflate(padding);\n",[21,2222,2223,2228,2233],{"__ignoreMap":19},[24,2224,2225],{"class":26,"line":27},[24,2226,2227],{},"var scale = LayoutHelper.GetLayoutScale(this);\n",[24,2229,2230],{"class":26,"line":129},[24,2231,2232],{},"var padding = LayoutHelper.RoundLayoutThickness(Padding, scale);\n",[24,2234,2235],{"class":26,"line":135},[24,2236,2237],{},"var deflatedSize = availableSize.Deflate(padding);\n",[38,2239,2240,2254,2271],{},[41,2241,2242,2249,2250,2253],{},[44,2243,2244,2245,2248],{},"获取 DPI 缩放比例 (",[21,2246,2247],{},"scale","):"," 获取当前控件所处的 UI 环境的布局缩放比例（DPI Scale）。这确保了布局计算是",[44,2251,2252],{},"DIPs (设备无关像素)"," 而非物理像素。",[41,2255,2256,2262,2263,2266,2267,2270],{},[44,2257,2258,2259,2248],{},"处理内边距 (",[21,2260,2261],{},"padding"," 使用 ",[21,2264,2265],{},"LayoutHelper.RoundLayoutThickness"," 将控件设置的 ",[21,2268,2269],{},"Padding"," 应用于布局缩放，并进行四舍五入以避免布局中的子像素问题。",[41,2272,2273,2279,2280,2283,2284,2287,2288,2290,2291,2294],{},[44,2274,2275,2276,2248],{},"计算可用内容空间 (",[21,2277,2278],{},"deflatedSize"," 将内边距从父容器提供的 ",[21,2281,2282],{},"availableSize"," 中减去（",[21,2285,2286],{},"Deflate","）。这个 ",[21,2289,2278],{}," 就是",[44,2292,2293],{},"实际可用于容纳文本内容","的空间。",[2212,2296],{},[244,2298,2300],{"id":2299},"_2-文本布局缓存和更新-textlayout-caching","2. 文本布局缓存和更新 (TextLayout Caching)",[14,2302,2304],{"className":16,"code":2303,"language":18,"meta":19,"style":19},"if (_constraint != deflatedSize)\n{\n    \u002F\u002FReset TextLayout when the constraint is not matching.\n    _textLayout?.Dispose();\n    _textLayout = null;\n    _constraint = deflatedSize;\n\n    \u002F\u002FForce arrange so text will be properly alligned.\n    InvalidateArrange();\n}\n",[21,2305,2306,2311,2315,2320,2325,2330,2335,2339,2344,2349],{"__ignoreMap":19},[24,2307,2308],{"class":26,"line":27},[24,2309,2310],{},"if (_constraint != deflatedSize)\n",[24,2312,2313],{"class":26,"line":129},[24,2314,262],{},[24,2316,2317],{"class":26,"line":135},[24,2318,2319],{},"    \u002F\u002FReset TextLayout when the constraint is not matching.\n",[24,2321,2322],{"class":26,"line":141},[24,2323,2324],{},"    _textLayout?.Dispose();\n",[24,2326,2327],{"class":26,"line":147},[24,2328,2329],{},"    _textLayout = null;\n",[24,2331,2332],{"class":26,"line":153},[24,2333,2334],{},"    _constraint = deflatedSize;\n",[24,2336,2337],{"class":26,"line":159},[24,2338,936],{"emptyLinePlaceholder":935},[24,2340,2341],{"class":26,"line":165},[24,2342,2343],{},"    \u002F\u002FForce arrange so text will be properly alligned.\n",[24,2345,2346],{"class":26,"line":170},[24,2347,2348],{},"    InvalidateArrange();\n",[24,2350,2351],{"class":26,"line":176},[24,2352,453],{},[10,2354,2355],{},"这是提高性能的关键步骤：",[38,2357,2358,2374,2418],{},[41,2359,2360,2363,2364,2366,2367,2366,2370,2373],{},[44,2361,2362],{},"检查约束变化："," 比较当前的内容可用空间 ",[21,2365,2278],{}," ",[44,2368,2369],{},"是否与上次测量使用的约束",[21,2371,2372],{},"_constraint"," 相同。",[41,2375,2376,2383],{},[44,2377,2378,2379,2382],{},"重新创建 ",[21,2380,2381],{},"TextLayout","：",[468,2384,2385,2392,2410],{},[41,2386,2387,2388,2391],{},"如果约束",[44,2389,2390],{},"发生变化","（例如父容器尺寸改变），说明文本的排版（如换行）可能需要重新计算。",[41,2393,2394,2395,2402,2403,2406,2407,228],{},"因此，它会 ",[44,2396,2397,2398,2401],{},"销毁 (",[21,2399,2400],{},"Dispose",")"," 旧的 ",[21,2404,2405],{},"_textLayout"," 对象，并将其设置为 ",[21,2408,2409],{},"null",[41,2411,2412,2413,2415,2416,228],{},"同时，将 ",[21,2414,2372],{}," 更新为新的 ",[21,2417,2278],{},[41,2419,2420,2426,2427,2430,2431,2434,2435,2438,2439,2442,2443,2446],{},[44,2421,2422,2423,2248],{},"强制排列 (",[21,2424,2425],{},"InvalidateArrange"," 调用 ",[21,2428,2429],{},"InvalidateArrange()"," 来",[44,2432,2433],{},"强制触发","后续的 ",[44,2436,2437],{},"排列阶段 (Arrange Pass)","。这是因为即使测量结果（",[21,2440,2441],{},"MeasureOverride"," 的返回值）可能不变，但由于约束变了，文本的",[44,2444,2445],{},"内部对齐或渲染位置","可能需要调整。",[2212,2448],{},[244,2450,2452],{"id":2451},"_3-准备文本运行-prepare-text-runs","3. 准备文本运行 (Prepare Text Runs)",[14,2454,2456],{"className":16,"code":2455,"language":18,"meta":19,"style":19},"var inlines = Inlines;\n\nif (HasComplexContent)\n{\n    var textRuns = new List\u003CTextRun>();\n\n    foreach (var inline in inlines!)\n    {\n        inline.BuildTextRun(textRuns);\n    }\n\n    _textRuns = textRuns;\n}\n",[21,2457,2458,2463,2467,2472,2476,2481,2485,2490,2494,2499,2503,2507,2512],{"__ignoreMap":19},[24,2459,2460],{"class":26,"line":27},[24,2461,2462],{},"var inlines = Inlines;\n",[24,2464,2465],{"class":26,"line":129},[24,2466,936],{"emptyLinePlaceholder":935},[24,2468,2469],{"class":26,"line":135},[24,2470,2471],{},"if (HasComplexContent)\n",[24,2473,2474],{"class":26,"line":141},[24,2475,262],{},[24,2477,2478],{"class":26,"line":147},[24,2479,2480],{},"    var textRuns = new List\u003CTextRun>();\n",[24,2482,2483],{"class":26,"line":153},[24,2484,936],{"emptyLinePlaceholder":935},[24,2486,2487],{"class":26,"line":159},[24,2488,2489],{},"    foreach (var inline in inlines!)\n",[24,2491,2492],{"class":26,"line":165},[24,2493,272],{},[24,2495,2496],{"class":26,"line":170},[24,2497,2498],{},"        inline.BuildTextRun(textRuns);\n",[24,2500,2501],{"class":26,"line":176},[24,2502,297],{},[24,2504,2505],{"class":26,"line":181},[24,2506,936],{"emptyLinePlaceholder":935},[24,2508,2509],{"class":26,"line":186},[24,2510,2511],{},"    _textRuns = textRuns;\n",[24,2513,2514],{"class":26,"line":192},[24,2515,453],{},[38,2517,2518,2535,2561],{},[41,2519,2520,2526,2527,2530,2531,2534],{},[44,2521,2522,2523,2248],{},"处理复杂内容 (",[21,2524,2525],{},"HasComplexContent"," 这段代码处理的是控件可能包含",[44,2528,2529],{},"富文本 (Rich Text)"," 或 ",[44,2532,2533],{},"内联元素 (Inlines)"," 的情况。",[41,2536,2537,2544,2545,2548,2549,2552,2553,2557,2558,2560],{},[44,2538,2539,2540,2543],{},"构建 ",[21,2541,2542],{},"TextRun"," 集合："," 如果是复杂内容，它会遍历所有的内联元素（如 ",[21,2546,2547],{},"Bold",", ",[21,2550,2551],{},"Italic",", 或其他内嵌控件），并将它们转换为 ",[44,2554,2555],{},[21,2556,2542],{}," 对象的列表。",[21,2559,2542],{}," 是渲染引擎理解的最小文本单元，包含文本内容和其格式（字体、颜色等）。",[41,2562,2563,2569,2570,2573,2574,2576],{},[44,2564,2565,2566,2382],{},"缓存 ",[21,2567,2568],{},"_textRuns"," 将构建好的 ",[21,2571,2572],{},"textRuns"," 缓存起来，供下一步创建 ",[21,2575,2381],{}," 使用。",[2212,2578],{},[244,2580,2582],{"id":2581},"_4-获取文本布局结果-determine-desired-size","4. 获取文本布局结果 (Determine Desired Size)",[14,2584,2586],{"className":16,"code":2585,"language":18,"meta":19,"style":19},"\u002F\u002FThis implicitly recreated the TextLayout with a new constraint if we previously reset it.\nvar textLayout = TextLayout;\n\n\u002F\u002F The textWidth used here is matching that TextPresenter uses to measure the text.\nvar size = LayoutHelper.RoundLayoutSizeUp(new Size(textLayout.WidthIncludingTrailingWhitespace, textLayout.Height).Inflate(padding), 1);\n\nreturn size;\n",[21,2587,2588,2593,2598,2602,2607,2612,2616],{"__ignoreMap":19},[24,2589,2590],{"class":26,"line":27},[24,2591,2592],{},"\u002F\u002FThis implicitly recreated the TextLayout with a new constraint if we previously reset it.\n",[24,2594,2595],{"class":26,"line":129},[24,2596,2597],{},"var textLayout = TextLayout;\n",[24,2599,2600],{"class":26,"line":135},[24,2601,936],{"emptyLinePlaceholder":935},[24,2603,2604],{"class":26,"line":141},[24,2605,2606],{},"\u002F\u002F The textWidth used here is matching that TextPresenter uses to measure the text.\n",[24,2608,2609],{"class":26,"line":147},[24,2610,2611],{},"var size = LayoutHelper.RoundLayoutSizeUp(new Size(textLayout.WidthIncludingTrailingWhitespace, textLayout.Height).Inflate(padding), 1);\n",[24,2613,2614],{"class":26,"line":153},[24,2615,936],{"emptyLinePlaceholder":935},[24,2617,2618],{"class":26,"line":159},[24,2619,2620],{},"return size;\n",[38,2622,2623,2653,2704],{},[41,2624,2625,2366,2630,2632,2633,2635,2636,2638,2639,2641,2642,1519,2644,2430,2646,2649,2650,2652],{},[44,2626,2627,2628,2382],{},"创建\u002F获取 ",[21,2629,2381],{},[21,2631,2381],{}," 属性的 getter 会检查 ",[21,2634,2405],{}," 是否为 ",[21,2637,2409],{},"。如果为 ",[21,2640,2409],{},"（在第 2 步中被重置），它会使用最新的 ",[21,2643,2372],{},[21,2645,2568],{},[44,2647,2648],{},"隐式地创建","一个新的 ",[21,2651,2381],{}," 对象。这个对象包含了文本实际排版后的尺寸信息。",[41,2654,2655,2661],{},[44,2656,2657,2658,2248],{},"计算最终尺寸 (",[21,2659,2660],{},"size",[468,2662,2663,2684,2698],{},[41,2664,2665,2666,2669,2670,2673,2674,2677,2678,2673,2681,2683],{},"它从 ",[21,2667,2668],{},"textLayout"," 中获取文本内容的",[44,2671,2672],{},"实际排版宽度"," (",[21,2675,2676],{},"WidthIncludingTrailingWhitespace",") 和",[44,2679,2680],{},"高度",[21,2682,497],{},")。",[41,2685,2686,2687,2366,2690,2693,2694,2697],{},"使用 ",[21,2688,2689],{},"Inflate(padding)",[44,2691,2692],{},"加回","控件的内边距，得到",[44,2695,2696],{},"包含内边距","的控件所需总尺寸。",[41,2699,2686,2700,2703],{},[21,2701,2702],{},"LayoutHelper.RoundLayoutSizeUp"," 对最终尺寸进行向上取整和缩放，以确保尺寸兼容 DPI 缩放且没有舍入错误。",[41,2705,2706,2709,2710,2712,2713,228],{},[44,2707,2708],{},"返回结果："," 将计算出的 ",[21,2711,2660],{}," 返回。这是控件在当前约束下，为容纳其内容",[44,2714,2715],{},"所需的最小尺寸",[244,2717,2718],{"id":2718},"总结布局流程",[10,2720,2721,2722,2725,2726,2729],{},"这段代码完美体现了 Avalonia 的",[44,2723,2724],{},"文本布局","和 ",[44,2727,2728],{},"两步布局 (Two-Pass Layout)"," 机制：",[468,2731,2732,2747],{},[41,2733,2734,2739,2740,2743,2744,2746],{},[44,2735,2736,2737,2248],{},"Measure 阶段 (",[21,2738,2441],{}," 确定",[44,2741,2742],{},"需要多大","。通过 ",[21,2745,2381],{}," 计算内容所需的尺寸，并返回。",[41,2748,2749,2739,2752,2755,2756,2759,2760,2762,2763,2766,2767,2366,2769,228],{},[44,2750,2751],{},"Arrange 阶段 (未显示):",[44,2753,2754],{},"放在哪里，最终多大","。在 ",[21,2757,2758],{},"ArrangeOverride"," 中，控件将使用 ",[21,2761,2441],{}," 确定的理想尺寸（或父容器分配给它的最终尺寸）来",[44,2764,2765],{},"定位","其内部的内容（文本），这就是为什么在约束变化时要调用 ",[21,2768,2429],{},[44,2770,2771],{},"来确保文本对齐正确",[244,2773,2774],{"id":2774},"脏组件判断触发测量和渲染",[14,2776,2778],{"className":16,"code":2777,"language":18,"meta":19,"style":19}," protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)\n        {\n            base.OnPropertyChanged(change);\n\n            if (change.Property == TextProperty)\n            {\n                if (HasComplexContent && !_clearTextInternal)\n                {\n                    Inlines?.Clear();\n                }\n            }\n\n            switch (change.Property.Name)\n            {\n                case nameof(FontSize):\n                case nameof(FontWeight):\n                case nameof(FontStyle):\n                case nameof(FontFamily):\n                case nameof(FontStretch):\n\n                case nameof(TextWrapping):\n                case nameof(TextTrimming):\n                case nameof(TextAlignment):\n\n                case nameof(FlowDirection):\n\n                case nameof(Padding):\n                case nameof(LineHeight):\n                case nameof(LetterSpacing):\n                case nameof(MaxLines):\n\n                case nameof(Text):\n                case nameof(TextDecorations):\n                case nameof(FontFeatures):\n                case nameof(Foreground):\n                    {\n                        InvalidateTextLayout();\n                        break;\n                    }\n                case nameof(Inlines):\n                    {\n                        OnInlinesChanged(change.OldValue as InlineCollection, change.NewValue as InlineCollection);\n                        InvalidateTextLayout();\n                        break;\n                    }\n            }\n        }\n",[21,2779,2780,2785,2789,2794,2798,2803,2807,2812,2816,2821,2825,2829,2833,2838,2842,2847,2852,2857,2862,2867,2871,2876,2881,2886,2890,2895,2899,2904,2909,2914,2919,2923,2928,2933,2938,2943,2948,2953,2958,2963,2968,2972,2977,2981,2985,2989,2993],{"__ignoreMap":19},[24,2781,2782],{"class":26,"line":27},[24,2783,2784],{}," protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)\n",[24,2786,2787],{"class":26,"line":129},[24,2788,325],{},[24,2790,2791],{"class":26,"line":135},[24,2792,2793],{},"            base.OnPropertyChanged(change);\n",[24,2795,2796],{"class":26,"line":141},[24,2797,936],{"emptyLinePlaceholder":935},[24,2799,2800],{"class":26,"line":147},[24,2801,2802],{},"            if (change.Property == TextProperty)\n",[24,2804,2805],{"class":26,"line":153},[24,2806,1265],{},[24,2808,2809],{"class":26,"line":159},[24,2810,2811],{},"                if (HasComplexContent && !_clearTextInternal)\n",[24,2813,2814],{"class":26,"line":165},[24,2815,2143],{},[24,2817,2818],{"class":26,"line":170},[24,2819,2820],{},"                    Inlines?.Clear();\n",[24,2822,2823],{"class":26,"line":176},[24,2824,2153],{},[24,2826,2827],{"class":26,"line":181},[24,2828,1303],{},[24,2830,2831],{"class":26,"line":186},[24,2832,936],{"emptyLinePlaceholder":935},[24,2834,2835],{"class":26,"line":192},[24,2836,2837],{},"            switch (change.Property.Name)\n",[24,2839,2840],{"class":26,"line":198},[24,2841,1265],{},[24,2843,2844],{"class":26,"line":204},[24,2845,2846],{},"                case nameof(FontSize):\n",[24,2848,2849],{"class":26,"line":328},[24,2850,2851],{},"                case nameof(FontWeight):\n",[24,2853,2854],{"class":26,"line":334},[24,2855,2856],{},"                case nameof(FontStyle):\n",[24,2858,2859],{"class":26,"line":340},[24,2860,2861],{},"                case nameof(FontFamily):\n",[24,2863,2864],{"class":26,"line":346},[24,2865,2866],{},"                case nameof(FontStretch):\n",[24,2868,2869],{"class":26,"line":351},[24,2870,936],{"emptyLinePlaceholder":935},[24,2872,2873],{"class":26,"line":356},[24,2874,2875],{},"                case nameof(TextWrapping):\n",[24,2877,2878],{"class":26,"line":362},[24,2879,2880],{},"                case nameof(TextTrimming):\n",[24,2882,2883],{"class":26,"line":367},[24,2884,2885],{},"                case nameof(TextAlignment):\n",[24,2887,2888],{"class":26,"line":373},[24,2889,936],{"emptyLinePlaceholder":935},[24,2891,2892],{"class":26,"line":379},[24,2893,2894],{},"                case nameof(FlowDirection):\n",[24,2896,2897],{"class":26,"line":384},[24,2898,936],{"emptyLinePlaceholder":935},[24,2900,2901],{"class":26,"line":389},[24,2902,2903],{},"                case nameof(Padding):\n",[24,2905,2906],{"class":26,"line":395},[24,2907,2908],{},"                case nameof(LineHeight):\n",[24,2910,2911],{"class":26,"line":400},[24,2912,2913],{},"                case nameof(LetterSpacing):\n",[24,2915,2916],{"class":26,"line":406},[24,2917,2918],{},"                case nameof(MaxLines):\n",[24,2920,2921],{"class":26,"line":412},[24,2922,936],{"emptyLinePlaceholder":935},[24,2924,2925],{"class":26,"line":417},[24,2926,2927],{},"                case nameof(Text):\n",[24,2929,2930],{"class":26,"line":422},[24,2931,2932],{},"                case nameof(TextDecorations):\n",[24,2934,2935],{"class":26,"line":428},[24,2936,2937],{},"                case nameof(FontFeatures):\n",[24,2939,2940],{"class":26,"line":433},[24,2941,2942],{},"                case nameof(Foreground):\n",[24,2944,2945],{"class":26,"line":439},[24,2946,2947],{},"                    {\n",[24,2949,2950],{"class":26,"line":445},[24,2951,2952],{},"                        InvalidateTextLayout();\n",[24,2954,2955],{"class":26,"line":450},[24,2956,2957],{},"                        break;\n",[24,2959,2960],{"class":26,"line":749},[24,2961,2962],{},"                    }\n",[24,2964,2965],{"class":26,"line":755},[24,2966,2967],{},"                case nameof(Inlines):\n",[24,2969,2970],{"class":26,"line":760},[24,2971,2947],{},[24,2973,2974],{"class":26,"line":765},[24,2975,2976],{},"                        OnInlinesChanged(change.OldValue as InlineCollection, change.NewValue as InlineCollection);\n",[24,2978,2979],{"class":26,"line":770},[24,2980,2952],{},[24,2982,2983],{"class":26,"line":776},[24,2984,2957],{},[24,2986,2987],{"class":26,"line":782},[24,2988,2962],{},[24,2990,2991],{"class":26,"line":788},[24,2992,1303],{},[24,2994,2995],{"class":26,"line":794},[24,2996,343],{},[10,2998,2999,3000,3007,3008,3011],{},"这段代码的目的是在控件的",[44,3001,3002,3003,3006],{},"依赖属性 (",[21,3004,3005],{},"StyledProperty",") 发生变化","时做出反应，特别是针对那些影响文本渲染和布局的关键属性。它通过调用 ",[21,3009,3010],{},"InvalidateTextLayout()"," 来标记控件的文本布局为“脏”（Dirty），从而触发重新的测量和渲染。",[10,3013,3014,3015,2548,3018,2548,3021,3023,3024,3027],{},"当任何依赖属性（如 ",[21,3016,3017],{},"FontSize",[21,3019,3020],{},"Text",[21,3022,2269],{}," 等）的值通过绑定、样式或直接代码设置而改变时，这个 ",[21,3025,3026],{},"OnPropertyChanged"," 方法就会被调用。",[10,3029,3030,3033,3034,3037,3038,3041],{},[44,3031,3032],{},"布局\u002F渲染管线："," Avalonia 的",[44,3035,3036],{},"调度器","会在下一个主循环中，按照顺序执行",[44,3039,3040],{},"全局","的测量、排列和渲染阶段，只处理那些被标记为“脏”的控件。",[98,3043,3045],{"id":3044},"contentcontrol","ContentControl",[10,3047,3048,3049,3052],{},"这是一个模块化的控件，用于根据数据末班来显示内容，它继承自 ",[21,3050,3051],{},"TemplatedControl"," 并实现了两个接口: ContentControl.cs:20",[244,3054,3055],{"id":3055},"实现的接口",[38,3057,3058,3093],{},[41,3059,3060,3063,3066,3067,3069,3070],{},[21,3061,3062],{},"IContentControl",[3064,3065],"br",{},"- 定义了使用模板显示内容的控件的契约。",[3064,3068],{},"该接口要求实现:",[468,3071,3072,3078,3084],{},[41,3073,3074,3077],{},[21,3075,3076],{},"Content"," 属性(要显示的内容) IContentControl.cs:16",[41,3079,3080,3083],{},[21,3081,3082],{},"ContentTemplate"," 属性(用于渲染的数据模板) IContentControl.cs:21",[41,3085,3086,1519,3089,3092],{},[21,3087,3088],{},"HorizontalContentAlignment",[21,3090,3091],{},"VerticalContentAlignment"," 属性(内容对齐方式) IContentControl.cs:26-31",[41,3094,3095,3098,3100,3101,3103,3104],{},[21,3096,3097],{},"IContentPresenterHost",[3064,3099],{},"- 定义了承载ContentPresenter的控件的接口。",[3064,3102],{},"该接口负责:",[468,3105,3106,3113],{},[41,3107,3108,3109,3112],{},"通过 ",[21,3110,3111],{},"LogicalChildren"," 属性管理逻辑子元素 IContentPresenterHost.cs:22",[41,3114,3108,3115,3118,3119,3122],{},[21,3116,3117],{},"RegisterContentPresenter()"," 方法注册 ",[21,3120,3121],{},"ContentPresenter"," 实例 IContentPresenterHost.cs:32",[244,3124,1341],{"id":3125},"核心功能-2",[10,3127,3128,3129,3132,3133,3136,3137,3139,3140,3143],{},"该类使用名为 ",[21,3130,3131],{},"\"PART_ContentPresenter\""," 的模板部件来",[44,3134,3135],{},"渲染其内容","。 ContentControl.cs:19 默认模板会创建一个 ",[21,3138,3121],{},",并将",[44,3141,3142],{},"其属性绑定到控件的相应属性","。 ContentControl.cs:48-61",[10,3145,3146,3147,3149,3150,3153],{},"当 ",[21,3148,3121],{}," 注册自己时,如果它的名称正确,控件会将其存储在 ",[21,3151,3152],{},"Presenter"," 属性中。 ContentControl.cs:136-139",[244,3155,3156],{"id":3156},"主要属性",[468,3158,3159,3168,3177,3186],{},[41,3160,3161,3163,3164,3167],{},[21,3162,3076],{}," - 要显示的",[44,3165,3166],{},"内容对象"," ContentControl.cs:69-72",[41,3169,3170,3172,3173,3176],{},[21,3171,3082],{}," - 用于显示内容的",[44,3174,3175],{},"数据模板"," ContentControl.cs:78-81",[41,3178,3179,3181,3182,3185],{},[21,3180,3152],{}," - 从控件模板中获取的",[44,3183,3184],{},"内容呈现器"," ContentControl.cs:87-91",[41,3187,3188,1519,3190,3192],{},[21,3189,3088],{},[21,3191,3091],{}," - 控制内容在控件内的对齐方式 ContentControl.cs:96-108",[98,3194,3051],{"id":3195},"templatedcontrol",[10,3197,3198,3200,3201,3204,3205,3208,3209,3212,3213,3215],{},[21,3199,3051],{}," 是一个",[44,3202,3203],{},"无外观控件","(lookless control),其",[44,3206,3207],{},"视觉外观","由 ",[21,3210,3211],{},"Template"," 属性定义。 TemplatedControl.cs:14-17 它直接继承自 ",[21,3214,35],{}," 类。 TemplatedControl.cs:17",[244,3217,3218],{"id":3218},"外观属性",[468,3220,3221,3227,3233,3239,3245],{},[41,3222,3223,3226],{},[21,3224,3225],{},"Background"," - 控件的背景画刷 TemplatedControl.cs:22-23",[41,3228,3229,3232],{},[21,3230,3231],{},"BorderBrush"," - 边框画刷 TemplatedControl.cs:34-35",[41,3234,3235,3238],{},[21,3236,3237],{},"BorderThickness"," - 边框粗细 TemplatedControl.cs:40-41",[41,3240,3241,3244],{},[21,3242,3243],{},"CornerRadius"," - 圆角半径 TemplatedControl.cs:46-47",[41,3246,3247,3249],{},[21,3248,2269],{}," - 内边距 TemplatedControl.cs:94-95",[244,3251,3252],{"id":3252},"文本属性",[468,3254,3255,3272],{},[41,3256,3257,494,3260,494,3262,494,3265,494,3268,3271],{},[21,3258,3259],{},"FontFamily",[21,3261,3017],{},[21,3263,3264],{},"FontStyle",[21,3266,3267],{},"FontWeight",[21,3269,3270],{},"FontStretch"," - 字体相关属性 TemplatedControl.cs:52-83",[41,3273,3274,1349,3277,3280],{},[21,3275,3276],{},"Foreground",[44,3278,3279],{},"前景色画刷"," TemplatedControl.cs:88-89",[244,3282,3283],{"id":3283},"模板属性",[468,3285,3286],{},[41,3287,3288,1349,3290,3293],{},[21,3289,3211],{},[44,3291,3292],{},"定义控件外观的控件模板"," TemplatedControl.cs:100-101",[244,3295,3296],{"id":3296},"附加属性",[10,3298,3299,3302,3303,3306],{},[21,3300,3301],{},"IsTemplateFocusTarget"," - 附加属性**,用于指定模板中哪个元素应该接收焦点装饰器**。 TemplatedControl.cs:106-107 当控件获得焦点时,",[44,3304,3305],{},"焦点装饰器会显示在标记了此属性的元素周围,而不是控件本身","。 TemplatedControl.cs:280-284",[244,3308,3309],{"id":3309},"备注",[10,3311,3312,3314,3315,3317,3318,3321],{},[21,3313,3051],{}," 是许多 Avalonia 控件的基类,包括之前讨论的 ",[21,3316,3045],{},"。 它提供了模板化控件的基础架构,使得控件的逻辑和外观可以完全分离。 所有模板子元素的 ",[21,3319,3320],{},"TemplatedParent"," 属性都会被设置为该控件实例。 TemplatedControlTests.cs:94-120",[98,3323,3062],{"id":3324},"icontentcontrol",[10,3326,3327,3329,3330,2366,3333,3336],{},[21,3328,3062],{}," 是一个内部接口,定义了根据 ",[21,3331,3332],{},"FuncDataTemplate",[44,3334,3335],{},"显示内容的控件的契约","。 IContentControl.cs:7-10",[244,3338,3340],{"id":3339},"_1-content-属性","1. Content 属性",[10,3342,3343,3344,3347,3348,3351],{},"用于",[44,3345,3346],{},"获取或设置要显示的内容对象","。 IContentControl.cs:13-16 这个属性可以是任何对象类型(",[21,3349,3350],{},"object?","),允许显示各种类型的内容。",[244,3353,3355],{"id":3354},"_2-contenttemplate-属性","2. ContentTemplate 属性",[10,3357,3343,3358,3361,3362,3365,3366,3368],{},[44,3359,3360],{},"获取或设置显示控件内容的数据模板","。 IContentControl.cs:18-21 该属性类型为 ",[21,3363,3364],{},"IDataTemplate?",",用于定义如何将 ",[21,3367,3076],{}," 渲染为可视化元素。",[244,3370,3372],{"id":3371},"_3-horizontalcontentalignment-属性","3. HorizontalContentAlignment 属性",[10,3374,3343,3375,3378,3379,3381],{},[44,3376,3377],{},"获取或设置内容在控件内的水平对齐方式","。 IContentControl.cs:23-26 使用 ",[21,3380,503],{}," 枚举类型。",[244,3383,3385],{"id":3384},"_4-verticalcontentalignment-属性","4. VerticalContentAlignment 属性",[10,3387,3343,3388,3391,3392,3381],{},[44,3389,3390],{},"获取或设置内容在控件内的垂直对齐方式","。 IContentControl.cs:28-31 使用 ",[21,3393,3394],{},"VerticalAlignment",[244,3396,3397],{"id":3397},"实现类",[10,3399,3400,3402,3403,3406],{},[21,3401,3045],{}," 类是该接口的主要实现者。 ContentControl.cs:20 它将这些接口属性实现为样式化属性(StyledProperty),",[44,3404,3405],{},"并提供了完整的功能实现",":",[468,3408,3409,3419,3428,3437],{},[41,3410,3411,3414,3415,3418],{},[21,3412,3413],{},"ContentProperty"," - 定义为 ",[21,3416,3417],{},"StyledProperty\u003Cobject?>"," ContentControl.cs:25-26",[41,3420,3421,3414,3424,3427],{},[21,3422,3423],{},"ContentTemplateProperty",[21,3425,3426],{},"StyledProperty\u003CIDataTemplate?>"," ContentControl.cs:31-32",[41,3429,3430,3414,3433,3436],{},[21,3431,3432],{},"HorizontalContentAlignmentProperty",[21,3434,3435],{},"StyledProperty\u003CHorizontalAlignment>"," ContentControl.cs:37-38",[41,3438,3439,3414,3442,3445],{},[21,3440,3441],{},"VerticalContentAlignmentProperty",[21,3443,3444],{},"StyledProperty\u003CVerticalAlignment>"," ContentControl.cs:43-44",[98,3447,3449],{"id":3448},"icommandsource","ICommandSource",[10,3451,3452,3454,3455,3458,3459,3462,3463,3466],{},[21,3453,3449],{}," 是一个公共接口,",[44,3456,3457],{},"定义了知道如何调用命令的类的契约","。 ICommandSource.cs:5-7 这个接口是 Avalonia ",[44,3460,3461],{},"命令模式实现的核心部分",",允许控件与 ",[21,3464,3465],{},"ICommand"," 对象进行交互。 ICommandSource.cs:1-2",[244,3468,3469],{"id":3469},"接口成员",[244,3471,3473],{"id":3472},"_1-command-属性","1. Command 属性",[10,3475,3476,3477,3480,3481,3484],{},"获取当类被\"调用\"时将执行的命令。 ICommandSource.cs:10-15 实现此接口的类应该根据命令的 ",[21,3478,3479],{},"CanExecute"," 返回值来启用或禁用自身。 ICommandSource.cs:12 ",[44,3482,3483],{},"该属性可以根据需要实现为读写属性","。 ICommandSource.cs:13",[244,3486,3488],{"id":3487},"_2-commandparameter-属性","2. CommandParameter 属性",[10,3490,3491,3492,3495,3496,3499],{},"获取",[44,3493,3494],{},"执行命令时将传递给命令的参数","。 ICommandSource.cs:17-21 该属性也",[44,3497,3498],{},"可以根据需要实现为读写属性","。 ICommandSource.cs:19",[244,3501,3503],{"id":3502},"_3-canexecutechanged-方法","3. CanExecuteChanged 方法",[10,3505,3506,3507,3510,3511,3514],{},"当检测到变化时为 ",[21,3508,3509],{},"CanExecuteChanged"," 事件调用。 ICommandSource.cs:23-28 该方法",[44,3512,3513],{},"接受事件发送者和事件参数作为参数","。 ICommandSource.cs:26-27",[244,3516,3518],{"id":3517},"_4-iseffectivelyenabled-属性","4. IsEffectivelyEnabled 属性",[10,3520,3521,3522,3525],{},"获取一个值,",[44,3523,3524],{},"指示此控件及其所有父控件是否已启用","。 ICommandSource.cs:30-33",[244,3527,3397],{"id":3528},"实现类-1",[244,3530,3532],{"id":3531},"button","Button",[10,3534,3535,3537],{},[21,3536,3532],{}," 类是该接口的主要实现者之一。 Button.cs:35 它实现了完整的命令模式功能:",[468,3539,3540,3550,3556],{},[41,3541,3542,3543,1519,3546,3549],{},"定义了 ",[21,3544,3545],{},"CommandProperty",[21,3547,3548],{},"CommandParameterProperty"," 样式化属性 Button.cs:51-64",[41,3551,3552,3553,3555],{},"在命令的 ",[21,3554,3509],{}," 事件触发时更新控件的启用状态 Button.cs:590-610",[41,3557,3558,3559,3561],{},"在附加到逻辑树时订阅命令的 ",[21,3560,3509],{}," 事件 Button.cs:477-492",[244,3563,3565],{"id":3564},"menuitem","MenuItem",[10,3567,3568,3570],{},[21,3569,3565],{}," 类也实现了此接口。 MenuItem.cs:25 它的实现包括:",[468,3572,3573,3580,3586],{},[41,3574,3542,3575,1519,3577,3579],{},[21,3576,3545],{},[21,3578,3548],{}," MenuItem.cs:33-46",[41,3581,3582,3583,3585],{},"实现了性能优化:仅在菜单打开时才调用 ",[21,3584,3479],{}," MenuItem.cs:662-667",[41,3587,3588,3589,3592],{},"显式实现了 ",[21,3590,3591],{},"ICommandSource.CanExecuteChanged"," 方法 MenuItem.cs:885",[244,3594,3596],{"id":3595},"splitbutton","SplitButton",[10,3598,3599,3601,3602,3604],{},[21,3600,3596],{}," 也实现了该接口。 SplitButton.cs:134 它的实现方式与 ",[21,3603,3532],{}," 类似,在命令状态改变时更新控件的启用状态。 SplitButton.cs:137-151",[244,3606,3608],{"id":3607},"nativemenuitem","NativeMenuItem",[10,3610,3611,3613,3614,1519,3616,3618,3619,3621,3622,3625],{},[21,3612,3608],{}," 类也使用了命令模式。 NativeMenuItem.cs:133-138 它定义了 ",[21,3615,3545],{},[21,3617,3548],{},",并在 ",[21,3620,3509],{}," 方法中更新 ",[21,3623,3624],{},"IsEnabled"," 属性。 NativeMenuItem.cs:166-172",[244,3627,3628],{"id":3628},"与热键管理器的集成",[10,3630,3631,2262,3634,3636,3637,3639],{},[21,3632,3633],{},"HotKeyManager",[21,3635,3449],{}," 接口来处理热键绑定。 HotkeyManager.cs:27-29 当控件实现了 ",[21,3638,3449],{}," 接口时,热键管理器会:",[468,3641,3642,3645,3651],{},[41,3643,3644],{},"检查命令是否可以执行 HotkeyManager.cs:43-46",[41,3646,2686,3647,3650],{},[21,3648,3649],{},"CommandParameter"," 执行命令 HotkeyManager.cs:60-62",[41,3652,3653,3654,2530,3656,3659],{},"验证控件是否实现了 ",[21,3655,3449],{},[21,3657,3658],{},"IClickableControl"," HotkeyManager.cs:152",[14,3661,3663],{"className":16,"code":3662,"language":18,"meta":19,"style":19},"    [PseudoClasses(pcFlyoutOpen, pcPressed)]\n    public class Button : ContentControl, ICommandSource, IClickableControl\n    {\n        private const string pcPressed = \":pressed\";\n        private const string pcFlyoutOpen = \":flyout-open\";\n",[21,3664,3665,3670,3675,3679,3684],{"__ignoreMap":19},[24,3666,3667],{"class":26,"line":27},[24,3668,3669],{},"    [PseudoClasses(pcFlyoutOpen, pcPressed)]\n",[24,3671,3672],{"class":26,"line":129},[24,3673,3674],{},"    public class Button : ContentControl, ICommandSource, IClickableControl\n",[24,3676,3677],{"class":26,"line":135},[24,3678,272],{},[24,3680,3681],{"class":26,"line":141},[24,3682,3683],{},"        private const string pcPressed = \":pressed\";\n",[24,3685,3686],{"class":26,"line":147},[24,3687,3688],{},"        private const string pcFlyoutOpen = \":flyout-open\";\n",[10,3690,3691,3694,3695,3698,3699,3702,3703,228],{},[21,3692,3693],{},"[PseudoClasses(pcFlyoutOpen, pcPressed)]"," 是一个特性(Attribute),用于",[44,3696,3697],{},"声明该控件支持的伪类","。 Button.cs:34 这个特性定义在 ",[21,3700,3701],{},"PseudoClassesAttribute"," 类中,主要",[44,3704,3705],{},"用于 IDE 的代码补全功能",[10,3707,3708,3709,3712],{},"这两个常量",[44,3710,3711],{},"定义了伪类","的字符串名称: Button.cs:37-38",[468,3714,3715,3721],{},[41,3716,3717,3720],{},[21,3718,3719],{},"pcPressed = \":pressed\""," - 按钮按下状态",[41,3722,3723,3726],{},[21,3724,3725],{},"pcFlyoutOpen = \":flyout-open\""," - 浮出菜单打开状态",[1787,3728,3729],{"id":3729},"渲染逻辑",[10,3731,3732,3733,3736,3737,3740,3741,3744,3745,3748],{},"Avalonia 使用",[44,3734,3735],{},"双线程架构",",将 UI 线程和渲染线程分离: Compositor.cs:19-22 ",[21,3738,3739],{},"Compositor"," 类",[44,3742,3743],{},"管理 UI 线程","和",[44,3746,3747],{},"渲染线程","之间的通信。 Compositor.cs:25-27",[98,3750,3752],{"id":3751},"ui线程","UI线程",[10,3754,3755,3756,3759],{},"它负责处理布局、动画和用户交互。然后通过MediaContext来调度渲染，需要重新绘制的时候会调",[21,3757,3758],{},"InvalidateVisual()"," 标记为脏: Visual.cs:377-380 。",[10,3761,3762,3763,3766],{},"UI线程的更改会被打包成",[21,3764,3765],{},"CompositionBatch"," 发送到渲染线程",[14,3768,3770],{"className":16,"code":3769,"language":18,"meta":19,"style":19}," private void RenderCore()\n    {\n        var now = _time.Elapsed;\n        if (!_animationsAreWaitingForComposition)\n            _clock.Pulse(now);\n\n        \u002F\u002F Since new animations could be started during the layout and can affect layout\u002Frender\n        \u002F\u002F We are doing several iterations when it happens\n        for (var c = 0; c \u003C 10; c++)\n        {\n            FireInvokeOnRenderCallbacks();\n            \n            if (_clock.HasNewSubscriptions)\n            {\n                _clock.PulseNewSubscriptions();\n                continue;\n            }\n\n            break;\n        }\n        \n        if (_requestedCommits.Count > 0 || _clock.HasSubscriptions)\n        {\n            _animationsAreWaitingForComposition = CommitCompositorsWithThrottling();\n            if (!_animationsAreWaitingForComposition && _clock.HasSubscriptions) \n                _animationsTimer.Start();\n        }\n    }\n",[21,3771,3772,3777,3781,3786,3791,3796,3800,3805,3810,3815,3819,3824,3828,3833,3837,3842,3847,3851,3855,3860,3864,3868,3873,3877,3882,3887,3892,3896],{"__ignoreMap":19},[24,3773,3774],{"class":26,"line":27},[24,3775,3776],{}," private void RenderCore()\n",[24,3778,3779],{"class":26,"line":129},[24,3780,272],{},[24,3782,3783],{"class":26,"line":135},[24,3784,3785],{},"        var now = _time.Elapsed;\n",[24,3787,3788],{"class":26,"line":141},[24,3789,3790],{},"        if (!_animationsAreWaitingForComposition)\n",[24,3792,3793],{"class":26,"line":147},[24,3794,3795],{},"            _clock.Pulse(now);\n",[24,3797,3798],{"class":26,"line":153},[24,3799,936],{"emptyLinePlaceholder":935},[24,3801,3802],{"class":26,"line":159},[24,3803,3804],{},"        \u002F\u002F Since new animations could be started during the layout and can affect layout\u002Frender\n",[24,3806,3807],{"class":26,"line":165},[24,3808,3809],{},"        \u002F\u002F We are doing several iterations when it happens\n",[24,3811,3812],{"class":26,"line":170},[24,3813,3814],{},"        for (var c = 0; c \u003C 10; c++)\n",[24,3816,3817],{"class":26,"line":176},[24,3818,325],{},[24,3820,3821],{"class":26,"line":181},[24,3822,3823],{},"            FireInvokeOnRenderCallbacks();\n",[24,3825,3826],{"class":26,"line":186},[24,3827,785],{},[24,3829,3830],{"class":26,"line":192},[24,3831,3832],{},"            if (_clock.HasNewSubscriptions)\n",[24,3834,3835],{"class":26,"line":198},[24,3836,1265],{},[24,3838,3839],{"class":26,"line":204},[24,3840,3841],{},"                _clock.PulseNewSubscriptions();\n",[24,3843,3844],{"class":26,"line":328},[24,3845,3846],{},"                continue;\n",[24,3848,3849],{"class":26,"line":334},[24,3850,1303],{},[24,3852,3853],{"class":26,"line":340},[24,3854,936],{"emptyLinePlaceholder":935},[24,3856,3857],{"class":26,"line":346},[24,3858,3859],{},"            break;\n",[24,3861,3862],{"class":26,"line":351},[24,3863,343],{},[24,3865,3866],{"class":26,"line":356},[24,3867,661],{},[24,3869,3870],{"class":26,"line":362},[24,3871,3872],{},"        if (_requestedCommits.Count > 0 || _clock.HasSubscriptions)\n",[24,3874,3875],{"class":26,"line":367},[24,3876,325],{},[24,3878,3879],{"class":26,"line":373},[24,3880,3881],{},"            _animationsAreWaitingForComposition = CommitCompositorsWithThrottling();\n",[24,3883,3884],{"class":26,"line":379},[24,3885,3886],{},"            if (!_animationsAreWaitingForComposition && _clock.HasSubscriptions) \n",[24,3888,3889],{"class":26,"line":384},[24,3890,3891],{},"                _animationsTimer.Start();\n",[24,3893,3894],{"class":26,"line":389},[24,3895,343],{},[24,3897,3898],{"class":26,"line":395},[24,3899,297],{},[10,3901,3902,3903,3906,3907,3406],{},"渲染线程通过 ",[21,3904,3905],{},"ServerCompositor"," 处理批次并",[44,3908,3909],{},"执行实际渲染",[14,3911,3913],{"className":16,"code":3912,"language":18,"meta":19,"style":19},"      private void RenderCore(bool catchExceptions)\n        {\n            UpdateServerTime();\n            ApplyPendingBatches();\n            NotifyBatchesProcessed();\n\n            Animations.Process();\n\n\n            ApplyEnqueuedRenderResourceChanges();\n            \n            try\n            {\n                if(!RenderInterface.IsReady)\n                    return;\n                RenderInterface.EnsureValidBackendContext();\n                ExecuteServerJobs(_receivedJobQueue);\n                foreach (var t in _activeTargets)\n                    t.Render();\n                ExecuteServerJobs(_receivedPostTargetJobQueue);\n            }\n            catch (Exception e) when(RT_OnContextLostExceptionFilterObserver(e) && catchExceptions)\n            {\n                Logger.TryGet(LogEventLevel.Error, LogArea.Visual)?.Log(this, \"Exception when rendering: {Error}\", e);\n            }\n        }\n",[21,3914,3915,3920,3924,3929,3934,3939,3943,3948,3952,3956,3961,3965,3970,3974,3979,3984,3989,3994,3999,4004,4009,4013,4018,4022,4027,4031],{"__ignoreMap":19},[24,3916,3917],{"class":26,"line":27},[24,3918,3919],{},"      private void RenderCore(bool catchExceptions)\n",[24,3921,3922],{"class":26,"line":129},[24,3923,325],{},[24,3925,3926],{"class":26,"line":135},[24,3927,3928],{},"            UpdateServerTime();\n",[24,3930,3931],{"class":26,"line":141},[24,3932,3933],{},"            ApplyPendingBatches();\n",[24,3935,3936],{"class":26,"line":147},[24,3937,3938],{},"            NotifyBatchesProcessed();\n",[24,3940,3941],{"class":26,"line":153},[24,3942,936],{"emptyLinePlaceholder":935},[24,3944,3945],{"class":26,"line":159},[24,3946,3947],{},"            Animations.Process();\n",[24,3949,3950],{"class":26,"line":165},[24,3951,936],{"emptyLinePlaceholder":935},[24,3953,3954],{"class":26,"line":170},[24,3955,936],{"emptyLinePlaceholder":935},[24,3957,3958],{"class":26,"line":176},[24,3959,3960],{},"            ApplyEnqueuedRenderResourceChanges();\n",[24,3962,3963],{"class":26,"line":181},[24,3964,785],{},[24,3966,3967],{"class":26,"line":186},[24,3968,3969],{},"            try\n",[24,3971,3972],{"class":26,"line":192},[24,3973,1265],{},[24,3975,3976],{"class":26,"line":198},[24,3977,3978],{},"                if(!RenderInterface.IsReady)\n",[24,3980,3981],{"class":26,"line":204},[24,3982,3983],{},"                    return;\n",[24,3985,3986],{"class":26,"line":328},[24,3987,3988],{},"                RenderInterface.EnsureValidBackendContext();\n",[24,3990,3991],{"class":26,"line":334},[24,3992,3993],{},"                ExecuteServerJobs(_receivedJobQueue);\n",[24,3995,3996],{"class":26,"line":340},[24,3997,3998],{},"                foreach (var t in _activeTargets)\n",[24,4000,4001],{"class":26,"line":346},[24,4002,4003],{},"                    t.Render();\n",[24,4005,4006],{"class":26,"line":351},[24,4007,4008],{},"                ExecuteServerJobs(_receivedPostTargetJobQueue);\n",[24,4010,4011],{"class":26,"line":356},[24,4012,1303],{},[24,4014,4015],{"class":26,"line":362},[24,4016,4017],{},"            catch (Exception e) when(RT_OnContextLostExceptionFilterObserver(e) && catchExceptions)\n",[24,4019,4020],{"class":26,"line":367},[24,4021,1265],{},[24,4023,4024],{"class":26,"line":373},[24,4025,4026],{},"                Logger.TryGet(LogEventLevel.Error, LogArea.Visual)?.Log(this, \"Exception when rendering: {Error}\", e);\n",[24,4028,4029],{"class":26,"line":379},[24,4030,1303],{},[24,4032,4033],{"class":26,"line":384},[24,4034,343],{},[10,4036,4037],{},"渲染流程包括:",[38,4039,4040,4046,4052],{},[41,4041,4042,4043,2401],{},"应用待处理的批次(",[21,4044,4045],{},"ApplyPendingBatches",[41,4047,4048,4049,2401],{},"处理动画(",[21,4050,4051],{},"Animations.Process",[41,4053,4054,4055,4058,4059,2401],{},"遍历",[44,4056,4057],{},"所有活动的渲染目标并渲染","(",[21,4060,4061],{},"t.Render()",[10,4063,4064,4065,4068,4069,4072],{},"每个可视化组件通过 ",[21,4066,4067],{},"ServerCompositionVisual"," 在",[44,4070,4071],{},"渲染线程上执行绘制",": ServerCompositionVisual.cs:61-94",[10,4074,4075,4076,4079,4080,4083],{},"绘制操作最终通过 ",[21,4077,4078],{},"IDrawingContextImpl"," 接口",[44,4081,4082],{},"调用底层渲染后端",": IDrawingContextImpl.cs:14-25",[14,4085,4087],{"className":16,"code":4086,"language":18,"meta":19,"style":19},"    \u002F\u002F\u002F Defines the interface through which drawing occurs.\n    \u002F\u002F\u002F \u003C\u002Fsummary>\n    [Unstable]\n    public interface IDrawingContextImpl : IDisposable\n    {\n        \u002F\u002F\u002F \u003Csummary>\n        \u002F\u002F\u002F Gets or sets the current transform of the drawing context.\n        \u002F\u002F\u002F \u003C\u002Fsummary>\n        Matrix Transform { get; set; }\n\n        \u002F\u002F\u002F \u003Csummary>\n        \u002F\u002F\u002F Clears the render target to the specified color.\n        \u002F\u002F\u002F \u003C\u002Fsummary>\n        \u002F\u002F\u002F \u003Cparam name=\"color\">The color.\u003C\u002Fparam>\n        void Clear(Color color);\n",[21,4088,4089,4094,4098,4103,4108,4112,4116,4121,4125,4130,4134,4138,4143,4147,4152],{"__ignoreMap":19},[24,4090,4091],{"class":26,"line":27},[24,4092,4093],{},"    \u002F\u002F\u002F Defines the interface through which drawing occurs.\n",[24,4095,4096],{"class":26,"line":129},[24,4097,960],{},[24,4099,4100],{"class":26,"line":135},[24,4101,4102],{},"    [Unstable]\n",[24,4104,4105],{"class":26,"line":141},[24,4106,4107],{},"    public interface IDrawingContextImpl : IDisposable\n",[24,4109,4110],{"class":26,"line":147},[24,4111,272],{},[24,4113,4114],{"class":26,"line":153},[24,4115,979],{},[24,4117,4118],{"class":26,"line":159},[24,4119,4120],{},"        \u002F\u002F\u002F Gets or sets the current transform of the drawing context.\n",[24,4122,4123],{"class":26,"line":165},[24,4124,989],{},[24,4126,4127],{"class":26,"line":170},[24,4128,4129],{},"        Matrix Transform { get; set; }\n",[24,4131,4132],{"class":26,"line":176},[24,4133,936],{"emptyLinePlaceholder":935},[24,4135,4136],{"class":26,"line":181},[24,4137,979],{},[24,4139,4140],{"class":26,"line":186},[24,4141,4142],{},"        \u002F\u002F\u002F Clears the render target to the specified color.\n",[24,4144,4145],{"class":26,"line":192},[24,4146,989],{},[24,4148,4149],{"class":26,"line":198},[24,4150,4151],{},"        \u002F\u002F\u002F \u003Cparam name=\"color\">The color.\u003C\u002Fparam>\n",[24,4153,4154],{"class":26,"line":204},[24,4155,4156],{},"        void Clear(Color color);\n",[98,4158,4159],{"id":4159},"线程模式选项",[10,4161,4162],{},"Avalonia 支持两种渲染模式:",[98,4164,4166],{"id":4165},"后台渲染线程默认","后台渲染线程(默认)",[10,4168,4169,4170,4173],{},"大多数平台使用独立的渲染线程,通过 ",[21,4171,4172],{},"IRenderLoop"," 驱动。 Compositor.cs:25-26",[98,4175,4177],{"id":4176},"ui-线程渲染","UI 线程渲染",[10,4179,4180,4181,4184],{},"某些场景下可以",[44,4182,4183],{},"配置在 UI 线程上渲染",": Win32PlatformOptions.cs:145-150",[14,4186,4188],{"className":16,"code":4187,"language":18,"meta":19,"style":19},"    \u002F\u002F\u002F \u003Csummary>\n    \u002F\u002F\u002F Render directly on the UI thread instead of using a dedicated render thread.\n    \u002F\u002F\u002F Only applicable if \u003Csee cref=\"CompositionMode\"\u002F> is set to \u003Csee cref=\"Win32CompositionMode.RedirectionSurface\"\u002F>.\n    \u002F\u002F\u002F This setting is only recommended for interop with systems that must render on the UI thread, such as WPF.\n    \u002F\u002F\u002F This setting is false by default.\n    \u002F\u002F\u002F \u003C\u002Fsummary>\n    public bool ShouldRenderOnUIThread { get; set; }\n",[21,4189,4190,4194,4199,4204,4209,4214,4218],{"__ignoreMap":19},[24,4191,4192],{"class":26,"line":27},[24,4193,950],{},[24,4195,4196],{"class":26,"line":129},[24,4197,4198],{},"    \u002F\u002F\u002F Render directly on the UI thread instead of using a dedicated render thread.\n",[24,4200,4201],{"class":26,"line":135},[24,4202,4203],{},"    \u002F\u002F\u002F Only applicable if \u003Csee cref=\"CompositionMode\"\u002F> is set to \u003Csee cref=\"Win32CompositionMode.RedirectionSurface\"\u002F>.\n",[24,4205,4206],{"class":26,"line":141},[24,4207,4208],{},"    \u002F\u002F\u002F This setting is only recommended for interop with systems that must render on the UI thread, such as WPF.\n",[24,4210,4211],{"class":26,"line":147},[24,4212,4213],{},"    \u002F\u002F\u002F This setting is false by default.\n",[24,4215,4216],{"class":26,"line":153},[24,4217,960],{},[24,4219,4220],{"class":26,"line":159},[24,4221,4222],{},"    public bool ShouldRenderOnUIThread { get; set; }\n",[10,4224,4225,4226,4229],{},"这种模式下,",[44,4227,4228],{},"渲染会在 UI 线程上同步执行",": ServerCompositor.cs:172-191",[14,4231,4233],{"className":16,"code":4232,"language":18,"meta":19,"style":19},"       public void Render(bool catchExceptions)\n        {\n            if (Dispatcher.UIThread.CheckAccess())\n            {\n                if (_uiThreadIsInsideRender)\n                    throw new InvalidOperationException(\"Reentrancy is not supported\");\n                _uiThreadIsInsideRender = true;\n                try\n                {\n                    using (Dispatcher.UIThread.DisableProcessing()) \n                        RenderReentrancySafe(catchExceptions);\n                }\n                finally\n                {\n                    _uiThreadIsInsideRender = false;\n                }\n            }\n            else\n                RenderReentrancySafe(catchExceptions);\n        }\n",[21,4234,4235,4240,4244,4249,4253,4258,4263,4268,4273,4277,4282,4287,4291,4296,4300,4305,4309,4313,4318,4323],{"__ignoreMap":19},[24,4236,4237],{"class":26,"line":27},[24,4238,4239],{},"       public void Render(bool catchExceptions)\n",[24,4241,4242],{"class":26,"line":129},[24,4243,325],{},[24,4245,4246],{"class":26,"line":135},[24,4247,4248],{},"            if (Dispatcher.UIThread.CheckAccess())\n",[24,4250,4251],{"class":26,"line":141},[24,4252,1265],{},[24,4254,4255],{"class":26,"line":147},[24,4256,4257],{},"                if (_uiThreadIsInsideRender)\n",[24,4259,4260],{"class":26,"line":153},[24,4261,4262],{},"                    throw new InvalidOperationException(\"Reentrancy is not supported\");\n",[24,4264,4265],{"class":26,"line":159},[24,4266,4267],{},"                _uiThreadIsInsideRender = true;\n",[24,4269,4270],{"class":26,"line":165},[24,4271,4272],{},"                try\n",[24,4274,4275],{"class":26,"line":170},[24,4276,2143],{},[24,4278,4279],{"class":26,"line":176},[24,4280,4281],{},"                    using (Dispatcher.UIThread.DisableProcessing()) \n",[24,4283,4284],{"class":26,"line":181},[24,4285,4286],{},"                        RenderReentrancySafe(catchExceptions);\n",[24,4288,4289],{"class":26,"line":186},[24,4290,2153],{},[24,4292,4293],{"class":26,"line":192},[24,4294,4295],{},"                finally\n",[24,4297,4298],{"class":26,"line":198},[24,4299,2143],{},[24,4301,4302],{"class":26,"line":204},[24,4303,4304],{},"                    _uiThreadIsInsideRender = false;\n",[24,4306,4307],{"class":26,"line":328},[24,4308,2153],{},[24,4310,4311],{"class":26,"line":334},[24,4312,1303],{},[24,4314,4315],{"class":26,"line":340},[24,4316,4317],{},"            else\n",[24,4319,4320],{"class":26,"line":346},[24,4321,4322],{},"                RenderReentrancySafe(catchExceptions);\n",[24,4324,4325],{"class":26,"line":351},[24,4326,343],{},[10,4328,4329],{},"并且Avalonia也支持在浏览器环境中渲染，可使用Web Worker作为渲染线程。",[10,4331,4332,4335,4336,4339],{},[44,4333,4334],{},"不是所有组件直接绑定到渲染线程","。实际上,组件在 UI 线程上进行布局和状态更新,然后将渲染指令批量发送到渲染线程。渲染线程独立执行绘制操作,这种设计避免了 UI 线程阻塞,提高了性能和响应性。同步等待机制(",[21,4337,4338],{},"SyncWaitCompositorBatch",")用于需要立即渲染的场景,如窗口调整大小。 MediaContext.Compositor.cs:98-114",[10,4341,4342,4343,4346,4347,4350,4351,4354,4355,4358],{},"在Linux平台，有一个 ",[21,4344,4345],{},"ShouldRenderOnUIThread"," 属性 X11Platform.cs:325-329 。其注释明确指出，",[44,4348,4349],{},"启用此选项可以在UI线程上直接渲染","，这在设备",[44,4352,4353],{},"没有多核处理器时可能有用"," X11Platform.cs:325-329 。默认情况下，此设置为 ",[21,4356,4357],{},"false"," X11Platform.cs:325-329 。",[10,4360,4361,4362,4365,4366,4368],{},"虽然但是，直接在UI线程上进行渲染的话这个会导致UI线程阻塞，进而导致无法交互的作用产生。Avalonia框架默认通过将",[44,4363,4364],{},"渲染任务卸载到单独的渲染线程","来避免这种情况 ServerCompositor.cs:14-19 。只有在特定平台选项中明确启用 ",[21,4367,4345],{}," 时，才会发生UI线程渲染，并且通常不推荐这样做，除非有特定的互操作性需求 Win32PlatformOptions.cs:145-150 。",[10,4370,4371,4374,4375,4378,4379,1886,4382,4385,4386,1519,4389,4392,4393,4396,4397,228],{},[21,4372,4373],{},"Dispatcher.UIThread"," 是Avalonia中用于",[44,4376,4377],{},"访问UI线程的单例"," Dispatcher.cs:47-54 。",[21,4380,4381],{},"CheckAccess()",[44,4383,4384],{},"用于判断当前线程是否是UI线程"," Dispatcher.cs:76 。",[21,4387,4388],{},"InvokeAsync",[21,4390,4391],{},"Invoke"," 方法允许在UI线程上执行操作 Dispatcher.Invoke.cs:61-63 Dispatcher.Invoke.cs:65-71 。这些方法",[44,4394,4395],{},"在渲染线程分离的场景下","，可以用于",[44,4398,4399],{},"将UI更新安全地调度回UI线程",[14,4401,4403],{"className":16,"code":4402,"language":18,"meta":19,"style":19},"\u002F\u002F 在UI线程上  \ncompositor.PostServerJob(() =>  \n{  \n    \u002F\u002F 这段代码将在渲染线程上执行  \n    Console.WriteLine(\"Hello from render thread!\");  \n});\n",[21,4404,4405,4410,4415,4420,4425,4430],{"__ignoreMap":19},[24,4406,4407],{"class":26,"line":27},[24,4408,4409],{},"\u002F\u002F 在UI线程上  \n",[24,4411,4412],{"class":26,"line":129},[24,4413,4414],{},"compositor.PostServerJob(() =>  \n",[24,4416,4417],{"class":26,"line":135},[24,4418,4419],{},"{  \n",[24,4421,4422],{"class":26,"line":141},[24,4423,4424],{},"    \u002F\u002F 这段代码将在渲染线程上执行  \n",[24,4426,4427],{"class":26,"line":147},[24,4428,4429],{},"    Console.WriteLine(\"Hello from render thread!\");  \n",[24,4431,4432],{"class":26,"line":153},[24,4433,4434],{},"});\n",[4436,4437,4438],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":19,"searchDepth":129,"depth":129,"links":4440},[4441,4442,4443,4444,4445,4446,4453],{"id":100,"depth":135,"text":101},{"id":919,"depth":135,"text":920},{"id":1156,"depth":135,"text":1157},{"id":1473,"depth":135,"text":1474},{"id":1555,"depth":135,"text":1556},{"id":1789,"depth":129,"text":1789,"children":4447},[4448,4449,4450,4451,4452],{"id":1792,"depth":135,"text":1793},{"id":3044,"depth":135,"text":3045},{"id":3195,"depth":135,"text":3051},{"id":3324,"depth":135,"text":3062},{"id":3448,"depth":135,"text":3449},{"id":3729,"depth":129,"text":3729,"children":4454},[4455,4456,4457,4458],{"id":3751,"depth":135,"text":3752},{"id":4159,"depth":135,"text":4159},{"id":4165,"depth":135,"text":4166},{"id":4176,"depth":135,"text":4177},"md",{},"\u002Fcsharp\u002Favalonia\u002Favalonia的渲染系统",{"description":12},"csharp\u002FAvalonia\u002FAvalonia的渲染系统","stc2wU1Zg8UzOy4bV0vy-qoJB6G5IoP5e2FeB8plR-c",1791042464741]