[{"data":1,"prerenderedAt":5020},["ShallowReactive",2],{"page-\u002Fcsharp\u002Fwpf\u002F基于propertychangedtrigger-(属性变更触发器)驱动的显式动画":3},{"id":4,"title":5,"body":6,"description":65,"extension":5014,"meta":5015,"navigation":302,"path":5016,"seo":5017,"stem":5018,"__hash__":5019},"content\u002Fcsharp\u002FWPF\u002F基于PropertyChangedTrigger (属性变更触发器)驱动的显式动画.md","基于 PropertyChangedTrigger (属性变更触发器) 驱动的显式动画",{"type":7,"value":8,"toc":4946},"minimark",[9,13,33,36,41,46,56,59,111,114,189,191,195,198,206,209,257,259,263,267,271,355,358,629,657,660,871,912,915,921,988,1006,1009,1088,1116,1118,1122,1125,1240,1258,1262,1467,1469,1473,1698,1700,1704,1708,1718,1722,1731,1734,1753,1757,1806,1810,1816,1822,1859,1862,1907,1910,1915,1924,1959,1964,1973,1989,1993,1997,2012,2016,2031,2035,2050,2054,2069,2072,2148,2152,2156,2182,2186,2191,2238,2243,2287,2308,2311,2414,2417,2421,2446,2450,2453,2457,2468,2471,2475,2485,2489,2498,2504,2508,2513,2555,2557,2561,2565,2654,2658,2753,2787,2791,2797,2799,2803,2807,2814,2817,2897,2901,2904,2906,2914,2919,2930,2933,2939,2941,2945,2949,2964,2968,2974,2978,3027,3031,3084,3088,3134,3137,3261,3265,3268,3348,3352,3355,3396,3398,3402,3406,3429,3471,3475,3478,3562,3566,3587,3591,3620,3622,3626,3630,3633,3677,3681,3684,3764,3768,3771,3816,3818,3821,3825,3861,3865,3880,3884,3887,3889,3893,3897,4366,4368,4372,4375,4408,4410,4424,4427,4455,4457,4461,4484,4486,4490,4942],[10,11,5],"h1",{"id":12},"基于-propertychangedtrigger-属性变更触发器-驱动的显式动画",[14,15,16],"blockquote",{},[17,18,19,23,24,28,29,32],"p",{},[20,21,22],"span",{},"!abstract"," 概述\n本文档详细讲解如何使用 ",[25,26,27],"strong",{},"PropertyChangedTrigger","（属性变更触发器）和自定义 ",[25,30,31],{},"Action"," 实现响应式动画。当目标属性（如宽度、高度）改变时，自动计算与当前值的差值，并执行平滑的二阶缓动动画。",[34,35],"hr",{},[37,38,40],"h2",{"id":39},"核心概念","📋 核心概念",[42,43,45],"h3",{"id":44},"什么是-propertychangedtrigger","什么是 PropertyChangedTrigger？",[17,47,48,51,52,55],{},[49,50,27],"code",{}," 是 Microsoft.Xaml.Behaviors.Wpf 库提供的一个触发器，它",[25,53,54],{},"监听数据绑定属性的变化","，当属性值改变时自动触发关联的 Action。",[42,57,58],{"id":58},"工作流程",[60,61,66],"pre",{"className":62,"code":63,"language":64,"meta":65,"style":65},"language-mermaid shiki shiki-themes github-light github-dark","graph TD\n    A[属性值改变] --> B[PropertyChangedTrigger 检测到变化]\n    B --> C[触发自定义 Action]\n    C --> D[计算当前值与目标值的差值]\n    D --> E[创建动画 Storyboard]\n    E --> F[执行二阶缓动动画]\n    F --> G[平滑过渡到目标值]\n","mermaid","",[49,67,68,75,81,87,93,99,105],{"__ignoreMap":65},[20,69,72],{"class":70,"line":71},"line",1,[20,73,74],{},"graph TD\n",[20,76,78],{"class":70,"line":77},2,[20,79,80],{},"    A[属性值改变] --> B[PropertyChangedTrigger 检测到变化]\n",[20,82,84],{"class":70,"line":83},3,[20,85,86],{},"    B --> C[触发自定义 Action]\n",[20,88,90],{"class":70,"line":89},4,[20,91,92],{},"    C --> D[计算当前值与目标值的差值]\n",[20,94,96],{"class":70,"line":95},5,[20,97,98],{},"    D --> E[创建动画 Storyboard]\n",[20,100,102],{"class":70,"line":101},6,[20,103,104],{},"    E --> F[执行二阶缓动动画]\n",[20,106,108],{"class":70,"line":107},7,[20,109,110],{},"    F --> G[平滑过渡到目标值]\n",[42,112,113],{"id":113},"与传统动画的区别",[115,116,117,133],"table",{},[118,119,120],"thead",{},[121,122,123,127,130],"tr",{},[124,125,126],"th",{},"特性",[124,128,129],{},"传统动画",[124,131,132],{},"PropertyChangedTrigger 驱动动画",[134,135,136,150,163,176],"tbody",{},[121,137,138,144,147],{},[139,140,141],"td",{},[25,142,143],{},"触发方式",[139,145,146],{},"手动触发（事件、代码）",[139,148,149],{},"自动触发（属性变化）",[121,151,152,157,160],{},[139,153,154],{},[25,155,156],{},"响应性",[139,158,159],{},"需要手动管理",[139,161,162],{},"完全响应式",[121,164,165,170,173],{},[139,166,167],{},[25,168,169],{},"维护性",[139,171,172],{},"需要同步状态",[139,174,175],{},"声明式，易于维护",[121,177,178,183,186],{},[139,179,180],{},[25,181,182],{},"适用场景",[139,184,185],{},"一次性动画",[139,187,188],{},"数据驱动的动画",[34,190],{},[37,192,194],{"id":193},"️-架构设计","🏗️ 架构设计",[42,196,197],{"id":197},"组件结构",[60,199,204],{"className":200,"code":202,"language":203},[201],"language-text","PropertyChangedTrigger 驱动动画系统\n├── PropertyChangedTrigger（触发器）\n│   └── 监听属性变化\n├── 自定义 Action（动作）\n│   ├── AnimateToTargetSizeAction\n│   ├── 计算差值逻辑\n│   └── 创建动画\n└── 目标元素（UI 控件）\n    └── 执行动画\n","text",[49,205,202],{"__ignoreMap":65},[42,207,208],{"id":208},"关键组件说明",[210,211,212,225,244],"ol",{},[213,214,215,217],"li",{},[25,216,27],{},[218,219,220,222],"ul",{},[213,221,54],{},[213,223,224],{},"当属性值改变时触发 Action",[213,226,227,230],{},[25,228,229],{},"自定义 Action（TriggerAction）",[218,231,232,238,241],{},[213,233,234,235],{},"继承自 ",[49,236,237],{},"TriggerAction\u003CFrameworkElement>",[213,239,240],{},"实现动画逻辑",[213,242,243],{},"计算当前值与目标值的差值",[213,245,246,249],{},[25,247,248],{},"动画目标元素",[218,250,251,254],{},[213,252,253],{},"要动画化的 UI 元素",[213,255,256],{},"通过依赖属性绑定目标值",[34,258],{},[37,260,262],{"id":261},"实现详解","💻 实现详解",[42,264,266],{"id":265},"_1-创建自定义-action","1. 创建自定义 Action",[268,269,270],"h4",{"id":270},"类定义",[60,272,276],{"className":273,"code":274,"language":275,"meta":65,"style":65},"language-csharp shiki shiki-themes github-light github-dark","using System;\nusing System.Windows;\nusing System.Windows.Media.Animation;\nusing Microsoft.Xaml.Behaviors;\n\nnamespace FluentScrollviewerSolution;\n\n\u002F\u002F\u002F \u003Csummary>\n\u002F\u002F\u002F 自定义 Action：当目标宽度\u002F高度改变时，自动计算与当前值的差值，并执行二阶缓动动画\n\u002F\u002F\u002F \u003C\u002Fsummary>\npublic class AnimateToTargetSizeAction : TriggerAction\u003CFrameworkElement>\n{\n    \u002F\u002F 实现细节见下文\n}\n","csharp",[49,277,278,283,288,293,298,304,309,313,319,325,331,337,343,349],{"__ignoreMap":65},[20,279,280],{"class":70,"line":71},[20,281,282],{},"using System;\n",[20,284,285],{"class":70,"line":77},[20,286,287],{},"using System.Windows;\n",[20,289,290],{"class":70,"line":83},[20,291,292],{},"using System.Windows.Media.Animation;\n",[20,294,295],{"class":70,"line":89},[20,296,297],{},"using Microsoft.Xaml.Behaviors;\n",[20,299,300],{"class":70,"line":95},[20,301,303],{"emptyLinePlaceholder":302},true,"\n",[20,305,306],{"class":70,"line":101},[20,307,308],{},"namespace FluentScrollviewerSolution;\n",[20,310,311],{"class":70,"line":107},[20,312,303],{"emptyLinePlaceholder":302},[20,314,316],{"class":70,"line":315},8,[20,317,318],{},"\u002F\u002F\u002F \u003Csummary>\n",[20,320,322],{"class":70,"line":321},9,[20,323,324],{},"\u002F\u002F\u002F 自定义 Action：当目标宽度\u002F高度改变时，自动计算与当前值的差值，并执行二阶缓动动画\n",[20,326,328],{"class":70,"line":327},10,[20,329,330],{},"\u002F\u002F\u002F \u003C\u002Fsummary>\n",[20,332,334],{"class":70,"line":333},11,[20,335,336],{},"public class AnimateToTargetSizeAction : TriggerAction\u003CFrameworkElement>\n",[20,338,340],{"class":70,"line":339},12,[20,341,342],{},"{\n",[20,344,346],{"class":70,"line":345},13,[20,347,348],{},"    \u002F\u002F 实现细节见下文\n",[20,350,352],{"class":70,"line":351},14,[20,353,354],{},"}\n",[268,356,357],{"id":357},"依赖属性定义",[60,359,361],{"className":273,"code":360,"language":275,"meta":65,"style":65},"\u002F\u002F 目标对象（要动画化的元素）\npublic FrameworkElement TargetObject\n{\n    get => (FrameworkElement)GetValue(TargetObjectProperty);\n    set => SetValue(TargetObjectProperty, value);\n}\n\npublic static readonly DependencyProperty TargetObjectProperty =\n    DependencyProperty.Register(nameof(TargetObject), typeof(FrameworkElement), \n        typeof(AnimateToTargetSizeAction), new PropertyMetadata(null));\n\n\u002F\u002F 目标宽度\npublic double TargetWidth\n{\n    get => (double)GetValue(TargetWidthProperty);\n    set => SetValue(TargetWidthProperty, value);\n}\n\npublic static readonly DependencyProperty TargetWidthProperty =\n    DependencyProperty.Register(nameof(TargetWidth), typeof(double), \n        typeof(AnimateToTargetSizeAction), new PropertyMetadata(double.NaN));\n\n\u002F\u002F 目标高度\npublic double TargetHeight\n{\n    get => (double)GetValue(TargetHeightProperty);\n    set => SetValue(TargetHeightProperty, value);\n}\n\npublic static readonly DependencyProperty TargetHeightProperty =\n    DependencyProperty.Register(nameof(TargetHeight), typeof(double), \n        typeof(AnimateToTargetSizeAction), new PropertyMetadata(double.NaN));\n\n    \u002F\u002F 动画持续时间\n    public Duration Duration { get; set; } = new Duration(TimeSpan.FromSeconds(0.2));\n    \n    \u002F\u002F 缓动函数（默认使用二阶缓动 CubicEase）\n    public IEasingFunction EasingFunction\n    {\n        get => (IEasingFunction)GetValue(EasingFunctionProperty);\n        set => SetValue(EasingFunctionProperty, value);\n    }\n    \n    public static readonly DependencyProperty EasingFunctionProperty =\n        DependencyProperty.Register(\n            nameof(EasingFunction), \n            typeof(IEasingFunction), \n            typeof(AnimateToTargetSizeAction), \n            new PropertyMetadata(new CubicEase { EasingMode = EasingMode.EaseInOut }));\n",[49,362,363,368,373,377,382,387,391,395,400,405,410,414,419,424,428,434,440,445,450,456,462,468,473,479,485,490,496,502,507,512,518,524,529,534,540,546,552,558,564,570,576,582,588,593,599,605,611,617,623],{"__ignoreMap":65},[20,364,365],{"class":70,"line":71},[20,366,367],{},"\u002F\u002F 目标对象（要动画化的元素）\n",[20,369,370],{"class":70,"line":77},[20,371,372],{},"public FrameworkElement TargetObject\n",[20,374,375],{"class":70,"line":83},[20,376,342],{},[20,378,379],{"class":70,"line":89},[20,380,381],{},"    get => (FrameworkElement)GetValue(TargetObjectProperty);\n",[20,383,384],{"class":70,"line":95},[20,385,386],{},"    set => SetValue(TargetObjectProperty, value);\n",[20,388,389],{"class":70,"line":101},[20,390,354],{},[20,392,393],{"class":70,"line":107},[20,394,303],{"emptyLinePlaceholder":302},[20,396,397],{"class":70,"line":315},[20,398,399],{},"public static readonly DependencyProperty TargetObjectProperty =\n",[20,401,402],{"class":70,"line":321},[20,403,404],{},"    DependencyProperty.Register(nameof(TargetObject), typeof(FrameworkElement), \n",[20,406,407],{"class":70,"line":327},[20,408,409],{},"        typeof(AnimateToTargetSizeAction), new PropertyMetadata(null));\n",[20,411,412],{"class":70,"line":333},[20,413,303],{"emptyLinePlaceholder":302},[20,415,416],{"class":70,"line":339},[20,417,418],{},"\u002F\u002F 目标宽度\n",[20,420,421],{"class":70,"line":345},[20,422,423],{},"public double TargetWidth\n",[20,425,426],{"class":70,"line":351},[20,427,342],{},[20,429,431],{"class":70,"line":430},15,[20,432,433],{},"    get => (double)GetValue(TargetWidthProperty);\n",[20,435,437],{"class":70,"line":436},16,[20,438,439],{},"    set => SetValue(TargetWidthProperty, value);\n",[20,441,443],{"class":70,"line":442},17,[20,444,354],{},[20,446,448],{"class":70,"line":447},18,[20,449,303],{"emptyLinePlaceholder":302},[20,451,453],{"class":70,"line":452},19,[20,454,455],{},"public static readonly DependencyProperty TargetWidthProperty =\n",[20,457,459],{"class":70,"line":458},20,[20,460,461],{},"    DependencyProperty.Register(nameof(TargetWidth), typeof(double), \n",[20,463,465],{"class":70,"line":464},21,[20,466,467],{},"        typeof(AnimateToTargetSizeAction), new PropertyMetadata(double.NaN));\n",[20,469,471],{"class":70,"line":470},22,[20,472,303],{"emptyLinePlaceholder":302},[20,474,476],{"class":70,"line":475},23,[20,477,478],{},"\u002F\u002F 目标高度\n",[20,480,482],{"class":70,"line":481},24,[20,483,484],{},"public double TargetHeight\n",[20,486,488],{"class":70,"line":487},25,[20,489,342],{},[20,491,493],{"class":70,"line":492},26,[20,494,495],{},"    get => (double)GetValue(TargetHeightProperty);\n",[20,497,499],{"class":70,"line":498},27,[20,500,501],{},"    set => SetValue(TargetHeightProperty, value);\n",[20,503,505],{"class":70,"line":504},28,[20,506,354],{},[20,508,510],{"class":70,"line":509},29,[20,511,303],{"emptyLinePlaceholder":302},[20,513,515],{"class":70,"line":514},30,[20,516,517],{},"public static readonly DependencyProperty TargetHeightProperty =\n",[20,519,521],{"class":70,"line":520},31,[20,522,523],{},"    DependencyProperty.Register(nameof(TargetHeight), typeof(double), \n",[20,525,527],{"class":70,"line":526},32,[20,528,467],{},[20,530,532],{"class":70,"line":531},33,[20,533,303],{"emptyLinePlaceholder":302},[20,535,537],{"class":70,"line":536},34,[20,538,539],{},"    \u002F\u002F 动画持续时间\n",[20,541,543],{"class":70,"line":542},35,[20,544,545],{},"    public Duration Duration { get; set; } = new Duration(TimeSpan.FromSeconds(0.2));\n",[20,547,549],{"class":70,"line":548},36,[20,550,551],{},"    \n",[20,553,555],{"class":70,"line":554},37,[20,556,557],{},"    \u002F\u002F 缓动函数（默认使用二阶缓动 CubicEase）\n",[20,559,561],{"class":70,"line":560},38,[20,562,563],{},"    public IEasingFunction EasingFunction\n",[20,565,567],{"class":70,"line":566},39,[20,568,569],{},"    {\n",[20,571,573],{"class":70,"line":572},40,[20,574,575],{},"        get => (IEasingFunction)GetValue(EasingFunctionProperty);\n",[20,577,579],{"class":70,"line":578},41,[20,580,581],{},"        set => SetValue(EasingFunctionProperty, value);\n",[20,583,585],{"class":70,"line":584},42,[20,586,587],{},"    }\n",[20,589,591],{"class":70,"line":590},43,[20,592,551],{},[20,594,596],{"class":70,"line":595},44,[20,597,598],{},"    public static readonly DependencyProperty EasingFunctionProperty =\n",[20,600,602],{"class":70,"line":601},45,[20,603,604],{},"        DependencyProperty.Register(\n",[20,606,608],{"class":70,"line":607},46,[20,609,610],{},"            nameof(EasingFunction), \n",[20,612,614],{"class":70,"line":613},47,[20,615,616],{},"            typeof(IEasingFunction), \n",[20,618,620],{"class":70,"line":619},48,[20,621,622],{},"            typeof(AnimateToTargetSizeAction), \n",[20,624,626],{"class":70,"line":625},49,[20,627,628],{},"            new PropertyMetadata(new CubicEase { EasingMode = EasingMode.EaseInOut }));\n",[14,630,631,637],{},[17,632,633,636],{},[20,634,635],{},"!tip"," 为什么使用依赖属性？",[218,638,639,642,645,648,651],{},[213,640,641],{},"支持数据绑定",[213,643,644],{},"支持样式和模板",[213,646,647],{},"支持动画",[213,649,650],{},"性能优化（值缓存）",[213,652,653,656],{},[25,654,655],{},"EasingFunction","：提供默认值（CubicEase），可在 XAML 中自定义",[268,658,659],{"id":659},"核心动画逻辑",[60,661,663],{"className":273,"code":662,"language":275,"meta":65,"style":65},"protected override void Invoke(object parameter)\n{\n    var target = TargetObject ?? AssociatedObject;\n    if (target == null) return;\n    \n    \u002F\u002F 1. 获取当前宽度和高度\n    double currentWidth = target.Width;\n    double currentHeight = target.Height;\n    \n    \u002F\u002F 2. 如果当前值为 NaN 或 0，使用 ActualWidth\u002FActualHeight\n    if (double.IsNaN(currentWidth) || currentWidth == 0)\n        currentWidth = target.ActualWidth;\n    if (double.IsNaN(currentHeight) || currentHeight == 0)\n        currentHeight = target.ActualHeight;\n    \n    \u002F\u002F 3. 创建动画序列\n    var storyboard = new Storyboard();\n    \n    \u002F\u002F 4. 动画化宽度（如果目标宽度有效且与当前值不同）\n    if (!double.IsNaN(TargetWidth) && Math.Abs(TargetWidth - currentWidth) > 0.01)\n    {\n        var widthAnimation = CreateSecondOrderAnimation(currentWidth, TargetWidth);\n        Storyboard.SetTarget(widthAnimation, target);\n        Storyboard.SetTargetProperty(widthAnimation, \n            new PropertyPath(FrameworkElement.WidthProperty));\n        storyboard.Children.Add(widthAnimation);\n    }\n    \n    \u002F\u002F 5. 动画化高度（如果目标高度有效且与当前值不同）\n    if (!double.IsNaN(TargetHeight) && Math.Abs(TargetHeight - currentHeight) > 0.01)\n    {\n        var heightAnimation = CreateSecondOrderAnimation(currentHeight, TargetHeight);\n        Storyboard.SetTarget(heightAnimation, target);\n        Storyboard.SetTargetProperty(heightAnimation, \n            new PropertyPath(FrameworkElement.HeightProperty));\n        storyboard.Children.Add(heightAnimation);\n    }\n    \n    \u002F\u002F 6. 如果至少有一个动画，则开始播放\n    if (storyboard.Children.Count > 0)\n    {\n        storyboard.Begin(target);\n    }\n}\n",[49,664,665,670,674,679,684,688,693,698,703,707,712,717,722,727,732,736,741,746,750,755,760,764,769,774,779,784,789,793,797,802,807,811,816,821,826,831,836,840,844,849,854,858,863,867],{"__ignoreMap":65},[20,666,667],{"class":70,"line":71},[20,668,669],{},"protected override void Invoke(object parameter)\n",[20,671,672],{"class":70,"line":77},[20,673,342],{},[20,675,676],{"class":70,"line":83},[20,677,678],{},"    var target = TargetObject ?? AssociatedObject;\n",[20,680,681],{"class":70,"line":89},[20,682,683],{},"    if (target == null) return;\n",[20,685,686],{"class":70,"line":95},[20,687,551],{},[20,689,690],{"class":70,"line":101},[20,691,692],{},"    \u002F\u002F 1. 获取当前宽度和高度\n",[20,694,695],{"class":70,"line":107},[20,696,697],{},"    double currentWidth = target.Width;\n",[20,699,700],{"class":70,"line":315},[20,701,702],{},"    double currentHeight = target.Height;\n",[20,704,705],{"class":70,"line":321},[20,706,551],{},[20,708,709],{"class":70,"line":327},[20,710,711],{},"    \u002F\u002F 2. 如果当前值为 NaN 或 0，使用 ActualWidth\u002FActualHeight\n",[20,713,714],{"class":70,"line":333},[20,715,716],{},"    if (double.IsNaN(currentWidth) || currentWidth == 0)\n",[20,718,719],{"class":70,"line":339},[20,720,721],{},"        currentWidth = target.ActualWidth;\n",[20,723,724],{"class":70,"line":345},[20,725,726],{},"    if (double.IsNaN(currentHeight) || currentHeight == 0)\n",[20,728,729],{"class":70,"line":351},[20,730,731],{},"        currentHeight = target.ActualHeight;\n",[20,733,734],{"class":70,"line":430},[20,735,551],{},[20,737,738],{"class":70,"line":436},[20,739,740],{},"    \u002F\u002F 3. 创建动画序列\n",[20,742,743],{"class":70,"line":442},[20,744,745],{},"    var storyboard = new Storyboard();\n",[20,747,748],{"class":70,"line":447},[20,749,551],{},[20,751,752],{"class":70,"line":452},[20,753,754],{},"    \u002F\u002F 4. 动画化宽度（如果目标宽度有效且与当前值不同）\n",[20,756,757],{"class":70,"line":458},[20,758,759],{},"    if (!double.IsNaN(TargetWidth) && Math.Abs(TargetWidth - currentWidth) > 0.01)\n",[20,761,762],{"class":70,"line":464},[20,763,569],{},[20,765,766],{"class":70,"line":470},[20,767,768],{},"        var widthAnimation = CreateSecondOrderAnimation(currentWidth, TargetWidth);\n",[20,770,771],{"class":70,"line":475},[20,772,773],{},"        Storyboard.SetTarget(widthAnimation, target);\n",[20,775,776],{"class":70,"line":481},[20,777,778],{},"        Storyboard.SetTargetProperty(widthAnimation, \n",[20,780,781],{"class":70,"line":487},[20,782,783],{},"            new PropertyPath(FrameworkElement.WidthProperty));\n",[20,785,786],{"class":70,"line":492},[20,787,788],{},"        storyboard.Children.Add(widthAnimation);\n",[20,790,791],{"class":70,"line":498},[20,792,587],{},[20,794,795],{"class":70,"line":504},[20,796,551],{},[20,798,799],{"class":70,"line":509},[20,800,801],{},"    \u002F\u002F 5. 动画化高度（如果目标高度有效且与当前值不同）\n",[20,803,804],{"class":70,"line":514},[20,805,806],{},"    if (!double.IsNaN(TargetHeight) && Math.Abs(TargetHeight - currentHeight) > 0.01)\n",[20,808,809],{"class":70,"line":520},[20,810,569],{},[20,812,813],{"class":70,"line":526},[20,814,815],{},"        var heightAnimation = CreateSecondOrderAnimation(currentHeight, TargetHeight);\n",[20,817,818],{"class":70,"line":531},[20,819,820],{},"        Storyboard.SetTarget(heightAnimation, target);\n",[20,822,823],{"class":70,"line":536},[20,824,825],{},"        Storyboard.SetTargetProperty(heightAnimation, \n",[20,827,828],{"class":70,"line":542},[20,829,830],{},"            new PropertyPath(FrameworkElement.HeightProperty));\n",[20,832,833],{"class":70,"line":548},[20,834,835],{},"        storyboard.Children.Add(heightAnimation);\n",[20,837,838],{"class":70,"line":554},[20,839,587],{},[20,841,842],{"class":70,"line":560},[20,843,551],{},[20,845,846],{"class":70,"line":566},[20,847,848],{},"    \u002F\u002F 6. 如果至少有一个动画，则开始播放\n",[20,850,851],{"class":70,"line":572},[20,852,853],{},"    if (storyboard.Children.Count > 0)\n",[20,855,856],{"class":70,"line":578},[20,857,569],{},[20,859,860],{"class":70,"line":584},[20,861,862],{},"        storyboard.Begin(target);\n",[20,864,865],{"class":70,"line":590},[20,866,587],{},[20,868,869],{"class":70,"line":595},[20,870,354],{},[14,872,873,879],{},[17,874,875,878],{},[20,876,877],{},"!important"," 关键点",[210,880,881,890,896,906],{},[213,882,883,886,887],{},[25,884,885],{},"差值计算","：自动计算 ",[49,888,889],{},"目标值 - 当前值",[213,891,892,895],{},[25,893,894],{},"阈值检查","：只有当差值大于 0.01 时才创建动画，避免不必要的动画",[213,897,898,901,902,905],{},[25,899,900],{},"NaN 处理","：如果当前值为 NaN，使用 ",[49,903,904],{},"ActualWidth\u002FActualHeight"," 作为当前值",[213,907,908,911],{},[25,909,910],{},"条件动画","：只有当目标值有效且与当前值不同时才创建动画",[268,913,914],{"id":914},"缓动函数依赖属性",[17,916,917,918,920],{},"为了提高泛用性，我们添加了一个 ",[49,919,655],{}," 依赖属性，允许自定义缓动函数，默认使用二阶缓动（CubicEase）：",[60,922,924],{"className":273,"code":923,"language":275,"meta":65,"style":65},"\u002F\u002F 缓动函数（默认使用二阶缓动 CubicEase）\npublic IEasingFunction EasingFunction\n{\n    get => (IEasingFunction)GetValue(EasingFunctionProperty);\n    set => SetValue(EasingFunctionProperty, value);\n}\n\npublic static readonly DependencyProperty EasingFunctionProperty =\n    DependencyProperty.Register(\n        nameof(EasingFunction), \n        typeof(IEasingFunction), \n        typeof(AnimateToTargetSizeAction), \n        new PropertyMetadata(new CubicEase { EasingMode = EasingMode.EaseInOut }));\n",[49,925,926,931,936,940,945,950,954,958,963,968,973,978,983],{"__ignoreMap":65},[20,927,928],{"class":70,"line":71},[20,929,930],{},"\u002F\u002F 缓动函数（默认使用二阶缓动 CubicEase）\n",[20,932,933],{"class":70,"line":77},[20,934,935],{},"public IEasingFunction EasingFunction\n",[20,937,938],{"class":70,"line":83},[20,939,342],{},[20,941,942],{"class":70,"line":89},[20,943,944],{},"    get => (IEasingFunction)GetValue(EasingFunctionProperty);\n",[20,946,947],{"class":70,"line":95},[20,948,949],{},"    set => SetValue(EasingFunctionProperty, value);\n",[20,951,952],{"class":70,"line":101},[20,953,354],{},[20,955,956],{"class":70,"line":107},[20,957,303],{"emptyLinePlaceholder":302},[20,959,960],{"class":70,"line":315},[20,961,962],{},"public static readonly DependencyProperty EasingFunctionProperty =\n",[20,964,965],{"class":70,"line":321},[20,966,967],{},"    DependencyProperty.Register(\n",[20,969,970],{"class":70,"line":327},[20,971,972],{},"        nameof(EasingFunction), \n",[20,974,975],{"class":70,"line":333},[20,976,977],{},"        typeof(IEasingFunction), \n",[20,979,980],{"class":70,"line":339},[20,981,982],{},"        typeof(AnimateToTargetSizeAction), \n",[20,984,985],{"class":70,"line":345},[20,986,987],{},"        new PropertyMetadata(new CubicEase { EasingMode = EasingMode.EaseInOut }));\n",[14,989,990,994],{},[17,991,992,636],{},[20,993,635],{},[218,995,996,999,1001,1003],{},[213,997,998],{},"支持在 XAML 中设置",[213,1000,641],{},[213,1002,644],{},[213,1004,1005],{},"提供默认值（CubicEase）",[268,1007,1008],{"id":1008},"动画创建方法",[60,1010,1012],{"className":273,"code":1011,"language":275,"meta":65,"style":65},"\u002F\u002F\u002F \u003Csummary>\n\u002F\u002F\u002F 创建动画（使用指定的缓动函数，默认为二阶缓动 CubicEase）\n\u002F\u002F\u002F \u003C\u002Fsummary>\nprivate DoubleAnimation CreateSecondOrderAnimation(double from, double to)\n{\n    var animation = new DoubleAnimation\n    {\n        From = from,        \u002F\u002F 起始值（当前值）\n        To = to,            \u002F\u002F 目标值\n        Duration = Duration, \u002F\u002F 持续时间（默认 0.2 秒）\n        \u002F\u002F 使用指定的缓动函数（默认为 CubicEase，二阶缓动）\n        EasingFunction = EasingFunction\n    };\n    \n    return animation;\n}\n",[49,1013,1014,1018,1023,1027,1032,1036,1041,1045,1050,1055,1060,1065,1070,1075,1079,1084],{"__ignoreMap":65},[20,1015,1016],{"class":70,"line":71},[20,1017,318],{},[20,1019,1020],{"class":70,"line":77},[20,1021,1022],{},"\u002F\u002F\u002F 创建动画（使用指定的缓动函数，默认为二阶缓动 CubicEase）\n",[20,1024,1025],{"class":70,"line":83},[20,1026,330],{},[20,1028,1029],{"class":70,"line":89},[20,1030,1031],{},"private DoubleAnimation CreateSecondOrderAnimation(double from, double to)\n",[20,1033,1034],{"class":70,"line":95},[20,1035,342],{},[20,1037,1038],{"class":70,"line":101},[20,1039,1040],{},"    var animation = new DoubleAnimation\n",[20,1042,1043],{"class":70,"line":107},[20,1044,569],{},[20,1046,1047],{"class":70,"line":315},[20,1048,1049],{},"        From = from,        \u002F\u002F 起始值（当前值）\n",[20,1051,1052],{"class":70,"line":321},[20,1053,1054],{},"        To = to,            \u002F\u002F 目标值\n",[20,1056,1057],{"class":70,"line":327},[20,1058,1059],{},"        Duration = Duration, \u002F\u002F 持续时间（默认 0.2 秒）\n",[20,1061,1062],{"class":70,"line":333},[20,1063,1064],{},"        \u002F\u002F 使用指定的缓动函数（默认为 CubicEase，二阶缓动）\n",[20,1066,1067],{"class":70,"line":339},[20,1068,1069],{},"        EasingFunction = EasingFunction\n",[20,1071,1072],{"class":70,"line":345},[20,1073,1074],{},"    };\n",[20,1076,1077],{"class":70,"line":351},[20,1078,551],{},[20,1080,1081],{"class":70,"line":430},[20,1082,1083],{},"    return animation;\n",[20,1085,1086],{"class":70,"line":436},[20,1087,354],{},[14,1089,1090,1096],{},[17,1091,1092,1095],{},[20,1093,1094],{},"!note"," 缓动函数说明",[218,1097,1098,1104,1110],{},[213,1099,1100,1103],{},[25,1101,1102],{},"默认值：CubicEase","：三次方缓动，提供平滑的加速和减速（二阶缓动）",[213,1105,1106,1109],{},[25,1107,1108],{},"EaseInOut","：开始和结束时都平滑，中间加速",[213,1111,1112,1115],{},[25,1113,1114],{},"可自定义","：可以在 XAML 中设置其他缓动函数",[34,1117],{},[42,1119,1121],{"id":1120},"_2-在-xaml-中使用-propertychangedtrigger","2. 在 XAML 中使用 PropertyChangedTrigger",[268,1123,1124],{"id":1124},"基本用法",[60,1126,1130],{"className":1127,"code":1128,"language":1129,"meta":65,"style":65},"language-xml shiki shiki-themes github-light github-dark","\u003CBorder x:Name=\"AnimatedBorder\">\n    \u003Ci:Interaction.Triggers>\n        \u003C!-- 监听 TargetWidth 属性变化 -->\n        \u003Ci:PropertyChangedTrigger \n            Binding=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\">\n            \u003Clocal:AnimateToTargetSizeAction \n                TargetObject=\"{Binding ElementName=AnimatedBorder}\"\n                TargetWidth=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n                TargetHeight=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n                Duration=\"0:0:0.2\"\u002F>\n        \u003C\u002Fi:PropertyChangedTrigger>\n        \n        \u003C!-- 监听 TargetHeight 属性变化 -->\n        \u003Ci:PropertyChangedTrigger \n            Binding=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\">\n            \u003Clocal:AnimateToTargetSizeAction \n                TargetObject=\"{Binding ElementName=AnimatedBorder}\"\n                TargetWidth=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n                TargetHeight=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n                Duration=\"0:0:0.2\"\u002F>\n        \u003C\u002Fi:PropertyChangedTrigger>\n    \u003C\u002Fi:Interaction.Triggers>\n\u003C\u002FBorder>\n","xml",[49,1131,1132,1137,1142,1147,1152,1157,1162,1167,1172,1177,1182,1187,1192,1197,1201,1206,1210,1214,1218,1222,1226,1230,1235],{"__ignoreMap":65},[20,1133,1134],{"class":70,"line":71},[20,1135,1136],{},"\u003CBorder x:Name=\"AnimatedBorder\">\n",[20,1138,1139],{"class":70,"line":77},[20,1140,1141],{},"    \u003Ci:Interaction.Triggers>\n",[20,1143,1144],{"class":70,"line":83},[20,1145,1146],{},"        \u003C!-- 监听 TargetWidth 属性变化 -->\n",[20,1148,1149],{"class":70,"line":89},[20,1150,1151],{},"        \u003Ci:PropertyChangedTrigger \n",[20,1153,1154],{"class":70,"line":95},[20,1155,1156],{},"            Binding=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\">\n",[20,1158,1159],{"class":70,"line":101},[20,1160,1161],{},"            \u003Clocal:AnimateToTargetSizeAction \n",[20,1163,1164],{"class":70,"line":107},[20,1165,1166],{},"                TargetObject=\"{Binding ElementName=AnimatedBorder}\"\n",[20,1168,1169],{"class":70,"line":315},[20,1170,1171],{},"                TargetWidth=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n",[20,1173,1174],{"class":70,"line":321},[20,1175,1176],{},"                TargetHeight=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n",[20,1178,1179],{"class":70,"line":327},[20,1180,1181],{},"                Duration=\"0:0:0.2\"\u002F>\n",[20,1183,1184],{"class":70,"line":333},[20,1185,1186],{},"        \u003C\u002Fi:PropertyChangedTrigger>\n",[20,1188,1189],{"class":70,"line":339},[20,1190,1191],{},"        \n",[20,1193,1194],{"class":70,"line":345},[20,1195,1196],{},"        \u003C!-- 监听 TargetHeight 属性变化 -->\n",[20,1198,1199],{"class":70,"line":351},[20,1200,1151],{},[20,1202,1203],{"class":70,"line":430},[20,1204,1205],{},"            Binding=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\">\n",[20,1207,1208],{"class":70,"line":436},[20,1209,1161],{},[20,1211,1212],{"class":70,"line":442},[20,1213,1166],{},[20,1215,1216],{"class":70,"line":447},[20,1217,1171],{},[20,1219,1220],{"class":70,"line":452},[20,1221,1176],{},[20,1223,1224],{"class":70,"line":458},[20,1225,1181],{},[20,1227,1228],{"class":70,"line":464},[20,1229,1186],{},[20,1231,1232],{"class":70,"line":470},[20,1233,1234],{},"    \u003C\u002Fi:Interaction.Triggers>\n",[20,1236,1237],{"class":70,"line":475},[20,1238,1239],{},"\u003C\u002FBorder>\n",[14,1241,1242,1247],{},[17,1243,1244,1246],{},[20,1245,635],{}," 为什么需要两个 PropertyChangedTrigger？",[218,1248,1249,1252,1255],{},[213,1250,1251],{},"分别监听宽度和高度的变化",[213,1253,1254],{},"当任一属性改变时，都会触发动画",[213,1256,1257],{},"确保宽度和高度都能独立动画化",[268,1259,1261],{"id":1260},"完整示例animatedsizecontrol","完整示例：AnimatedSizeControl",[60,1263,1265],{"className":1127,"code":1264,"language":1129,"meta":65,"style":65},"\u003CUserControl x:Class=\"FluentScrollviewerSolution.AnimatedSizeControl\"\n             xmlns=\"http:\u002F\u002Fschemas.microsoft.com\u002Fwinfx\u002F2006\u002Fxaml\u002Fpresentation\"\n             xmlns:x=\"http:\u002F\u002Fschemas.microsoft.com\u002Fwinfx\u002F2006\u002Fxaml\"\n             xmlns:i=\"http:\u002F\u002Fschemas.microsoft.com\u002Fxaml\u002Fbehaviors\"\n             xmlns:local=\"clr-namespace:FluentScrollviewerSolution\">\n    \u003CGrid>\n        \u003C!-- 控制面板 -->\n        \u003CBorder Grid.Row=\"0\">\n            \u003CStackPanel>\n                \u003CSlider \n                    Value=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}, Mode=TwoWay}\"\n                    Minimum=\"100\" Maximum=\"800\"\u002F>\n                \u003CSlider \n                    Value=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}, Mode=TwoWay}\"\n                    Minimum=\"100\" Maximum=\"600\"\u002F>\n            \u003C\u002FStackPanel>\n        \u003C\u002FBorder>\n        \n        \u003C!-- 动画目标元素 -->\n        \u003CBorder x:Name=\"AnimatedBorder\" Grid.Row=\"1\">\n            \u003Ci:Interaction.Triggers>\n                \u003Ci:PropertyChangedTrigger \n                    Binding=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\">\n                    \u003Clocal:AnimateToTargetSizeAction \n                        TargetObject=\"{Binding ElementName=AnimatedBorder}\"\n                        TargetWidth=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n                        TargetHeight=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n                        Duration=\"0:0:0.2\"\u002F>\n                \u003C\u002Fi:PropertyChangedTrigger>\n                \n                \u003Ci:PropertyChangedTrigger \n                    Binding=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\">\n                    \u003Clocal:AnimateToTargetSizeAction \n                        TargetObject=\"{Binding ElementName=AnimatedBorder}\"\n                        TargetWidth=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n                        TargetHeight=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n                        Duration=\"0:0:0.2\"\u002F>\n                \u003C\u002Fi:PropertyChangedTrigger>\n            \u003C\u002Fi:Interaction.Triggers>\n        \u003C\u002FBorder>\n    \u003C\u002FGrid>\n\u003C\u002FUserControl>\n",[49,1266,1267,1272,1277,1282,1287,1292,1297,1302,1307,1312,1317,1322,1327,1331,1336,1341,1346,1351,1355,1360,1365,1370,1375,1380,1385,1390,1395,1400,1405,1410,1415,1419,1424,1428,1432,1436,1440,1444,1448,1453,1457,1462],{"__ignoreMap":65},[20,1268,1269],{"class":70,"line":71},[20,1270,1271],{},"\u003CUserControl x:Class=\"FluentScrollviewerSolution.AnimatedSizeControl\"\n",[20,1273,1274],{"class":70,"line":77},[20,1275,1276],{},"             xmlns=\"http:\u002F\u002Fschemas.microsoft.com\u002Fwinfx\u002F2006\u002Fxaml\u002Fpresentation\"\n",[20,1278,1279],{"class":70,"line":83},[20,1280,1281],{},"             xmlns:x=\"http:\u002F\u002Fschemas.microsoft.com\u002Fwinfx\u002F2006\u002Fxaml\"\n",[20,1283,1284],{"class":70,"line":89},[20,1285,1286],{},"             xmlns:i=\"http:\u002F\u002Fschemas.microsoft.com\u002Fxaml\u002Fbehaviors\"\n",[20,1288,1289],{"class":70,"line":95},[20,1290,1291],{},"             xmlns:local=\"clr-namespace:FluentScrollviewerSolution\">\n",[20,1293,1294],{"class":70,"line":101},[20,1295,1296],{},"    \u003CGrid>\n",[20,1298,1299],{"class":70,"line":107},[20,1300,1301],{},"        \u003C!-- 控制面板 -->\n",[20,1303,1304],{"class":70,"line":315},[20,1305,1306],{},"        \u003CBorder Grid.Row=\"0\">\n",[20,1308,1309],{"class":70,"line":321},[20,1310,1311],{},"            \u003CStackPanel>\n",[20,1313,1314],{"class":70,"line":327},[20,1315,1316],{},"                \u003CSlider \n",[20,1318,1319],{"class":70,"line":333},[20,1320,1321],{},"                    Value=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}, Mode=TwoWay}\"\n",[20,1323,1324],{"class":70,"line":339},[20,1325,1326],{},"                    Minimum=\"100\" Maximum=\"800\"\u002F>\n",[20,1328,1329],{"class":70,"line":345},[20,1330,1316],{},[20,1332,1333],{"class":70,"line":351},[20,1334,1335],{},"                    Value=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}, Mode=TwoWay}\"\n",[20,1337,1338],{"class":70,"line":430},[20,1339,1340],{},"                    Minimum=\"100\" Maximum=\"600\"\u002F>\n",[20,1342,1343],{"class":70,"line":436},[20,1344,1345],{},"            \u003C\u002FStackPanel>\n",[20,1347,1348],{"class":70,"line":442},[20,1349,1350],{},"        \u003C\u002FBorder>\n",[20,1352,1353],{"class":70,"line":447},[20,1354,1191],{},[20,1356,1357],{"class":70,"line":452},[20,1358,1359],{},"        \u003C!-- 动画目标元素 -->\n",[20,1361,1362],{"class":70,"line":458},[20,1363,1364],{},"        \u003CBorder x:Name=\"AnimatedBorder\" Grid.Row=\"1\">\n",[20,1366,1367],{"class":70,"line":464},[20,1368,1369],{},"            \u003Ci:Interaction.Triggers>\n",[20,1371,1372],{"class":70,"line":470},[20,1373,1374],{},"                \u003Ci:PropertyChangedTrigger \n",[20,1376,1377],{"class":70,"line":475},[20,1378,1379],{},"                    Binding=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\">\n",[20,1381,1382],{"class":70,"line":481},[20,1383,1384],{},"                    \u003Clocal:AnimateToTargetSizeAction \n",[20,1386,1387],{"class":70,"line":487},[20,1388,1389],{},"                        TargetObject=\"{Binding ElementName=AnimatedBorder}\"\n",[20,1391,1392],{"class":70,"line":492},[20,1393,1394],{},"                        TargetWidth=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n",[20,1396,1397],{"class":70,"line":498},[20,1398,1399],{},"                        TargetHeight=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n",[20,1401,1402],{"class":70,"line":504},[20,1403,1404],{},"                        Duration=\"0:0:0.2\"\u002F>\n",[20,1406,1407],{"class":70,"line":509},[20,1408,1409],{},"                \u003C\u002Fi:PropertyChangedTrigger>\n",[20,1411,1412],{"class":70,"line":514},[20,1413,1414],{},"                \n",[20,1416,1417],{"class":70,"line":520},[20,1418,1374],{},[20,1420,1421],{"class":70,"line":526},[20,1422,1423],{},"                    Binding=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\">\n",[20,1425,1426],{"class":70,"line":531},[20,1427,1384],{},[20,1429,1430],{"class":70,"line":536},[20,1431,1389],{},[20,1433,1434],{"class":70,"line":542},[20,1435,1394],{},[20,1437,1438],{"class":70,"line":548},[20,1439,1399],{},[20,1441,1442],{"class":70,"line":554},[20,1443,1404],{},[20,1445,1446],{"class":70,"line":560},[20,1447,1409],{},[20,1449,1450],{"class":70,"line":566},[20,1451,1452],{},"            \u003C\u002Fi:Interaction.Triggers>\n",[20,1454,1455],{"class":70,"line":572},[20,1456,1350],{},[20,1458,1459],{"class":70,"line":578},[20,1460,1461],{},"    \u003C\u002FGrid>\n",[20,1463,1464],{"class":70,"line":584},[20,1465,1466],{},"\u003C\u002FUserControl>\n",[34,1468],{},[42,1470,1472],{"id":1471},"_3-代码后台定义依赖属性","3. 代码后台：定义依赖属性",[60,1474,1476],{"className":273,"code":1475,"language":275,"meta":65,"style":65},"public partial class AnimatedSizeControl : UserControl\n{\n    \u002F\u002F 目标宽度依赖属性\n    public static readonly DependencyProperty TargetWidthProperty =\n        DependencyProperty.Register(\n            nameof(TargetWidth),\n            typeof(double),\n            typeof(AnimatedSizeControl),\n            new PropertyMetadata(300.0, OnTargetSizeChanged));\n    \n    \u002F\u002F 目标高度依赖属性\n    public static readonly DependencyProperty TargetHeightProperty =\n        DependencyProperty.Register(\n            nameof(TargetHeight),\n            typeof(double),\n            typeof(AnimatedSizeControl),\n            new PropertyMetadata(200.0, OnTargetSizeChanged));\n    \n    public double TargetWidth\n    {\n        get => (double)GetValue(TargetWidthProperty);\n        set => SetValue(TargetWidthProperty, value);\n    }\n    \n    public double TargetHeight\n    {\n        get => (double)GetValue(TargetHeightProperty);\n        set => SetValue(TargetHeightProperty, value);\n    }\n    \n    public AnimatedSizeControl()\n    {\n        InitializeComponent();\n        \n        \u002F\u002F 初始化动画目标元素的尺寸\n        if (AnimatedBorder != null)\n        {\n            AnimatedBorder.Width = TargetWidth;\n            AnimatedBorder.Height = TargetHeight;\n        }\n    }\n    \n    private static void OnTargetSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n    {\n        \u002F\u002F PropertyChangedTrigger 会自动处理动画\n        \u002F\u002F 这里可以添加额外的逻辑（如果需要）\n    }\n}\n",[49,1477,1478,1483,1487,1492,1497,1501,1506,1511,1516,1521,1525,1530,1535,1539,1544,1548,1552,1557,1561,1566,1570,1575,1580,1584,1588,1593,1597,1602,1607,1611,1615,1620,1624,1629,1633,1638,1643,1648,1653,1658,1663,1667,1671,1676,1680,1685,1690,1694],{"__ignoreMap":65},[20,1479,1480],{"class":70,"line":71},[20,1481,1482],{},"public partial class AnimatedSizeControl : UserControl\n",[20,1484,1485],{"class":70,"line":77},[20,1486,342],{},[20,1488,1489],{"class":70,"line":83},[20,1490,1491],{},"    \u002F\u002F 目标宽度依赖属性\n",[20,1493,1494],{"class":70,"line":89},[20,1495,1496],{},"    public static readonly DependencyProperty TargetWidthProperty =\n",[20,1498,1499],{"class":70,"line":95},[20,1500,604],{},[20,1502,1503],{"class":70,"line":101},[20,1504,1505],{},"            nameof(TargetWidth),\n",[20,1507,1508],{"class":70,"line":107},[20,1509,1510],{},"            typeof(double),\n",[20,1512,1513],{"class":70,"line":315},[20,1514,1515],{},"            typeof(AnimatedSizeControl),\n",[20,1517,1518],{"class":70,"line":321},[20,1519,1520],{},"            new PropertyMetadata(300.0, OnTargetSizeChanged));\n",[20,1522,1523],{"class":70,"line":327},[20,1524,551],{},[20,1526,1527],{"class":70,"line":333},[20,1528,1529],{},"    \u002F\u002F 目标高度依赖属性\n",[20,1531,1532],{"class":70,"line":339},[20,1533,1534],{},"    public static readonly DependencyProperty TargetHeightProperty =\n",[20,1536,1537],{"class":70,"line":345},[20,1538,604],{},[20,1540,1541],{"class":70,"line":351},[20,1542,1543],{},"            nameof(TargetHeight),\n",[20,1545,1546],{"class":70,"line":430},[20,1547,1510],{},[20,1549,1550],{"class":70,"line":436},[20,1551,1515],{},[20,1553,1554],{"class":70,"line":442},[20,1555,1556],{},"            new PropertyMetadata(200.0, OnTargetSizeChanged));\n",[20,1558,1559],{"class":70,"line":447},[20,1560,551],{},[20,1562,1563],{"class":70,"line":452},[20,1564,1565],{},"    public double TargetWidth\n",[20,1567,1568],{"class":70,"line":458},[20,1569,569],{},[20,1571,1572],{"class":70,"line":464},[20,1573,1574],{},"        get => (double)GetValue(TargetWidthProperty);\n",[20,1576,1577],{"class":70,"line":470},[20,1578,1579],{},"        set => SetValue(TargetWidthProperty, value);\n",[20,1581,1582],{"class":70,"line":475},[20,1583,587],{},[20,1585,1586],{"class":70,"line":481},[20,1587,551],{},[20,1589,1590],{"class":70,"line":487},[20,1591,1592],{},"    public double TargetHeight\n",[20,1594,1595],{"class":70,"line":492},[20,1596,569],{},[20,1598,1599],{"class":70,"line":498},[20,1600,1601],{},"        get => (double)GetValue(TargetHeightProperty);\n",[20,1603,1604],{"class":70,"line":504},[20,1605,1606],{},"        set => SetValue(TargetHeightProperty, value);\n",[20,1608,1609],{"class":70,"line":509},[20,1610,587],{},[20,1612,1613],{"class":70,"line":514},[20,1614,551],{},[20,1616,1617],{"class":70,"line":520},[20,1618,1619],{},"    public AnimatedSizeControl()\n",[20,1621,1622],{"class":70,"line":526},[20,1623,569],{},[20,1625,1626],{"class":70,"line":531},[20,1627,1628],{},"        InitializeComponent();\n",[20,1630,1631],{"class":70,"line":536},[20,1632,1191],{},[20,1634,1635],{"class":70,"line":542},[20,1636,1637],{},"        \u002F\u002F 初始化动画目标元素的尺寸\n",[20,1639,1640],{"class":70,"line":548},[20,1641,1642],{},"        if (AnimatedBorder != null)\n",[20,1644,1645],{"class":70,"line":554},[20,1646,1647],{},"        {\n",[20,1649,1650],{"class":70,"line":560},[20,1651,1652],{},"            AnimatedBorder.Width = TargetWidth;\n",[20,1654,1655],{"class":70,"line":566},[20,1656,1657],{},"            AnimatedBorder.Height = TargetHeight;\n",[20,1659,1660],{"class":70,"line":572},[20,1661,1662],{},"        }\n",[20,1664,1665],{"class":70,"line":578},[20,1666,587],{},[20,1668,1669],{"class":70,"line":584},[20,1670,551],{},[20,1672,1673],{"class":70,"line":590},[20,1674,1675],{},"    private static void OnTargetSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\n",[20,1677,1678],{"class":70,"line":595},[20,1679,569],{},[20,1681,1682],{"class":70,"line":601},[20,1683,1684],{},"        \u002F\u002F PropertyChangedTrigger 会自动处理动画\n",[20,1686,1687],{"class":70,"line":607},[20,1688,1689],{},"        \u002F\u002F 这里可以添加额外的逻辑（如果需要）\n",[20,1691,1692],{"class":70,"line":613},[20,1693,587],{},[20,1695,1696],{"class":70,"line":619},[20,1697,354],{},[34,1699],{},[37,1701,1703],{"id":1702},"relativesource-绑定详解","🔗 RelativeSource 绑定详解",[42,1705,1707],{"id":1706},"什么是-relativesource","什么是 RelativeSource？",[17,1709,1710,1713,1714,1717],{},[49,1711,1712],{},"RelativeSource"," 是 WPF 数据绑定中的一个特殊标记扩展，它允许你",[25,1715,1716],{},"相对于当前元素在 UI 树中的位置","来查找绑定源，而不是使用绝对路径。",[42,1719,1721],{"id":1720},"relativesource-的语法","RelativeSource 的语法",[60,1723,1725],{"className":1127,"code":1724,"language":1129,"meta":65,"style":65},"{Binding Path=PropertyName, RelativeSource={RelativeSource AncestorType=TargetType}}\n",[49,1726,1727],{"__ignoreMap":65},[20,1728,1729],{"class":70,"line":71},[20,1730,1724],{},[17,1732,1733],{},"其中：",[218,1735,1736,1742,1747],{},[213,1737,1738,1741],{},[49,1739,1740],{},"Path","：要绑定的属性名",[213,1743,1744,1746],{},[49,1745,1712],{},"：相对源标记",[213,1748,1749,1752],{},[49,1750,1751],{},"AncestorType","：要查找的祖先元素类型",[42,1754,1756],{"id":1755},"工作原理向上遍历-ui-树","工作原理：向上遍历 UI 树",[60,1758,1760],{"className":62,"code":1759,"language":64,"meta":65,"style":65},"graph TD\n    A[当前元素 Border] --> B[向上查找父元素]\n    B --> C{是 UserControl?}\n    C -->|否| D[继续向上查找]\n    D --> E{是 UserControl?}\n    E -->|否| F[继续向上查找]\n    F --> G{是 UserControl?}\n    G -->|是| H[找到！绑定到 UserControl 的属性]\n    H --> I[返回 TargetWidth\u002FTargetHeight 的值]\n",[49,1761,1762,1766,1771,1776,1781,1786,1791,1796,1801],{"__ignoreMap":65},[20,1763,1764],{"class":70,"line":71},[20,1765,74],{},[20,1767,1768],{"class":70,"line":77},[20,1769,1770],{},"    A[当前元素 Border] --> B[向上查找父元素]\n",[20,1772,1773],{"class":70,"line":83},[20,1774,1775],{},"    B --> C{是 UserControl?}\n",[20,1777,1778],{"class":70,"line":89},[20,1779,1780],{},"    C -->|否| D[继续向上查找]\n",[20,1782,1783],{"class":70,"line":95},[20,1784,1785],{},"    D --> E{是 UserControl?}\n",[20,1787,1788],{"class":70,"line":101},[20,1789,1790],{},"    E -->|否| F[继续向上查找]\n",[20,1792,1793],{"class":70,"line":107},[20,1794,1795],{},"    F --> G{是 UserControl?}\n",[20,1797,1798],{"class":70,"line":315},[20,1799,1800],{},"    G -->|是| H[找到！绑定到 UserControl 的属性]\n",[20,1802,1803],{"class":70,"line":321},[20,1804,1805],{},"    H --> I[返回 TargetWidth\u002FTargetHeight 的值]\n",[268,1807,1809],{"id":1808},"实际-ui-树结构","实际 UI 树结构",[60,1811,1814],{"className":1812,"code":1813,"language":203},[201],"Window\n└── TabControl\n    └── TabItem\n        └── AnimatedSizeControl (UserControl) ← 我们要找的目标\n            └── Grid\n                └── Border (AnimatedBorder) ← 当前元素\n                    └── Interaction.Triggers\n                        └── PropertyChangedTrigger ← 绑定在这里\n",[49,1815,1813],{"__ignoreMap":65},[17,1817,1818,1821],{},[25,1819,1820],{},"绑定过程","：",[210,1823,1824,1830,1846,1852],{},[213,1825,1826,1827,1829],{},"从 ",[49,1828,27],{}," 开始",[213,1831,1832,1833,1835,1836,1835,1839,1835,1842,1845],{},"向上查找：",[49,1834,27],{}," → ",[49,1837,1838],{},"Border",[49,1840,1841],{},"Grid",[49,1843,1844],{},"AnimatedSizeControl"," ✅",[213,1847,1848,1849,1851],{},"找到 ",[49,1850,1844],{},"（UserControl 类型）",[213,1853,1854,1855,1858],{},"绑定到 ",[49,1856,1857],{},"AnimatedSizeControl.TargetWidth"," 属性",[42,1860,1861],{"id":1861},"代码示例解析",[60,1863,1865],{"className":1127,"code":1864,"language":1129,"meta":65,"style":65},"\u003Ci:PropertyChangedTrigger \n    Binding=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\">\n    \u003Clocal:AnimateToTargetSizeAction \n        TargetObject=\"{Binding ElementName=AnimatedBorder}\"\n        TargetWidth=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n        TargetHeight=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n        Duration=\"0:0:0.4\"\u002F>\n\u003C\u002Fi:PropertyChangedTrigger>\n",[49,1866,1867,1872,1877,1882,1887,1892,1897,1902],{"__ignoreMap":65},[20,1868,1869],{"class":70,"line":71},[20,1870,1871],{},"\u003Ci:PropertyChangedTrigger \n",[20,1873,1874],{"class":70,"line":77},[20,1875,1876],{},"    Binding=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\">\n",[20,1878,1879],{"class":70,"line":83},[20,1880,1881],{},"    \u003Clocal:AnimateToTargetSizeAction \n",[20,1883,1884],{"class":70,"line":89},[20,1885,1886],{},"        TargetObject=\"{Binding ElementName=AnimatedBorder}\"\n",[20,1888,1889],{"class":70,"line":95},[20,1890,1891],{},"        TargetWidth=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n",[20,1893,1894],{"class":70,"line":101},[20,1895,1896],{},"        TargetHeight=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n",[20,1898,1899],{"class":70,"line":107},[20,1900,1901],{},"        Duration=\"0:0:0.4\"\u002F>\n",[20,1903,1904],{"class":70,"line":315},[20,1905,1906],{},"\u003C\u002Fi:PropertyChangedTrigger>\n",[268,1908,1909],{"id":1909},"逐行解释",[17,1911,1912],{},[25,1913,1914],{},"第 1 行：PropertyChangedTrigger 的 Binding",[60,1916,1918],{"className":1127,"code":1917,"language":1129,"meta":65,"style":65},"Binding=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n",[49,1919,1920],{"__ignoreMap":65},[20,1921,1922],{"class":70,"line":71},[20,1923,1917],{},[218,1925,1926,1932,1938],{},[213,1927,1928,1931],{},[25,1929,1930],{},"Binding","：这是 PropertyChangedTrigger 要监听的属性",[213,1933,1934,1937],{},[25,1935,1936],{},"Path=TargetWidth","：要监听的属性名（可以省略 Path=）",[213,1939,1940,1943,1944],{},[25,1941,1942],{},"RelativeSource={RelativeSource AncestorType=UserControl}","：\n",[218,1945,1946,1953],{},[213,1947,1948,1949,1952],{},"向上查找最近的 ",[49,1950,1951],{},"UserControl"," 类型的元素",[213,1954,1955,1956,1858],{},"找到后，绑定到该元素的 ",[49,1957,1958],{},"TargetWidth",[17,1960,1961],{},[25,1962,1963],{},"第 3 行：TargetWidth 绑定",[60,1965,1967],{"className":1127,"code":1966,"language":1129,"meta":65,"style":65},"TargetWidth=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n",[49,1968,1969],{"__ignoreMap":65},[20,1970,1971],{"class":70,"line":71},[20,1972,1966],{},[218,1974,1975,1983],{},[213,1976,1977,1978,1980,1981,1858],{},"将 Action 的 ",[49,1979,1958],{}," 属性绑定到 UserControl 的 ",[49,1982,1958],{},[213,1984,1985,1986,1988],{},"这样当 UserControl 的 ",[49,1987,1958],{}," 改变时，Action 也能获取到新值",[42,1990,1992],{"id":1991},"relativesource-的其他用法","RelativeSource 的其他用法",[268,1994,1996],{"id":1995},"_1-ancestorlevel指定层级","1. AncestorLevel（指定层级）",[60,1998,2000],{"className":1127,"code":1999,"language":1129,"meta":65,"style":65},"\u003C!-- 向上查找第 2 个 UserControl（跳过第一个） -->\n{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl, AncestorLevel=2}}\n",[49,2001,2002,2007],{"__ignoreMap":65},[20,2003,2004],{"class":70,"line":71},[20,2005,2006],{},"\u003C!-- 向上查找第 2 个 UserControl（跳过第一个） -->\n",[20,2008,2009],{"class":70,"line":77},[20,2010,2011],{},"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl, AncestorLevel=2}}\n",[268,2013,2015],{"id":2014},"_2-self绑定到自己","2. Self（绑定到自己）",[60,2017,2019],{"className":1127,"code":2018,"language":1129,"meta":65,"style":65},"\u003C!-- 绑定到当前元素自己的属性 -->\n{Binding Width, RelativeSource={RelativeSource Self}}\n",[49,2020,2021,2026],{"__ignoreMap":65},[20,2022,2023],{"class":70,"line":71},[20,2024,2025],{},"\u003C!-- 绑定到当前元素自己的属性 -->\n",[20,2027,2028],{"class":70,"line":77},[20,2029,2030],{},"{Binding Width, RelativeSource={RelativeSource Self}}\n",[268,2032,2034],{"id":2033},"_3-templatedparent模板父元素","3. TemplatedParent（模板父元素）",[60,2036,2038],{"className":1127,"code":2037,"language":1129,"meta":65,"style":65},"\u003C!-- 在 ControlTemplate 中绑定到应用模板的控件 -->\n{Binding DataContext, RelativeSource={RelativeSource TemplatedParent}}\n",[49,2039,2040,2045],{"__ignoreMap":65},[20,2041,2042],{"class":70,"line":71},[20,2043,2044],{},"\u003C!-- 在 ControlTemplate 中绑定到应用模板的控件 -->\n",[20,2046,2047],{"class":70,"line":77},[20,2048,2049],{},"{Binding DataContext, RelativeSource={RelativeSource TemplatedParent}}\n",[268,2051,2053],{"id":2052},"_4-previousdata列表中的前一项","4. PreviousData（列表中的前一项）",[60,2055,2057],{"className":1127,"code":2056,"language":1129,"meta":65,"style":65},"\u003C!-- 在 ItemsControl 中绑定到前一项数据 -->\n{Binding PreviousData.Name}\n",[49,2058,2059,2064],{"__ignoreMap":65},[20,2060,2061],{"class":70,"line":71},[20,2062,2063],{},"\u003C!-- 在 ItemsControl 中绑定到前一项数据 -->\n",[20,2065,2066],{"class":70,"line":77},[20,2067,2068],{},"{Binding PreviousData.Name}\n",[42,2070,2071],{"id":2071},"与其他绑定方式的对比",[115,2073,2074,2086],{},[118,2075,2076],{},[121,2077,2078,2081,2084],{},[124,2079,2080],{},"绑定方式",[124,2082,2083],{},"语法",[124,2085,182],{},[134,2087,2088,2103,2118,2133],{},[121,2089,2090,2095,2100],{},[139,2091,2092],{},[25,2093,2094],{},"RelativeSource AncestorType",[139,2096,2097],{},[49,2098,2099],{},"{Binding Path, RelativeSource={RelativeSource AncestorType=Type}}",[139,2101,2102],{},"向上查找特定类型的祖先元素",[121,2104,2105,2110,2115],{},[139,2106,2107],{},[25,2108,2109],{},"ElementName",[139,2111,2112],{},[49,2113,2114],{},"{Binding Path, ElementName=Name}",[139,2116,2117],{},"绑定到命名元素（同一 XAML 中）",[121,2119,2120,2125,2130],{},[139,2121,2122],{},[25,2123,2124],{},"Source",[139,2126,2127],{},[49,2128,2129],{},"{Binding Path, Source=Object}",[139,2131,2132],{},"绑定到代码中的对象",[121,2134,2135,2140,2145],{},[139,2136,2137],{},[25,2138,2139],{},"DataContext",[139,2141,2142],{},[49,2143,2144],{},"{Binding Path}",[139,2146,2147],{},"绑定到 DataContext（默认）",[42,2149,2151],{"id":2150},"为什么使用-relativesource","为什么使用 RelativeSource？",[268,2153,2155],{"id":2154},"优势","✅ 优势",[210,2157,2158,2164,2170,2176],{},[213,2159,2160,2163],{},[25,2161,2162],{},"灵活性","：不依赖具体的元素名称",[213,2165,2166,2169],{},[25,2167,2168],{},"可复用性","：可以在不同的上下文中使用",[213,2171,2172,2175],{},[25,2173,2174],{},"解耦","：不需要知道完整的 UI 树结构",[213,2177,2178,2181],{},[25,2179,2180],{},"类型安全","：通过类型查找，而不是名称",[268,2183,2185],{"id":2184},"不使用-relativesource-的替代方案","❌ 不使用 RelativeSource 的替代方案",[17,2187,2188],{},[25,2189,2190],{},"方案 1：使用 ElementName（需要命名）",[60,2192,2194],{"className":1127,"code":2193,"language":1129,"meta":65,"style":65},"\u003C!-- 需要给 UserControl 一个 x:Name -->\n\u003CUserControl x:Name=\"MyControl\">\n    \u003CBorder>\n        \u003Ci:PropertyChangedTrigger \n            Binding=\"{Binding TargetWidth, ElementName=MyControl}\">\n            \u003C!-- ... -->\n        \u003C\u002Fi:PropertyChangedTrigger>\n    \u003C\u002FBorder>\n\u003C\u002FUserControl>\n",[49,2195,2196,2201,2206,2211,2215,2220,2225,2229,2234],{"__ignoreMap":65},[20,2197,2198],{"class":70,"line":71},[20,2199,2200],{},"\u003C!-- 需要给 UserControl 一个 x:Name -->\n",[20,2202,2203],{"class":70,"line":77},[20,2204,2205],{},"\u003CUserControl x:Name=\"MyControl\">\n",[20,2207,2208],{"class":70,"line":83},[20,2209,2210],{},"    \u003CBorder>\n",[20,2212,2213],{"class":70,"line":89},[20,2214,1151],{},[20,2216,2217],{"class":70,"line":95},[20,2218,2219],{},"            Binding=\"{Binding TargetWidth, ElementName=MyControl}\">\n",[20,2221,2222],{"class":70,"line":101},[20,2223,2224],{},"            \u003C!-- ... -->\n",[20,2226,2227],{"class":70,"line":107},[20,2228,1186],{},[20,2230,2231],{"class":70,"line":315},[20,2232,2233],{},"    \u003C\u002FBorder>\n",[20,2235,2236],{"class":70,"line":321},[20,2237,1466],{},[17,2239,2240],{},[25,2241,2242],{},"方案 2：使用 DataContext（需要设置）",[60,2244,2246],{"className":1127,"code":2245,"language":1129,"meta":65,"style":65},"\u003C!-- 需要设置 DataContext -->\n\u003CUserControl DataContext=\"{Binding RelativeSource={RelativeSource Self}}\">\n    \u003CBorder>\n        \u003Ci:PropertyChangedTrigger \n            Binding=\"{Binding TargetWidth}\">\n            \u003C!-- ... -->\n        \u003C\u002Fi:PropertyChangedTrigger>\n    \u003C\u002FBorder>\n\u003C\u002FUserControl>\n",[49,2247,2248,2253,2258,2262,2266,2271,2275,2279,2283],{"__ignoreMap":65},[20,2249,2250],{"class":70,"line":71},[20,2251,2252],{},"\u003C!-- 需要设置 DataContext -->\n",[20,2254,2255],{"class":70,"line":77},[20,2256,2257],{},"\u003CUserControl DataContext=\"{Binding RelativeSource={RelativeSource Self}}\">\n",[20,2259,2260],{"class":70,"line":83},[20,2261,2210],{},[20,2263,2264],{"class":70,"line":89},[20,2265,1151],{},[20,2267,2268],{"class":70,"line":95},[20,2269,2270],{},"            Binding=\"{Binding TargetWidth}\">\n",[20,2272,2273],{"class":70,"line":101},[20,2274,2224],{},[20,2276,2277],{"class":70,"line":107},[20,2278,1186],{},[20,2280,2281],{"class":70,"line":315},[20,2282,2233],{},[20,2284,2285],{"class":70,"line":321},[20,2286,1466],{},[14,2288,2289,2294],{},[17,2290,2291,2293],{},[20,2292,635],{}," 为什么选择 RelativeSource？",[218,2295,2296,2299,2302,2305],{},[213,2297,2298],{},"不需要给 UserControl 命名",[213,2300,2301],{},"不需要设置 DataContext",[213,2303,2304],{},"代码更简洁",[213,2306,2307],{},"更符合 WPF 的最佳实践",[42,2309,2310],{"id":2310},"实际执行流程",[60,2312,2314],{"className":273,"code":2313,"language":275,"meta":65,"style":65},"\u002F\u002F 1. WPF 绑定引擎解析 RelativeSource\nvar relativeSource = new RelativeSource(RelativeSourceMode.FindAncestor);\nrelativeSource.AncestorType = typeof(UserControl);\n\n\u002F\u002F 2. 从当前元素开始向上遍历 UI 树\nFrameworkElement current = PropertyChangedTrigger; \u002F\u002F 当前元素\nwhile (current != null)\n{\n    current = current.Parent as FrameworkElement;\n    \n    \u002F\u002F 3. 检查是否是目标类型\n    if (current is UserControl)\n    {\n        \u002F\u002F 4. 找到！绑定到该元素的属性\n        var bindingSource = current; \u002F\u002F AnimatedSizeControl\n        var propertyValue = bindingSource.GetValue(UserControl.TargetWidthProperty);\n        return propertyValue;\n    }\n}\n\n\u002F\u002F 5. 如果没找到，返回 DependencyProperty.UnsetValue\n",[49,2315,2316,2321,2326,2331,2335,2340,2345,2350,2354,2359,2363,2368,2373,2377,2382,2387,2392,2397,2401,2405,2409],{"__ignoreMap":65},[20,2317,2318],{"class":70,"line":71},[20,2319,2320],{},"\u002F\u002F 1. WPF 绑定引擎解析 RelativeSource\n",[20,2322,2323],{"class":70,"line":77},[20,2324,2325],{},"var relativeSource = new RelativeSource(RelativeSourceMode.FindAncestor);\n",[20,2327,2328],{"class":70,"line":83},[20,2329,2330],{},"relativeSource.AncestorType = typeof(UserControl);\n",[20,2332,2333],{"class":70,"line":89},[20,2334,303],{"emptyLinePlaceholder":302},[20,2336,2337],{"class":70,"line":95},[20,2338,2339],{},"\u002F\u002F 2. 从当前元素开始向上遍历 UI 树\n",[20,2341,2342],{"class":70,"line":101},[20,2343,2344],{},"FrameworkElement current = PropertyChangedTrigger; \u002F\u002F 当前元素\n",[20,2346,2347],{"class":70,"line":107},[20,2348,2349],{},"while (current != null)\n",[20,2351,2352],{"class":70,"line":315},[20,2353,342],{},[20,2355,2356],{"class":70,"line":321},[20,2357,2358],{},"    current = current.Parent as FrameworkElement;\n",[20,2360,2361],{"class":70,"line":327},[20,2362,551],{},[20,2364,2365],{"class":70,"line":333},[20,2366,2367],{},"    \u002F\u002F 3. 检查是否是目标类型\n",[20,2369,2370],{"class":70,"line":339},[20,2371,2372],{},"    if (current is UserControl)\n",[20,2374,2375],{"class":70,"line":345},[20,2376,569],{},[20,2378,2379],{"class":70,"line":351},[20,2380,2381],{},"        \u002F\u002F 4. 找到！绑定到该元素的属性\n",[20,2383,2384],{"class":70,"line":430},[20,2385,2386],{},"        var bindingSource = current; \u002F\u002F AnimatedSizeControl\n",[20,2388,2389],{"class":70,"line":436},[20,2390,2391],{},"        var propertyValue = bindingSource.GetValue(UserControl.TargetWidthProperty);\n",[20,2393,2394],{"class":70,"line":442},[20,2395,2396],{},"        return propertyValue;\n",[20,2398,2399],{"class":70,"line":447},[20,2400,587],{},[20,2402,2403],{"class":70,"line":452},[20,2404,354],{},[20,2406,2407],{"class":70,"line":458},[20,2408,303],{"emptyLinePlaceholder":302},[20,2410,2411],{"class":70,"line":464},[20,2412,2413],{},"\u002F\u002F 5. 如果没找到，返回 DependencyProperty.UnsetValue\n",[42,2415,2416],{"id":2416},"调试技巧",[268,2418,2420],{"id":2419},"_1-检查绑定是否成功","1. 检查绑定是否成功",[60,2422,2424],{"className":1127,"code":2423,"language":1129,"meta":65,"style":65},"\u003C!-- 添加 TextBlock 显示绑定值，用于调试 -->\n\u003CTextBlock \n    Text=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}, \n                   StringFormat='TargetWidth: {0}'}\"\u002F>\n",[49,2425,2426,2431,2436,2441],{"__ignoreMap":65},[20,2427,2428],{"class":70,"line":71},[20,2429,2430],{},"\u003C!-- 添加 TextBlock 显示绑定值，用于调试 -->\n",[20,2432,2433],{"class":70,"line":77},[20,2434,2435],{},"\u003CTextBlock \n",[20,2437,2438],{"class":70,"line":83},[20,2439,2440],{},"    Text=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}, \n",[20,2442,2443],{"class":70,"line":89},[20,2444,2445],{},"                   StringFormat='TargetWidth: {0}'}\"\u002F>\n",[268,2447,2449],{"id":2448},"_2-使用输出窗口查看绑定错误","2. 使用输出窗口查看绑定错误",[17,2451,2452],{},"在 Visual Studio 的输出窗口中，选择\"显示输出来源：调试\"，可以看到绑定错误信息。",[268,2454,2456],{"id":2455},"_3-使用-snoop-工具","3. 使用 Snoop 工具",[17,2458,2459,2460,2467],{},"使用 ",[2461,2462,2466],"a",{"href":2463,"rel":2464},"https:\u002F\u002Fgithub.com\u002Fsnoopwpf\u002Fsnoopwpf",[2465],"nofollow","Snoop"," 工具可以实时查看 UI 树和绑定状态。",[42,2469,2470],{"id":2470},"常见问题",[268,2472,2474],{"id":2473},"q1-如果-ui-树中没有-usercontrol-会怎样","Q1: 如果 UI 树中没有 UserControl 会怎样？",[17,2476,2477,2480,2481,2484],{},[25,2478,2479],{},"A",": 绑定会失败，返回 ",[49,2482,2483],{},"DependencyProperty.UnsetValue","，但不会抛出异常。PropertyChangedTrigger 不会触发。",[268,2486,2488],{"id":2487},"q2-如果有多个-usercontrol会绑定到哪一个","Q2: 如果有多个 UserControl，会绑定到哪一个？",[17,2490,2491,2493,2494,2497],{},[25,2492,2479],{},": 会绑定到",[25,2495,2496],{},"最近的","（向上遍历时第一个遇到的）UserControl。",[60,2499,2502],{"className":2500,"code":2501,"language":203},[201],"Window\n└── UserControl1 ← 更远\n    └── UserControl2 ← 更近（会绑定到这里）\n        └── Border\n",[49,2503,2501],{"__ignoreMap":65},[268,2505,2507],{"id":2506},"q3-可以在代码中使用-relativesource-吗","Q3: 可以在代码中使用 RelativeSource 吗？",[17,2509,2510,2512],{},[25,2511,2479],{},": 可以，但通常不需要。XAML 绑定更简洁：",[60,2514,2516],{"className":273,"code":2515,"language":275,"meta":65,"style":65},"\u002F\u002F 代码中创建绑定（不推荐，XAML 更简洁）\nvar binding = new Binding(\"TargetWidth\")\n{\n    RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor)\n    {\n        AncestorType = typeof(UserControl)\n    }\n};\n",[49,2517,2518,2523,2528,2532,2537,2541,2546,2550],{"__ignoreMap":65},[20,2519,2520],{"class":70,"line":71},[20,2521,2522],{},"\u002F\u002F 代码中创建绑定（不推荐，XAML 更简洁）\n",[20,2524,2525],{"class":70,"line":77},[20,2526,2527],{},"var binding = new Binding(\"TargetWidth\")\n",[20,2529,2530],{"class":70,"line":83},[20,2531,342],{},[20,2533,2534],{"class":70,"line":89},[20,2535,2536],{},"    RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor)\n",[20,2538,2539],{"class":70,"line":95},[20,2540,569],{},[20,2542,2543],{"class":70,"line":101},[20,2544,2545],{},"        AncestorType = typeof(UserControl)\n",[20,2547,2548],{"class":70,"line":107},[20,2549,587],{},[20,2551,2552],{"class":70,"line":315},[20,2553,2554],{},"};\n",[34,2556],{},[37,2558,2560],{"id":2559},"工作原理深度解析","🔍 工作原理深度解析",[42,2562,2564],{"id":2563},"_1-propertychangedtrigger-如何工作","1. PropertyChangedTrigger 如何工作？",[60,2566,2568],{"className":62,"code":2567,"language":64,"meta":65,"style":65},"sequenceDiagram\n    participant User as 用户操作\n    participant Slider as 滑块控件\n    participant DP as 依赖属性\n    participant Trigger as PropertyChangedTrigger\n    participant Action as AnimateToTargetSizeAction\n    participant Storyboard as Storyboard\n    participant UI as UI 元素\n    \n    User->>Slider: 拖动滑块\n    Slider->>DP: 更新 TargetWidth\u002FTargetHeight\n    DP->>Trigger: 触发 PropertyChanged 事件\n    Trigger->>Action: 调用 Invoke()\n    Action->>Action: 计算当前值与目标值的差值\n    Action->>Storyboard: 创建动画\n    Storyboard->>UI: 执行动画\n    UI->>UI: 平滑过渡到目标值\n",[49,2569,2570,2575,2580,2585,2590,2595,2600,2605,2610,2614,2619,2624,2629,2634,2639,2644,2649],{"__ignoreMap":65},[20,2571,2572],{"class":70,"line":71},[20,2573,2574],{},"sequenceDiagram\n",[20,2576,2577],{"class":70,"line":77},[20,2578,2579],{},"    participant User as 用户操作\n",[20,2581,2582],{"class":70,"line":83},[20,2583,2584],{},"    participant Slider as 滑块控件\n",[20,2586,2587],{"class":70,"line":89},[20,2588,2589],{},"    participant DP as 依赖属性\n",[20,2591,2592],{"class":70,"line":95},[20,2593,2594],{},"    participant Trigger as PropertyChangedTrigger\n",[20,2596,2597],{"class":70,"line":101},[20,2598,2599],{},"    participant Action as AnimateToTargetSizeAction\n",[20,2601,2602],{"class":70,"line":107},[20,2603,2604],{},"    participant Storyboard as Storyboard\n",[20,2606,2607],{"class":70,"line":315},[20,2608,2609],{},"    participant UI as UI 元素\n",[20,2611,2612],{"class":70,"line":321},[20,2613,551],{},[20,2615,2616],{"class":70,"line":327},[20,2617,2618],{},"    User->>Slider: 拖动滑块\n",[20,2620,2621],{"class":70,"line":333},[20,2622,2623],{},"    Slider->>DP: 更新 TargetWidth\u002FTargetHeight\n",[20,2625,2626],{"class":70,"line":339},[20,2627,2628],{},"    DP->>Trigger: 触发 PropertyChanged 事件\n",[20,2630,2631],{"class":70,"line":345},[20,2632,2633],{},"    Trigger->>Action: 调用 Invoke()\n",[20,2635,2636],{"class":70,"line":351},[20,2637,2638],{},"    Action->>Action: 计算当前值与目标值的差值\n",[20,2640,2641],{"class":70,"line":430},[20,2642,2643],{},"    Action->>Storyboard: 创建动画\n",[20,2645,2646],{"class":70,"line":436},[20,2647,2648],{},"    Storyboard->>UI: 执行动画\n",[20,2650,2651],{"class":70,"line":442},[20,2652,2653],{},"    UI->>UI: 平滑过渡到目标值\n",[42,2655,2657],{"id":2656},"_2-差值计算逻辑","2. 差值计算逻辑",[60,2659,2661],{"className":273,"code":2660,"language":275,"meta":65,"style":65},"\u002F\u002F 步骤 1：获取当前值\ndouble currentWidth = target.Width;  \u002F\u002F 可能是 NaN\ndouble currentHeight = target.Height; \u002F\u002F 可能是 NaN\n\n\u002F\u002F 步骤 2：处理 NaN 情况\nif (double.IsNaN(currentWidth) || currentWidth == 0)\n    currentWidth = target.ActualWidth;  \u002F\u002F 使用实际宽度\n\n\u002F\u002F 步骤 3：计算差值\ndouble deltaWidth = TargetWidth - currentWidth;  \u002F\u002F 自动计算差值\ndouble deltaHeight = TargetHeight - currentHeight;\n\n\u002F\u002F 步骤 4：创建动画（从当前值到目标值）\nvar animation = new DoubleAnimation\n{\n    From = currentWidth,    \u002F\u002F 起始值\n    To = TargetWidth,       \u002F\u002F 目标值\n    Duration = Duration\n};\n",[49,2662,2663,2668,2673,2678,2682,2687,2692,2697,2701,2706,2711,2716,2720,2725,2730,2734,2739,2744,2749],{"__ignoreMap":65},[20,2664,2665],{"class":70,"line":71},[20,2666,2667],{},"\u002F\u002F 步骤 1：获取当前值\n",[20,2669,2670],{"class":70,"line":77},[20,2671,2672],{},"double currentWidth = target.Width;  \u002F\u002F 可能是 NaN\n",[20,2674,2675],{"class":70,"line":83},[20,2676,2677],{},"double currentHeight = target.Height; \u002F\u002F 可能是 NaN\n",[20,2679,2680],{"class":70,"line":89},[20,2681,303],{"emptyLinePlaceholder":302},[20,2683,2684],{"class":70,"line":95},[20,2685,2686],{},"\u002F\u002F 步骤 2：处理 NaN 情况\n",[20,2688,2689],{"class":70,"line":101},[20,2690,2691],{},"if (double.IsNaN(currentWidth) || currentWidth == 0)\n",[20,2693,2694],{"class":70,"line":107},[20,2695,2696],{},"    currentWidth = target.ActualWidth;  \u002F\u002F 使用实际宽度\n",[20,2698,2699],{"class":70,"line":315},[20,2700,303],{"emptyLinePlaceholder":302},[20,2702,2703],{"class":70,"line":321},[20,2704,2705],{},"\u002F\u002F 步骤 3：计算差值\n",[20,2707,2708],{"class":70,"line":327},[20,2709,2710],{},"double deltaWidth = TargetWidth - currentWidth;  \u002F\u002F 自动计算差值\n",[20,2712,2713],{"class":70,"line":333},[20,2714,2715],{},"double deltaHeight = TargetHeight - currentHeight;\n",[20,2717,2718],{"class":70,"line":339},[20,2719,303],{"emptyLinePlaceholder":302},[20,2721,2722],{"class":70,"line":345},[20,2723,2724],{},"\u002F\u002F 步骤 4：创建动画（从当前值到目标值）\n",[20,2726,2727],{"class":70,"line":351},[20,2728,2729],{},"var animation = new DoubleAnimation\n",[20,2731,2732],{"class":70,"line":430},[20,2733,342],{},[20,2735,2736],{"class":70,"line":436},[20,2737,2738],{},"    From = currentWidth,    \u002F\u002F 起始值\n",[20,2740,2741],{"class":70,"line":442},[20,2742,2743],{},"    To = TargetWidth,       \u002F\u002F 目标值\n",[20,2745,2746],{"class":70,"line":447},[20,2747,2748],{},"    Duration = Duration\n",[20,2750,2751],{"class":70,"line":452},[20,2752,2554],{},[14,2754,2755,2761],{},[17,2756,2757,2760],{},[20,2758,2759],{},"!info"," 为什么需要处理 NaN？",[218,2762,2763,2777,2782],{},[213,2764,2765,2766,2769,2770,2773,2774],{},"WPF 中，如果 ",[49,2767,2768],{},"Width"," 或 ",[49,2771,2772],{},"Height"," 未设置，值为 ",[49,2775,2776],{},"NaN",[213,2778,2779,2781],{},[49,2780,904],{}," 总是返回实际渲染尺寸",[213,2783,2459,2784,2786],{},[49,2785,904],{}," 作为当前值，确保动画从正确的位置开始",[42,2788,2790],{"id":2789},"_3-动画执行流程","3. 动画执行流程",[60,2792,2795],{"className":2793,"code":2794,"language":203},[201],"1. PropertyChangedTrigger 检测到属性变化\n   ↓\n2. 触发 AnimateToTargetSizeAction.Invoke()\n   ↓\n3. 获取目标元素的当前尺寸\n   ↓\n4. 计算差值（目标值 - 当前值）\n   ↓\n5. 创建 DoubleAnimation（From = 当前值, To = 目标值）\n   ↓\n6. 应用 CubicEase 缓动函数\n   ↓\n7. 创建 Storyboard 并添加动画\n   ↓\n8. 执行动画，平滑过渡\n",[49,2796,2794],{"__ignoreMap":65},[34,2798],{},[37,2800,2802],{"id":2801},"二阶缓动动画详解","🎨 二阶缓动动画详解",[42,2804,2806],{"id":2805},"什么是二阶缓动","什么是二阶缓动？",[17,2808,2809,2810,2813],{},"二阶缓动（Second-Order Easing）是指动画的",[25,2811,2812],{},"加速度","也会变化，产生更自然的运动效果。",[268,2815,2816],{"id":2816},"缓动函数对比",[115,2818,2819,2831],{},[118,2820,2821],{},[121,2822,2823,2826,2829],{},[124,2824,2825],{},"缓动类型",[124,2827,2828],{},"描述",[124,2830,182],{},[134,2832,2833,2846,2859,2872,2885],{},[121,2834,2835,2840,2843],{},[139,2836,2837],{},[25,2838,2839],{},"Linear",[139,2841,2842],{},"匀速运动",[139,2844,2845],{},"简单过渡",[121,2847,2848,2853,2856],{},[139,2849,2850],{},[25,2851,2852],{},"QuadraticEase",[139,2854,2855],{},"二次方缓动",[139,2857,2858],{},"轻微加速\u002F减速",[121,2860,2861,2866,2869],{},[139,2862,2863],{},[25,2864,2865],{},"CubicEase",[139,2867,2868],{},"三次方缓动（二阶）",[139,2870,2871],{},"平滑的加速和减速",[121,2873,2874,2879,2882],{},[139,2875,2876],{},[25,2877,2878],{},"ElasticEase",[139,2880,2881],{},"弹性效果",[139,2883,2884],{},"需要弹跳效果",[121,2886,2887,2892,2895],{},[139,2888,2889],{},[25,2890,2891],{},"BounceEase",[139,2893,2894],{},"弹跳效果",[139,2896,2884],{},[268,2898,2900],{"id":2899},"cubicease-的数学原理","CubicEase 的数学原理",[17,2902,2903],{},"$$\nf(t) = t^3\n$$",[17,2905,1733],{},[218,2907,2908,2911],{},[213,2909,2910],{},"$t$ 是归一化的时间（0 到 1）",[213,2912,2913],{},"$f(t)$ 是归一化的进度（0 到 1）",[17,2915,2916,2918],{},[25,2917,1108],{}," 模式：",[218,2920,2921,2924,2927],{},[213,2922,2923],{},"开始：缓慢加速（$t^3$ 在 0 附近增长慢）",[213,2925,2926],{},"中间：快速变化（$t^3$ 在 0.5 附近增长快）",[213,2928,2929],{},"结束：缓慢减速（$t^3$ 在 1 附近增长慢）",[268,2931,2932],{"id":2932},"视觉效果对比",[60,2934,2937],{"className":2935,"code":2936,"language":203},[201],"Linear（线性）:\n位置: 0 ──────────────── 100\n时间: 0 ──────────────── 1.0\n\nCubicEase EaseInOut（二阶缓动）:\n位置: 0 ────┐         ┌──── 100\n时间: 0 ────┘         └──── 1.0\n      (慢)  (快)  (快)  (慢)\n",[49,2938,2936],{"__ignoreMap":65},[34,2940],{},[37,2942,2944],{"id":2943},"高级用法","🚀 高级用法",[42,2946,2948],{"id":2947},"_1-自定义动画持续时间","1. 自定义动画持续时间",[60,2950,2952],{"className":1127,"code":2951,"language":1129,"meta":65,"style":65},"\u003Clocal:AnimateToTargetSizeAction \n    Duration=\"0:0:0.5\"\u002F>  \u003C!-- 0.5 秒 -->\n",[49,2953,2954,2959],{"__ignoreMap":65},[20,2955,2956],{"class":70,"line":71},[20,2957,2958],{},"\u003Clocal:AnimateToTargetSizeAction \n",[20,2960,2961],{"class":70,"line":77},[20,2962,2963],{},"    Duration=\"0:0:0.5\"\u002F>  \u003C!-- 0.5 秒 -->\n",[42,2965,2967],{"id":2966},"_2-自定义缓动函数","2. 自定义缓动函数",[17,2969,2970,2971,2973],{},"现在可以通过 ",[49,2972,655],{}," 依赖属性在 XAML 中自定义缓动函数：",[268,2975,2977],{"id":2976},"方式-1在-xaml-中设置","方式 1：在 XAML 中设置",[60,2979,2981],{"className":1127,"code":2980,"language":1129,"meta":65,"style":65},"\u003Clocal:AnimateToTargetSizeAction \n    TargetObject=\"{Binding ElementName=AnimatedBorder}\"\n    TargetWidth=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n    TargetHeight=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n    Duration=\"0:0:0.2\">\n    \u003Clocal:AnimateToTargetSizeAction.EasingFunction>\n        \u003CElasticEase EasingMode=\"EaseOut\" Oscillations=\"2\" Springiness=\"3\"\u002F>\n    \u003C\u002Flocal:AnimateToTargetSizeAction.EasingFunction>\n\u003C\u002Flocal:AnimateToTargetSizeAction>\n",[49,2982,2983,2987,2992,2997,3002,3007,3012,3017,3022],{"__ignoreMap":65},[20,2984,2985],{"class":70,"line":71},[20,2986,2958],{},[20,2988,2989],{"class":70,"line":77},[20,2990,2991],{},"    TargetObject=\"{Binding ElementName=AnimatedBorder}\"\n",[20,2993,2994],{"class":70,"line":83},[20,2995,2996],{},"    TargetWidth=\"{Binding TargetWidth, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n",[20,2998,2999],{"class":70,"line":89},[20,3000,3001],{},"    TargetHeight=\"{Binding TargetHeight, RelativeSource={RelativeSource AncestorType=UserControl}}\"\n",[20,3003,3004],{"class":70,"line":95},[20,3005,3006],{},"    Duration=\"0:0:0.2\">\n",[20,3008,3009],{"class":70,"line":101},[20,3010,3011],{},"    \u003Clocal:AnimateToTargetSizeAction.EasingFunction>\n",[20,3013,3014],{"class":70,"line":107},[20,3015,3016],{},"        \u003CElasticEase EasingMode=\"EaseOut\" Oscillations=\"2\" Springiness=\"3\"\u002F>\n",[20,3018,3019],{"class":70,"line":315},[20,3020,3021],{},"    \u003C\u002Flocal:AnimateToTargetSizeAction.EasingFunction>\n",[20,3023,3024],{"class":70,"line":321},[20,3025,3026],{},"\u003C\u002Flocal:AnimateToTargetSizeAction>\n",[268,3028,3030],{"id":3029},"方式-2使用资源","方式 2：使用资源",[60,3032,3034],{"className":1127,"code":3033,"language":1129,"meta":65,"style":65},"\u003CUserControl.Resources>\n    \u003CElasticEase x:Key=\"MyEasing\" \n                 EasingMode=\"EaseOut\" \n                 Oscillations=\"2\" \n                 Springiness=\"3\"\u002F>\n\u003C\u002FUserControl.Resources>\n\n\u003Clocal:AnimateToTargetSizeAction \n    EasingFunction=\"{StaticResource MyEasing}\"\n    ...\u002F>\n",[49,3035,3036,3041,3046,3051,3056,3061,3066,3070,3074,3079],{"__ignoreMap":65},[20,3037,3038],{"class":70,"line":71},[20,3039,3040],{},"\u003CUserControl.Resources>\n",[20,3042,3043],{"class":70,"line":77},[20,3044,3045],{},"    \u003CElasticEase x:Key=\"MyEasing\" \n",[20,3047,3048],{"class":70,"line":83},[20,3049,3050],{},"                 EasingMode=\"EaseOut\" \n",[20,3052,3053],{"class":70,"line":89},[20,3054,3055],{},"                 Oscillations=\"2\" \n",[20,3057,3058],{"class":70,"line":95},[20,3059,3060],{},"                 Springiness=\"3\"\u002F>\n",[20,3062,3063],{"class":70,"line":101},[20,3064,3065],{},"\u003C\u002FUserControl.Resources>\n",[20,3067,3068],{"class":70,"line":107},[20,3069,303],{"emptyLinePlaceholder":302},[20,3071,3072],{"class":70,"line":315},[20,3073,2958],{},[20,3075,3076],{"class":70,"line":321},[20,3077,3078],{},"    EasingFunction=\"{StaticResource MyEasing}\"\n",[20,3080,3081],{"class":70,"line":327},[20,3082,3083],{},"    ...\u002F>\n",[268,3085,3087],{"id":3086},"方式-3在代码中设置","方式 3：在代码中设置",[60,3089,3091],{"className":273,"code":3090,"language":275,"meta":65,"style":65},"var action = new AnimateToTargetSizeAction\n{\n    EasingFunction = new ElasticEase\n    {\n        EasingMode = EasingMode.EaseOut,\n        Oscillations = 2,\n        Springiness = 3\n    }\n};\n",[49,3092,3093,3098,3102,3107,3111,3116,3121,3126,3130],{"__ignoreMap":65},[20,3094,3095],{"class":70,"line":71},[20,3096,3097],{},"var action = new AnimateToTargetSizeAction\n",[20,3099,3100],{"class":70,"line":77},[20,3101,342],{},[20,3103,3104],{"class":70,"line":83},[20,3105,3106],{},"    EasingFunction = new ElasticEase\n",[20,3108,3109],{"class":70,"line":89},[20,3110,569],{},[20,3112,3113],{"class":70,"line":95},[20,3114,3115],{},"        EasingMode = EasingMode.EaseOut,\n",[20,3117,3118],{"class":70,"line":101},[20,3119,3120],{},"        Oscillations = 2,\n",[20,3122,3123],{"class":70,"line":107},[20,3124,3125],{},"        Springiness = 3\n",[20,3127,3128],{"class":70,"line":315},[20,3129,587],{},[20,3131,3132],{"class":70,"line":321},[20,3133,2554],{},[268,3135,3136],{"id":3136},"可用的缓动函数类型",[115,3138,3139,3151],{},[118,3140,3141],{},[121,3142,3143,3146,3149],{},[124,3144,3145],{},"缓动函数",[124,3147,3148],{},"效果",[124,3150,182],{},[134,3152,3153,3165,3176,3186,3196,3209,3222,3235,3248],{},[121,3154,3155,3160,3162],{},[139,3156,3157,3159],{},[25,3158,2865],{},"（默认）",[139,3161,2871],{},[139,3163,3164],{},"通用动画",[121,3166,3167,3171,3174],{},[139,3168,3169],{},[25,3170,2852],{},[139,3172,3173],{},"轻微的加速\u002F减速",[139,3175,2845],{},[121,3177,3178,3182,3184],{},[139,3179,3180],{},[25,3181,2878],{},[139,3183,2881],{},[139,3185,2884],{},[121,3187,3188,3192,3194],{},[139,3189,3190],{},[25,3191,2891],{},[139,3193,2894],{},[139,3195,2884],{},[121,3197,3198,3203,3206],{},[139,3199,3200],{},[25,3201,3202],{},"BackEase",[139,3204,3205],{},"回弹效果",[139,3207,3208],{},"需要回弹效果",[121,3210,3211,3216,3219],{},[139,3212,3213],{},[25,3214,3215],{},"CircleEase",[139,3217,3218],{},"圆形曲线",[139,3220,3221],{},"特殊效果",[121,3223,3224,3229,3232],{},[139,3225,3226],{},[25,3227,3228],{},"ExponentialEase",[139,3230,3231],{},"指数曲线",[139,3233,3234],{},"快速变化",[121,3236,3237,3242,3245],{},[139,3238,3239],{},[25,3240,3241],{},"PowerEase",[139,3243,3244],{},"幂函数曲线",[139,3246,3247],{},"可自定义幂次",[121,3249,3250,3255,3258],{},[139,3251,3252],{},[25,3253,3254],{},"SineEase",[139,3256,3257],{},"正弦曲线",[139,3259,3260],{},"平滑波动",[42,3262,3264],{"id":3263},"_3-动画多个属性","3. 动画多个属性",[17,3266,3267],{},"可以扩展 Action 来动画化其他属性：",[60,3269,3271],{"className":273,"code":3270,"language":275,"meta":65,"style":65},"\u002F\u002F 添加目标透明度\npublic double TargetOpacity\n{\n    get => (double)GetValue(TargetOpacityProperty);\n    set => SetValue(TargetOpacityProperty, value);\n}\n\n\u002F\u002F 在 Invoke 方法中添加\nif (!double.IsNaN(TargetOpacity))\n{\n    var opacityAnimation = CreateSecondOrderAnimation(\n        target.Opacity, TargetOpacity);\n    Storyboard.SetTargetProperty(opacityAnimation, \n        new PropertyPath(UIElement.OpacityProperty));\n    storyboard.Children.Add(opacityAnimation);\n}\n",[49,3272,3273,3278,3283,3287,3292,3297,3301,3305,3310,3315,3319,3324,3329,3334,3339,3344],{"__ignoreMap":65},[20,3274,3275],{"class":70,"line":71},[20,3276,3277],{},"\u002F\u002F 添加目标透明度\n",[20,3279,3280],{"class":70,"line":77},[20,3281,3282],{},"public double TargetOpacity\n",[20,3284,3285],{"class":70,"line":83},[20,3286,342],{},[20,3288,3289],{"class":70,"line":89},[20,3290,3291],{},"    get => (double)GetValue(TargetOpacityProperty);\n",[20,3293,3294],{"class":70,"line":95},[20,3295,3296],{},"    set => SetValue(TargetOpacityProperty, value);\n",[20,3298,3299],{"class":70,"line":101},[20,3300,354],{},[20,3302,3303],{"class":70,"line":107},[20,3304,303],{"emptyLinePlaceholder":302},[20,3306,3307],{"class":70,"line":315},[20,3308,3309],{},"\u002F\u002F 在 Invoke 方法中添加\n",[20,3311,3312],{"class":70,"line":321},[20,3313,3314],{},"if (!double.IsNaN(TargetOpacity))\n",[20,3316,3317],{"class":70,"line":327},[20,3318,342],{},[20,3320,3321],{"class":70,"line":333},[20,3322,3323],{},"    var opacityAnimation = CreateSecondOrderAnimation(\n",[20,3325,3326],{"class":70,"line":339},[20,3327,3328],{},"        target.Opacity, TargetOpacity);\n",[20,3330,3331],{"class":70,"line":345},[20,3332,3333],{},"    Storyboard.SetTargetProperty(opacityAnimation, \n",[20,3335,3336],{"class":70,"line":351},[20,3337,3338],{},"        new PropertyPath(UIElement.OpacityProperty));\n",[20,3340,3341],{"class":70,"line":430},[20,3342,3343],{},"    storyboard.Children.Add(opacityAnimation);\n",[20,3345,3346],{"class":70,"line":436},[20,3347,354],{},[42,3349,3351],{"id":3350},"_4-条件动画","4. 条件动画",[17,3353,3354],{},"只在满足条件时执行动画：",[60,3356,3358],{"className":273,"code":3357,"language":275,"meta":65,"style":65},"protected override void Invoke(object parameter)\n{\n    \u002F\u002F 检查条件\n    if (ShouldAnimate == false) return;\n    \n    \u002F\u002F 执行动画逻辑\n    \u002F\u002F ...\n}\n",[49,3359,3360,3364,3368,3373,3378,3382,3387,3392],{"__ignoreMap":65},[20,3361,3362],{"class":70,"line":71},[20,3363,669],{},[20,3365,3366],{"class":70,"line":77},[20,3367,342],{},[20,3369,3370],{"class":70,"line":83},[20,3371,3372],{},"    \u002F\u002F 检查条件\n",[20,3374,3375],{"class":70,"line":89},[20,3376,3377],{},"    if (ShouldAnimate == false) return;\n",[20,3379,3380],{"class":70,"line":95},[20,3381,551],{},[20,3383,3384],{"class":70,"line":101},[20,3385,3386],{},"    \u002F\u002F 执行动画逻辑\n",[20,3388,3389],{"class":70,"line":107},[20,3390,3391],{},"    \u002F\u002F ...\n",[20,3393,3394],{"class":70,"line":315},[20,3395,354],{},[34,3397],{},[37,3399,3401],{"id":3400},"️-注意事项和最佳实践","⚠️ 注意事项和最佳实践",[42,3403,3405],{"id":3404},"_1-性能考虑","1. 性能考虑",[14,3407,3408,3414],{},[17,3409,3410,3413],{},[20,3411,3412],{},"!warning"," 性能提示",[218,3415,3416,3423,3426],{},[213,3417,3418,3419,3422],{},"PropertyChangedTrigger 会在",[25,3420,3421],{},"每次属性变化","时触发",[213,3424,3425],{},"如果属性频繁变化，可能导致动画冲突",[213,3427,3428],{},"建议添加阈值检查，避免不必要的动画",[60,3430,3432],{"className":273,"code":3431,"language":275,"meta":65,"style":65},"\u002F\u002F ✅ 好的做法：检查差值是否足够大\nif (Math.Abs(TargetWidth - currentWidth) > 0.01)\n{\n    \u002F\u002F 创建动画\n}\n\n\u002F\u002F ❌ 不好的做法：每次都创建动画\n\u002F\u002F 即使差值很小也会创建动画，浪费资源\n",[49,3433,3434,3439,3444,3448,3453,3457,3461,3466],{"__ignoreMap":65},[20,3435,3436],{"class":70,"line":71},[20,3437,3438],{},"\u002F\u002F ✅ 好的做法：检查差值是否足够大\n",[20,3440,3441],{"class":70,"line":77},[20,3442,3443],{},"if (Math.Abs(TargetWidth - currentWidth) > 0.01)\n",[20,3445,3446],{"class":70,"line":83},[20,3447,342],{},[20,3449,3450],{"class":70,"line":89},[20,3451,3452],{},"    \u002F\u002F 创建动画\n",[20,3454,3455],{"class":70,"line":95},[20,3456,354],{},[20,3458,3459],{"class":70,"line":101},[20,3460,303],{"emptyLinePlaceholder":302},[20,3462,3463],{"class":70,"line":107},[20,3464,3465],{},"\u002F\u002F ❌ 不好的做法：每次都创建动画\n",[20,3467,3468],{"class":70,"line":315},[20,3469,3470],{},"\u002F\u002F 即使差值很小也会创建动画，浪费资源\n",[42,3472,3474],{"id":3473},"_2-动画冲突处理","2. 动画冲突处理",[17,3476,3477],{},"如果动画正在进行时属性再次改变：",[60,3479,3481],{"className":273,"code":3480,"language":275,"meta":65,"style":65},"protected override void Invoke(object parameter)\n{\n    var target = TargetObject ?? AssociatedObject;\n    if (target == null) return;\n    \n    \u002F\u002F 停止当前正在进行的动画\n    var currentStoryboard = target.GetValue(StoryboardProperty) as Storyboard;\n    if (currentStoryboard != null)\n    {\n        currentStoryboard.Stop(target);\n    }\n    \n    \u002F\u002F 创建新动画\n    \u002F\u002F ...\n    \n    \u002F\u002F 保存 Storyboard 引用\n    target.SetValue(StoryboardProperty, storyboard);\n}\n",[49,3482,3483,3487,3491,3495,3499,3503,3508,3513,3518,3522,3527,3531,3535,3540,3544,3548,3553,3558],{"__ignoreMap":65},[20,3484,3485],{"class":70,"line":71},[20,3486,669],{},[20,3488,3489],{"class":70,"line":77},[20,3490,342],{},[20,3492,3493],{"class":70,"line":83},[20,3494,678],{},[20,3496,3497],{"class":70,"line":89},[20,3498,683],{},[20,3500,3501],{"class":70,"line":95},[20,3502,551],{},[20,3504,3505],{"class":70,"line":101},[20,3506,3507],{},"    \u002F\u002F 停止当前正在进行的动画\n",[20,3509,3510],{"class":70,"line":107},[20,3511,3512],{},"    var currentStoryboard = target.GetValue(StoryboardProperty) as Storyboard;\n",[20,3514,3515],{"class":70,"line":315},[20,3516,3517],{},"    if (currentStoryboard != null)\n",[20,3519,3520],{"class":70,"line":321},[20,3521,569],{},[20,3523,3524],{"class":70,"line":327},[20,3525,3526],{},"        currentStoryboard.Stop(target);\n",[20,3528,3529],{"class":70,"line":333},[20,3530,587],{},[20,3532,3533],{"class":70,"line":339},[20,3534,551],{},[20,3536,3537],{"class":70,"line":345},[20,3538,3539],{},"    \u002F\u002F 创建新动画\n",[20,3541,3542],{"class":70,"line":351},[20,3543,3391],{},[20,3545,3546],{"class":70,"line":430},[20,3547,551],{},[20,3549,3550],{"class":70,"line":436},[20,3551,3552],{},"    \u002F\u002F 保存 Storyboard 引用\n",[20,3554,3555],{"class":70,"line":442},[20,3556,3557],{},"    target.SetValue(StoryboardProperty, storyboard);\n",[20,3559,3560],{"class":70,"line":447},[20,3561,354],{},[42,3563,3565],{"id":3564},"_3-内存泄漏预防","3. 内存泄漏预防",[14,3567,3568,3573],{},[17,3569,3570,3572],{},[20,3571,877],{}," 内存管理",[218,3574,3575,3578,3581],{},[213,3576,3577],{},"及时停止不需要的动画",[213,3579,3580],{},"避免在事件处理中创建大量对象",[213,3582,2459,3583,3586],{},[49,3584,3585],{},"WeakReference"," 如果需要在动画完成后访问对象",[42,3588,3590],{"id":3589},"_4-数据绑定最佳实践","4. 数据绑定最佳实践",[60,3592,3594],{"className":1127,"code":3593,"language":1129,"meta":65,"style":65},"\u003C!-- ✅ 好的做法：使用 TwoWay 绑定 -->\n\u003CSlider Value=\"{Binding TargetWidth, Mode=TwoWay}\"\u002F>\n\n\u003C!-- ❌ 不好的做法：只使用 OneWay 绑定 -->\n\u003CSlider Value=\"{Binding TargetWidth}\"\u002F>\n",[49,3595,3596,3601,3606,3610,3615],{"__ignoreMap":65},[20,3597,3598],{"class":70,"line":71},[20,3599,3600],{},"\u003C!-- ✅ 好的做法：使用 TwoWay 绑定 -->\n",[20,3602,3603],{"class":70,"line":77},[20,3604,3605],{},"\u003CSlider Value=\"{Binding TargetWidth, Mode=TwoWay}\"\u002F>\n",[20,3607,3608],{"class":70,"line":83},[20,3609,303],{"emptyLinePlaceholder":302},[20,3611,3612],{"class":70,"line":89},[20,3613,3614],{},"\u003C!-- ❌ 不好的做法：只使用 OneWay 绑定 -->\n",[20,3616,3617],{"class":70,"line":95},[20,3618,3619],{},"\u003CSlider Value=\"{Binding TargetWidth}\"\u002F>\n",[34,3621],{},[37,3623,3625],{"id":3624},"使用场景","📊 使用场景",[42,3627,3629],{"id":3628},"_1-响应式布局","1. 响应式布局",[17,3631,3632],{},"当窗口大小改变时，自动调整控件尺寸：",[60,3634,3636],{"className":1127,"code":3635,"language":1129,"meta":65,"style":65},"\u003CBorder x:Name=\"ResponsiveBorder\">\n    \u003Ci:Interaction.Triggers>\n        \u003Ci:PropertyChangedTrigger \n            Binding=\"{Binding WindowWidth, RelativeSource={RelativeSource AncestorType=Window}}\">\n            \u003Clocal:AnimateToTargetSizeAction \n                TargetWidth=\"{Binding CalculatedWidth}\"\u002F>\n        \u003C\u002Fi:PropertyChangedTrigger>\n    \u003C\u002Fi:Interaction.Triggers>\n\u003C\u002FBorder>\n",[49,3637,3638,3643,3647,3651,3656,3660,3665,3669,3673],{"__ignoreMap":65},[20,3639,3640],{"class":70,"line":71},[20,3641,3642],{},"\u003CBorder x:Name=\"ResponsiveBorder\">\n",[20,3644,3645],{"class":70,"line":77},[20,3646,1141],{},[20,3648,3649],{"class":70,"line":83},[20,3650,1151],{},[20,3652,3653],{"class":70,"line":89},[20,3654,3655],{},"            Binding=\"{Binding WindowWidth, RelativeSource={RelativeSource AncestorType=Window}}\">\n",[20,3657,3658],{"class":70,"line":95},[20,3659,1161],{},[20,3661,3662],{"class":70,"line":101},[20,3663,3664],{},"                TargetWidth=\"{Binding CalculatedWidth}\"\u002F>\n",[20,3666,3667],{"class":70,"line":107},[20,3668,1186],{},[20,3670,3671],{"class":70,"line":315},[20,3672,1234],{},[20,3674,3675],{"class":70,"line":321},[20,3676,1239],{},[42,3678,3680],{"id":3679},"_2-数据驱动的-ui-动画","2. 数据驱动的 UI 动画",[17,3682,3683],{},"当数据模型改变时，UI 自动动画化：",[60,3685,3687],{"className":1127,"code":3686,"language":1129,"meta":65,"style":65},"\u003CItemsControl ItemsSource=\"{Binding Items}\">\n    \u003CItemsControl.ItemTemplate>\n        \u003CDataTemplate>\n            \u003CBorder>\n                \u003Ci:Interaction.Triggers>\n                    \u003Ci:PropertyChangedTrigger \n                        Binding=\"{Binding IsExpanded}\">\n                        \u003Clocal:AnimateToTargetSizeAction \n                            TargetHeight=\"{Binding ExpandedHeight}\"\u002F>\n                    \u003C\u002Fi:PropertyChangedTrigger>\n                \u003C\u002Fi:Interaction.Triggers>\n            \u003C\u002FBorder>\n        \u003C\u002FDataTemplate>\n    \u003C\u002FItemsControl.ItemTemplate>\n\u003C\u002FItemsControl>\n",[49,3688,3689,3694,3699,3704,3709,3714,3719,3724,3729,3734,3739,3744,3749,3754,3759],{"__ignoreMap":65},[20,3690,3691],{"class":70,"line":71},[20,3692,3693],{},"\u003CItemsControl ItemsSource=\"{Binding Items}\">\n",[20,3695,3696],{"class":70,"line":77},[20,3697,3698],{},"    \u003CItemsControl.ItemTemplate>\n",[20,3700,3701],{"class":70,"line":83},[20,3702,3703],{},"        \u003CDataTemplate>\n",[20,3705,3706],{"class":70,"line":89},[20,3707,3708],{},"            \u003CBorder>\n",[20,3710,3711],{"class":70,"line":95},[20,3712,3713],{},"                \u003Ci:Interaction.Triggers>\n",[20,3715,3716],{"class":70,"line":101},[20,3717,3718],{},"                    \u003Ci:PropertyChangedTrigger \n",[20,3720,3721],{"class":70,"line":107},[20,3722,3723],{},"                        Binding=\"{Binding IsExpanded}\">\n",[20,3725,3726],{"class":70,"line":315},[20,3727,3728],{},"                        \u003Clocal:AnimateToTargetSizeAction \n",[20,3730,3731],{"class":70,"line":321},[20,3732,3733],{},"                            TargetHeight=\"{Binding ExpandedHeight}\"\u002F>\n",[20,3735,3736],{"class":70,"line":327},[20,3737,3738],{},"                    \u003C\u002Fi:PropertyChangedTrigger>\n",[20,3740,3741],{"class":70,"line":333},[20,3742,3743],{},"                \u003C\u002Fi:Interaction.Triggers>\n",[20,3745,3746],{"class":70,"line":339},[20,3747,3748],{},"            \u003C\u002FBorder>\n",[20,3750,3751],{"class":70,"line":345},[20,3752,3753],{},"        \u003C\u002FDataTemplate>\n",[20,3755,3756],{"class":70,"line":351},[20,3757,3758],{},"    \u003C\u002FItemsControl.ItemTemplate>\n",[20,3760,3761],{"class":70,"line":430},[20,3762,3763],{},"\u003C\u002FItemsControl>\n",[42,3765,3767],{"id":3766},"_3-用户交互反馈","3. 用户交互反馈",[17,3769,3770],{},"当用户操作时，提供视觉反馈：",[60,3772,3774],{"className":1127,"code":3773,"language":1129,"meta":65,"style":65},"\u003CButton Command=\"{Binding ToggleCommand}\">\n    \u003Ci:Interaction.Triggers>\n        \u003Ci:PropertyChangedTrigger \n            Binding=\"{Binding IsToggled}\">\n            \u003Clocal:AnimateToTargetSizeAction \n                TargetWidth=\"{Binding ToggledWidth}\"\u002F>\n        \u003C\u002Fi:PropertyChangedTrigger>\n    \u003C\u002Fi:Interaction.Triggers>\n\u003C\u002FButton>\n",[49,3775,3776,3781,3785,3789,3794,3798,3803,3807,3811],{"__ignoreMap":65},[20,3777,3778],{"class":70,"line":71},[20,3779,3780],{},"\u003CButton Command=\"{Binding ToggleCommand}\">\n",[20,3782,3783],{"class":70,"line":77},[20,3784,1141],{},[20,3786,3787],{"class":70,"line":83},[20,3788,1151],{},[20,3790,3791],{"class":70,"line":89},[20,3792,3793],{},"            Binding=\"{Binding IsToggled}\">\n",[20,3795,3796],{"class":70,"line":95},[20,3797,1161],{},[20,3799,3800],{"class":70,"line":101},[20,3801,3802],{},"                TargetWidth=\"{Binding ToggledWidth}\"\u002F>\n",[20,3804,3805],{"class":70,"line":107},[20,3806,1186],{},[20,3808,3809],{"class":70,"line":315},[20,3810,1234],{},[20,3812,3813],{"class":70,"line":321},[20,3814,3815],{},"\u003C\u002FButton>\n",[34,3817],{},[37,3819,3820],{"id":2416},"🔧 调试技巧",[42,3822,3824],{"id":3823},"_1-添加日志","1. 添加日志",[60,3826,3828],{"className":273,"code":3827,"language":275,"meta":65,"style":65},"protected override void Invoke(object parameter)\n{\n    Debug.WriteLine($\"AnimateToTargetSizeAction invoked\");\n    Debug.WriteLine($\"TargetWidth: {TargetWidth}, TargetHeight: {TargetHeight}\");\n    \n    \u002F\u002F ... 动画逻辑\n}\n",[49,3829,3830,3834,3838,3843,3848,3852,3857],{"__ignoreMap":65},[20,3831,3832],{"class":70,"line":71},[20,3833,669],{},[20,3835,3836],{"class":70,"line":77},[20,3837,342],{},[20,3839,3840],{"class":70,"line":83},[20,3841,3842],{},"    Debug.WriteLine($\"AnimateToTargetSizeAction invoked\");\n",[20,3844,3845],{"class":70,"line":89},[20,3846,3847],{},"    Debug.WriteLine($\"TargetWidth: {TargetWidth}, TargetHeight: {TargetHeight}\");\n",[20,3849,3850],{"class":70,"line":95},[20,3851,551],{},[20,3853,3854],{"class":70,"line":101},[20,3855,3856],{},"    \u002F\u002F ... 动画逻辑\n",[20,3858,3859],{"class":70,"line":107},[20,3860,354],{},[42,3862,3864],{"id":3863},"_2-检查绑定","2. 检查绑定",[60,3866,3868],{"className":1127,"code":3867,"language":1129,"meta":65,"style":65},"\u003C!-- 添加 TextBlock 显示当前值，用于调试 -->\n\u003CTextBlock Text=\"{Binding TargetWidth, StringFormat='Width: {0}'}\"\u002F>\n",[49,3869,3870,3875],{"__ignoreMap":65},[20,3871,3872],{"class":70,"line":71},[20,3873,3874],{},"\u003C!-- 添加 TextBlock 显示当前值，用于调试 -->\n",[20,3876,3877],{"class":70,"line":77},[20,3878,3879],{},"\u003CTextBlock Text=\"{Binding TargetWidth, StringFormat='Width: {0}'}\"\u002F>\n",[42,3881,3883],{"id":3882},"_3-使用-visual-studio-的-xaml-绑定调试","3. 使用 Visual Studio 的 XAML 绑定调试",[17,3885,3886],{},"在输出窗口中查看绑定错误和警告。",[34,3888],{},[37,3890,3892],{"id":3891},"完整代码示例","📝 完整代码示例",[42,3894,3896],{"id":3895},"animatetotargetsizeactioncs","AnimateToTargetSizeAction.cs",[60,3898,3900],{"className":273,"code":3899,"language":275,"meta":65,"style":65},"using System;\nusing System.Windows;\nusing System.Windows.Media.Animation;\nusing Microsoft.Xaml.Behaviors;\n\nnamespace FluentScrollviewerSolution;\n\npublic class AnimateToTargetSizeAction : TriggerAction\u003CFrameworkElement>\n{\n    public FrameworkElement TargetObject\n    {\n        get => (FrameworkElement)GetValue(TargetObjectProperty);\n        set => SetValue(TargetObjectProperty, value);\n    }\n    \n    public static readonly DependencyProperty TargetObjectProperty =\n        DependencyProperty.Register(nameof(TargetObject), typeof(FrameworkElement), \n            typeof(AnimateToTargetSizeAction), new PropertyMetadata(null));\n    \n    public double TargetWidth\n    {\n        get => (double)GetValue(TargetWidthProperty);\n        set => SetValue(TargetWidthProperty, value);\n    }\n    \n    public static readonly DependencyProperty TargetWidthProperty =\n        DependencyProperty.Register(nameof(TargetWidth), typeof(double), \n            typeof(AnimateToTargetSizeAction), new PropertyMetadata(double.NaN));\n    \n    public double TargetHeight\n    {\n        get => (double)GetValue(TargetHeightProperty);\n        set => SetValue(TargetHeightProperty, value);\n    }\n    \n    public static readonly DependencyProperty TargetHeightProperty =\n        DependencyProperty.Register(nameof(TargetHeight), typeof(double), \n            typeof(AnimateToTargetSizeAction), new PropertyMetadata(double.NaN));\n    \n    public Duration Duration { get; set; } = new Duration(TimeSpan.FromSeconds(0.2));\n    \n    protected override void Invoke(object parameter)\n    {\n        var target = TargetObject ?? AssociatedObject;\n        if (target == null) return;\n        \n        double currentWidth = target.Width;\n        double currentHeight = target.Height;\n        \n        if (double.IsNaN(currentWidth) || currentWidth == 0)\n            currentWidth = target.ActualWidth;\n        if (double.IsNaN(currentHeight) || currentHeight == 0)\n            currentHeight = target.ActualHeight;\n        \n        var storyboard = new Storyboard();\n        \n        if (!double.IsNaN(TargetWidth) && Math.Abs(TargetWidth - currentWidth) > 0.01)\n        {\n            var widthAnimation = CreateSecondOrderAnimation(currentWidth, TargetWidth);\n            Storyboard.SetTarget(widthAnimation, target);\n            Storyboard.SetTargetProperty(widthAnimation, \n                new PropertyPath(FrameworkElement.WidthProperty));\n            storyboard.Children.Add(widthAnimation);\n        }\n        \n        if (!double.IsNaN(TargetHeight) && Math.Abs(TargetHeight - currentHeight) > 0.01)\n        {\n            var heightAnimation = CreateSecondOrderAnimation(currentHeight, TargetHeight);\n            Storyboard.SetTarget(heightAnimation, target);\n            Storyboard.SetTargetProperty(heightAnimation, \n                new PropertyPath(FrameworkElement.HeightProperty));\n            storyboard.Children.Add(heightAnimation);\n        }\n        \n        if (storyboard.Children.Count > 0)\n        {\n            storyboard.Begin(target);\n        }\n    }\n    \n    private DoubleAnimation CreateSecondOrderAnimation(double from, double to)\n    {\n        return new DoubleAnimation\n        {\n            From = from,\n            To = to,\n            Duration = Duration,\n            EasingFunction = new CubicEase\n            {\n                EasingMode = EasingMode.EaseInOut\n            }\n        };\n    }\n}\n",[49,3901,3902,3906,3910,3914,3918,3922,3926,3930,3934,3938,3943,3947,3952,3957,3961,3965,3970,3975,3980,3984,3988,3992,3996,4000,4004,4008,4012,4017,4022,4026,4030,4034,4038,4042,4046,4050,4054,4059,4063,4067,4071,4075,4080,4084,4089,4094,4098,4103,4108,4112,4118,4124,4130,4136,4141,4147,4152,4158,4163,4169,4175,4181,4187,4193,4198,4203,4209,4214,4220,4226,4232,4238,4244,4249,4254,4260,4265,4271,4276,4281,4286,4292,4297,4303,4308,4314,4320,4326,4332,4338,4344,4350,4356,4361],{"__ignoreMap":65},[20,3903,3904],{"class":70,"line":71},[20,3905,282],{},[20,3907,3908],{"class":70,"line":77},[20,3909,287],{},[20,3911,3912],{"class":70,"line":83},[20,3913,292],{},[20,3915,3916],{"class":70,"line":89},[20,3917,297],{},[20,3919,3920],{"class":70,"line":95},[20,3921,303],{"emptyLinePlaceholder":302},[20,3923,3924],{"class":70,"line":101},[20,3925,308],{},[20,3927,3928],{"class":70,"line":107},[20,3929,303],{"emptyLinePlaceholder":302},[20,3931,3932],{"class":70,"line":315},[20,3933,336],{},[20,3935,3936],{"class":70,"line":321},[20,3937,342],{},[20,3939,3940],{"class":70,"line":327},[20,3941,3942],{},"    public FrameworkElement TargetObject\n",[20,3944,3945],{"class":70,"line":333},[20,3946,569],{},[20,3948,3949],{"class":70,"line":339},[20,3950,3951],{},"        get => (FrameworkElement)GetValue(TargetObjectProperty);\n",[20,3953,3954],{"class":70,"line":345},[20,3955,3956],{},"        set => SetValue(TargetObjectProperty, value);\n",[20,3958,3959],{"class":70,"line":351},[20,3960,587],{},[20,3962,3963],{"class":70,"line":430},[20,3964,551],{},[20,3966,3967],{"class":70,"line":436},[20,3968,3969],{},"    public static readonly DependencyProperty TargetObjectProperty =\n",[20,3971,3972],{"class":70,"line":442},[20,3973,3974],{},"        DependencyProperty.Register(nameof(TargetObject), typeof(FrameworkElement), \n",[20,3976,3977],{"class":70,"line":447},[20,3978,3979],{},"            typeof(AnimateToTargetSizeAction), new PropertyMetadata(null));\n",[20,3981,3982],{"class":70,"line":452},[20,3983,551],{},[20,3985,3986],{"class":70,"line":458},[20,3987,1565],{},[20,3989,3990],{"class":70,"line":464},[20,3991,569],{},[20,3993,3994],{"class":70,"line":470},[20,3995,1574],{},[20,3997,3998],{"class":70,"line":475},[20,3999,1579],{},[20,4001,4002],{"class":70,"line":481},[20,4003,587],{},[20,4005,4006],{"class":70,"line":487},[20,4007,551],{},[20,4009,4010],{"class":70,"line":492},[20,4011,1496],{},[20,4013,4014],{"class":70,"line":498},[20,4015,4016],{},"        DependencyProperty.Register(nameof(TargetWidth), typeof(double), \n",[20,4018,4019],{"class":70,"line":504},[20,4020,4021],{},"            typeof(AnimateToTargetSizeAction), new PropertyMetadata(double.NaN));\n",[20,4023,4024],{"class":70,"line":509},[20,4025,551],{},[20,4027,4028],{"class":70,"line":514},[20,4029,1592],{},[20,4031,4032],{"class":70,"line":520},[20,4033,569],{},[20,4035,4036],{"class":70,"line":526},[20,4037,1601],{},[20,4039,4040],{"class":70,"line":531},[20,4041,1606],{},[20,4043,4044],{"class":70,"line":536},[20,4045,587],{},[20,4047,4048],{"class":70,"line":542},[20,4049,551],{},[20,4051,4052],{"class":70,"line":548},[20,4053,1534],{},[20,4055,4056],{"class":70,"line":554},[20,4057,4058],{},"        DependencyProperty.Register(nameof(TargetHeight), typeof(double), \n",[20,4060,4061],{"class":70,"line":560},[20,4062,4021],{},[20,4064,4065],{"class":70,"line":566},[20,4066,551],{},[20,4068,4069],{"class":70,"line":572},[20,4070,545],{},[20,4072,4073],{"class":70,"line":578},[20,4074,551],{},[20,4076,4077],{"class":70,"line":584},[20,4078,4079],{},"    protected override void Invoke(object parameter)\n",[20,4081,4082],{"class":70,"line":590},[20,4083,569],{},[20,4085,4086],{"class":70,"line":595},[20,4087,4088],{},"        var target = TargetObject ?? AssociatedObject;\n",[20,4090,4091],{"class":70,"line":601},[20,4092,4093],{},"        if (target == null) return;\n",[20,4095,4096],{"class":70,"line":607},[20,4097,1191],{},[20,4099,4100],{"class":70,"line":613},[20,4101,4102],{},"        double currentWidth = target.Width;\n",[20,4104,4105],{"class":70,"line":619},[20,4106,4107],{},"        double currentHeight = target.Height;\n",[20,4109,4110],{"class":70,"line":625},[20,4111,1191],{},[20,4113,4115],{"class":70,"line":4114},50,[20,4116,4117],{},"        if (double.IsNaN(currentWidth) || currentWidth == 0)\n",[20,4119,4121],{"class":70,"line":4120},51,[20,4122,4123],{},"            currentWidth = target.ActualWidth;\n",[20,4125,4127],{"class":70,"line":4126},52,[20,4128,4129],{},"        if (double.IsNaN(currentHeight) || currentHeight == 0)\n",[20,4131,4133],{"class":70,"line":4132},53,[20,4134,4135],{},"            currentHeight = target.ActualHeight;\n",[20,4137,4139],{"class":70,"line":4138},54,[20,4140,1191],{},[20,4142,4144],{"class":70,"line":4143},55,[20,4145,4146],{},"        var storyboard = new Storyboard();\n",[20,4148,4150],{"class":70,"line":4149},56,[20,4151,1191],{},[20,4153,4155],{"class":70,"line":4154},57,[20,4156,4157],{},"        if (!double.IsNaN(TargetWidth) && Math.Abs(TargetWidth - currentWidth) > 0.01)\n",[20,4159,4161],{"class":70,"line":4160},58,[20,4162,1647],{},[20,4164,4166],{"class":70,"line":4165},59,[20,4167,4168],{},"            var widthAnimation = CreateSecondOrderAnimation(currentWidth, TargetWidth);\n",[20,4170,4172],{"class":70,"line":4171},60,[20,4173,4174],{},"            Storyboard.SetTarget(widthAnimation, target);\n",[20,4176,4178],{"class":70,"line":4177},61,[20,4179,4180],{},"            Storyboard.SetTargetProperty(widthAnimation, \n",[20,4182,4184],{"class":70,"line":4183},62,[20,4185,4186],{},"                new PropertyPath(FrameworkElement.WidthProperty));\n",[20,4188,4190],{"class":70,"line":4189},63,[20,4191,4192],{},"            storyboard.Children.Add(widthAnimation);\n",[20,4194,4196],{"class":70,"line":4195},64,[20,4197,1662],{},[20,4199,4201],{"class":70,"line":4200},65,[20,4202,1191],{},[20,4204,4206],{"class":70,"line":4205},66,[20,4207,4208],{},"        if (!double.IsNaN(TargetHeight) && Math.Abs(TargetHeight - currentHeight) > 0.01)\n",[20,4210,4212],{"class":70,"line":4211},67,[20,4213,1647],{},[20,4215,4217],{"class":70,"line":4216},68,[20,4218,4219],{},"            var heightAnimation = CreateSecondOrderAnimation(currentHeight, TargetHeight);\n",[20,4221,4223],{"class":70,"line":4222},69,[20,4224,4225],{},"            Storyboard.SetTarget(heightAnimation, target);\n",[20,4227,4229],{"class":70,"line":4228},70,[20,4230,4231],{},"            Storyboard.SetTargetProperty(heightAnimation, \n",[20,4233,4235],{"class":70,"line":4234},71,[20,4236,4237],{},"                new PropertyPath(FrameworkElement.HeightProperty));\n",[20,4239,4241],{"class":70,"line":4240},72,[20,4242,4243],{},"            storyboard.Children.Add(heightAnimation);\n",[20,4245,4247],{"class":70,"line":4246},73,[20,4248,1662],{},[20,4250,4252],{"class":70,"line":4251},74,[20,4253,1191],{},[20,4255,4257],{"class":70,"line":4256},75,[20,4258,4259],{},"        if (storyboard.Children.Count > 0)\n",[20,4261,4263],{"class":70,"line":4262},76,[20,4264,1647],{},[20,4266,4268],{"class":70,"line":4267},77,[20,4269,4270],{},"            storyboard.Begin(target);\n",[20,4272,4274],{"class":70,"line":4273},78,[20,4275,1662],{},[20,4277,4279],{"class":70,"line":4278},79,[20,4280,587],{},[20,4282,4284],{"class":70,"line":4283},80,[20,4285,551],{},[20,4287,4289],{"class":70,"line":4288},81,[20,4290,4291],{},"    private DoubleAnimation CreateSecondOrderAnimation(double from, double to)\n",[20,4293,4295],{"class":70,"line":4294},82,[20,4296,569],{},[20,4298,4300],{"class":70,"line":4299},83,[20,4301,4302],{},"        return new DoubleAnimation\n",[20,4304,4306],{"class":70,"line":4305},84,[20,4307,1647],{},[20,4309,4311],{"class":70,"line":4310},85,[20,4312,4313],{},"            From = from,\n",[20,4315,4317],{"class":70,"line":4316},86,[20,4318,4319],{},"            To = to,\n",[20,4321,4323],{"class":70,"line":4322},87,[20,4324,4325],{},"            Duration = Duration,\n",[20,4327,4329],{"class":70,"line":4328},88,[20,4330,4331],{},"            EasingFunction = new CubicEase\n",[20,4333,4335],{"class":70,"line":4334},89,[20,4336,4337],{},"            {\n",[20,4339,4341],{"class":70,"line":4340},90,[20,4342,4343],{},"                EasingMode = EasingMode.EaseInOut\n",[20,4345,4347],{"class":70,"line":4346},91,[20,4348,4349],{},"            }\n",[20,4351,4353],{"class":70,"line":4352},92,[20,4354,4355],{},"        };\n",[20,4357,4359],{"class":70,"line":4358},93,[20,4360,587],{},[20,4362,4364],{"class":70,"line":4363},94,[20,4365,354],{},[34,4367],{},[37,4369,4371],{"id":4370},"总结","🎓 总结",[42,4373,4374],{"id":4374},"核心优势",[210,4376,4377,4384,4390,4396,4402],{},[213,4378,4379,4380,4383],{},"✅ ",[25,4381,4382],{},"响应式","：属性变化自动触发动画",[213,4385,4379,4386,4389],{},[25,4387,4388],{},"声明式","：在 XAML 中配置，无需代码",[213,4391,4379,4392,4395],{},[25,4393,4394],{},"可复用","：自定义 Action 可以在多个地方使用",[213,4397,4379,4398,4401],{},[25,4399,4400],{},"平滑","：二阶缓动提供自然的动画效果",[213,4403,4379,4404,4407],{},[25,4405,4406],{},"灵活","：可以动画化任何依赖属性",[42,4409,182],{"id":182},[218,4411,4412,4415,4418,4421],{},[213,4413,4414],{},"✅ 数据驱动的 UI 动画",[213,4416,4417],{},"✅ 响应式布局调整",[213,4419,4420],{},"✅ 用户交互反馈",[213,4422,4423],{},"✅ 状态转换动画",[42,4425,4426],{"id":4426},"关键要点",[210,4428,4429,4435,4444,4449],{},[213,4430,4431,4434],{},[25,4432,4433],{},"差值自动计算","：Action 自动计算当前值与目标值的差值",[213,4436,4437,4440,4441,4443],{},[25,4438,4439],{},"二阶缓动","：使用 ",[49,4442,2865],{}," 实现平滑的加速和减速",[213,4445,4446,4448],{},[25,4447,910],{},"：只有当差值足够大时才创建动画",[213,4450,4451,4454],{},[25,4452,4453],{},"性能优化","：避免不必要的动画，及时停止冲突的动画",[34,4456],{},[37,4458,4460],{"id":4459},"相关资源","🔗 相关资源",[218,4462,4463,4470,4477],{},[213,4464,4465],{},[2461,4466,4469],{"href":4467,"rel":4468},"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FXamlBehaviorsWpf",[2465],"Microsoft.Xaml.Behaviors.Wpf 文档",[213,4471,4472],{},[2461,4473,4476],{"href":4474,"rel":4475},"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fdotnet\u002Fdesktop\u002Fwpf\u002Fgraphics-multimedia\u002Fanimation-overview",[2465],"WPF 动画概述",[213,4478,4479],{},[2461,4480,4483],{"href":4481,"rel":4482},"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fdotnet\u002Fdesktop\u002Fwpf\u002Fgraphics-multimedia\u002Feasing-functions",[2465],"缓动函数参考",[34,4485],{},[37,4487,4489],{"id":4488},"完整代码","完整代码：",[60,4491,4495],{"className":4492,"code":4493,"language":4494,"meta":65,"style":65},"language-cs shiki shiki-themes github-light github-dark","using System;  \nusing System.Windows;  \nusing System.Windows.Media.Animation;  \nusing Microsoft.Xaml.Behaviors;  \n  \nnamespace FluentScrollviewerSolution;  \n  \n\u002F\u002F\u002F \u003Csummary>  \n\u002F\u002F\u002F 自定义 Action：当目标宽度\u002F高度改变时，自动计算与当前值的差值，并执行二阶缓动动画  \n\u002F\u002F\u002F \u003C\u002Fsummary>  \npublic class AnimateToTargetSizeAction : TriggerAction\u003CFrameworkElement>  \n{  \n    \u002F\u002F 目标对象（要动画化的元素）  \n    public FrameworkElement TargetObject  \n    {  \n        get => (FrameworkElement)GetValue(TargetObjectProperty);  \n        set => SetValue(TargetObjectProperty, value);  \n    }    public static readonly DependencyProperty TargetObjectProperty =  \n        DependencyProperty.Register(nameof(TargetObject), typeof(FrameworkElement),   \ntypeof(AnimateToTargetSizeAction), new PropertyMetadata(null));  \n    \u002F\u002F 目标宽度  \n    public double TargetWidth  \n    {  \n        get => (double)GetValue(TargetWidthProperty);  \n        set => SetValue(TargetWidthProperty, value);  \n    }    public static readonly DependencyProperty TargetWidthProperty =  \n        DependencyProperty.Register(nameof(TargetWidth), typeof(double),   \ntypeof(AnimateToTargetSizeAction), new PropertyMetadata(double.NaN));  \n    \u002F\u002F 目标高度  \n    public double TargetHeight  \n    {  \n        get => (double)GetValue(TargetHeightProperty);  \n        set => SetValue(TargetHeightProperty, value);  \n    }    public static readonly DependencyProperty TargetHeightProperty =  \n        DependencyProperty.Register(nameof(TargetHeight), typeof(double),   \ntypeof(AnimateToTargetSizeAction), new PropertyMetadata(double.NaN));  \n    \u002F\u002F 动画持续时间  \n    public Duration Duration { get; set; } = new Duration(TimeSpan.FromSeconds(0.2));  \n    \u002F\u002F 缓动函数（默认使用二阶缓动 CubicEase）  \n    public IEasingFunction EasingFunction  \n    {  \n        get => (IEasingFunction)GetValue(EasingFunctionProperty);  \n        set => SetValue(EasingFunctionProperty, value);  \n    }    public static readonly DependencyProperty EasingFunctionProperty =  \n        DependencyProperty.Register(  \n            nameof(EasingFunction),   \ntypeof(IEasingFunction),   \ntypeof(AnimateToTargetSizeAction),   \nnew PropertyMetadata(new CubicEase { EasingMode = EasingMode.EaseInOut }));  \n    protected override void Invoke(object parameter)  \n    {        var target = TargetObject ?? AssociatedObject;  \n        if (target == null) return;  \n        \u002F\u002F 获取当前宽度和高度  \n        double currentWidth = target.Width;  \n        double currentHeight = target.Height;  \n        \u002F\u002F 如果当前值为 NaN 或 0，使用 ActualWidth\u002FActualHeight        if (double.IsNaN(currentWidth) || currentWidth == 0)  \n            currentWidth = target.ActualWidth;  \n        if (double.IsNaN(currentHeight) || currentHeight == 0)  \n            currentHeight = target.ActualHeight;  \n        \u002F\u002F 创建动画序列  \n        var storyboard = new Storyboard();  \n        \u002F\u002F 动画化宽度（如果目标宽度有效且与当前值不同）  \n        if (!double.IsNaN(TargetWidth) && Math.Abs(TargetWidth - currentWidth) > 0.01)  \n        {            var widthAnimation = CreateSecondOrderAnimation(currentWidth, TargetWidth);  \n            Storyboard.SetTarget(widthAnimation, target);  \n            Storyboard.SetTargetProperty(widthAnimation, new PropertyPath(FrameworkElement.WidthProperty));  \n            storyboard.Children.Add(widthAnimation);  \n        }        \u002F\u002F 动画化高度（如果目标高度有效且与当前值不同）  \n        if (!double.IsNaN(TargetHeight) && Math.Abs(TargetHeight - currentHeight) > 0.01)  \n        {            var heightAnimation = CreateSecondOrderAnimation(currentHeight, TargetHeight);  \n            Storyboard.SetTarget(heightAnimation, target);  \n            Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(FrameworkElement.HeightProperty));  \n            storyboard.Children.Add(heightAnimation);  \n        }        \u002F\u002F 如果至少有一个动画，则开始播放  \n        if (storyboard.Children.Count > 0)  \n        {            storyboard.Begin(target);  \n        }    }    \u002F\u002F\u002F \u003Csummary>  \n    \u002F\u002F\u002F 创建动画（使用指定的缓动函数，默认为二阶缓动 CubicEase）  \n    \u002F\u002F\u002F \u003C\u002Fsummary>  \n    private DoubleAnimation CreateSecondOrderAnimation(double from, double to)  \n    {        var animation = new DoubleAnimation  \n        {  \n            From = from,  \n            To = to,  \n            Duration = Duration,  \n            \u002F\u002F 使用指定的缓动函数（默认为 CubicEase，二阶缓动）  \n            EasingFunction = EasingFunction  \n        };  \n        return animation;  \n    }}\n","cs",[49,4496,4497,4502,4507,4512,4517,4522,4527,4531,4536,4541,4546,4551,4556,4561,4566,4571,4576,4581,4586,4591,4596,4601,4606,4610,4615,4620,4625,4630,4635,4640,4645,4649,4654,4659,4664,4669,4673,4678,4683,4688,4693,4697,4702,4707,4712,4717,4722,4727,4732,4737,4742,4747,4752,4757,4762,4767,4772,4777,4782,4787,4792,4797,4802,4807,4812,4817,4822,4827,4832,4837,4842,4847,4852,4857,4862,4867,4872,4877,4882,4887,4892,4897,4902,4907,4912,4917,4922,4927,4932,4937],{"__ignoreMap":65},[20,4498,4499],{"class":70,"line":71},[20,4500,4501],{},"using System;  \n",[20,4503,4504],{"class":70,"line":77},[20,4505,4506],{},"using System.Windows;  \n",[20,4508,4509],{"class":70,"line":83},[20,4510,4511],{},"using System.Windows.Media.Animation;  \n",[20,4513,4514],{"class":70,"line":89},[20,4515,4516],{},"using Microsoft.Xaml.Behaviors;  \n",[20,4518,4519],{"class":70,"line":95},[20,4520,4521],{},"  \n",[20,4523,4524],{"class":70,"line":101},[20,4525,4526],{},"namespace FluentScrollviewerSolution;  \n",[20,4528,4529],{"class":70,"line":107},[20,4530,4521],{},[20,4532,4533],{"class":70,"line":315},[20,4534,4535],{},"\u002F\u002F\u002F \u003Csummary>  \n",[20,4537,4538],{"class":70,"line":321},[20,4539,4540],{},"\u002F\u002F\u002F 自定义 Action：当目标宽度\u002F高度改变时，自动计算与当前值的差值，并执行二阶缓动动画  \n",[20,4542,4543],{"class":70,"line":327},[20,4544,4545],{},"\u002F\u002F\u002F \u003C\u002Fsummary>  \n",[20,4547,4548],{"class":70,"line":333},[20,4549,4550],{},"public class AnimateToTargetSizeAction : TriggerAction\u003CFrameworkElement>  \n",[20,4552,4553],{"class":70,"line":339},[20,4554,4555],{},"{  \n",[20,4557,4558],{"class":70,"line":345},[20,4559,4560],{},"    \u002F\u002F 目标对象（要动画化的元素）  \n",[20,4562,4563],{"class":70,"line":351},[20,4564,4565],{},"    public FrameworkElement TargetObject  \n",[20,4567,4568],{"class":70,"line":430},[20,4569,4570],{},"    {  \n",[20,4572,4573],{"class":70,"line":436},[20,4574,4575],{},"        get => (FrameworkElement)GetValue(TargetObjectProperty);  \n",[20,4577,4578],{"class":70,"line":442},[20,4579,4580],{},"        set => SetValue(TargetObjectProperty, value);  \n",[20,4582,4583],{"class":70,"line":447},[20,4584,4585],{},"    }    public static readonly DependencyProperty TargetObjectProperty =  \n",[20,4587,4588],{"class":70,"line":452},[20,4589,4590],{},"        DependencyProperty.Register(nameof(TargetObject), typeof(FrameworkElement),   \n",[20,4592,4593],{"class":70,"line":458},[20,4594,4595],{},"typeof(AnimateToTargetSizeAction), new PropertyMetadata(null));  \n",[20,4597,4598],{"class":70,"line":464},[20,4599,4600],{},"    \u002F\u002F 目标宽度  \n",[20,4602,4603],{"class":70,"line":470},[20,4604,4605],{},"    public double TargetWidth  \n",[20,4607,4608],{"class":70,"line":475},[20,4609,4570],{},[20,4611,4612],{"class":70,"line":481},[20,4613,4614],{},"        get => (double)GetValue(TargetWidthProperty);  \n",[20,4616,4617],{"class":70,"line":487},[20,4618,4619],{},"        set => SetValue(TargetWidthProperty, value);  \n",[20,4621,4622],{"class":70,"line":492},[20,4623,4624],{},"    }    public static readonly DependencyProperty TargetWidthProperty =  \n",[20,4626,4627],{"class":70,"line":498},[20,4628,4629],{},"        DependencyProperty.Register(nameof(TargetWidth), typeof(double),   \n",[20,4631,4632],{"class":70,"line":504},[20,4633,4634],{},"typeof(AnimateToTargetSizeAction), new PropertyMetadata(double.NaN));  \n",[20,4636,4637],{"class":70,"line":509},[20,4638,4639],{},"    \u002F\u002F 目标高度  \n",[20,4641,4642],{"class":70,"line":514},[20,4643,4644],{},"    public double TargetHeight  \n",[20,4646,4647],{"class":70,"line":520},[20,4648,4570],{},[20,4650,4651],{"class":70,"line":526},[20,4652,4653],{},"        get => (double)GetValue(TargetHeightProperty);  \n",[20,4655,4656],{"class":70,"line":531},[20,4657,4658],{},"        set => SetValue(TargetHeightProperty, value);  \n",[20,4660,4661],{"class":70,"line":536},[20,4662,4663],{},"    }    public static readonly DependencyProperty TargetHeightProperty =  \n",[20,4665,4666],{"class":70,"line":542},[20,4667,4668],{},"        DependencyProperty.Register(nameof(TargetHeight), typeof(double),   \n",[20,4670,4671],{"class":70,"line":548},[20,4672,4634],{},[20,4674,4675],{"class":70,"line":554},[20,4676,4677],{},"    \u002F\u002F 动画持续时间  \n",[20,4679,4680],{"class":70,"line":560},[20,4681,4682],{},"    public Duration Duration { get; set; } = new Duration(TimeSpan.FromSeconds(0.2));  \n",[20,4684,4685],{"class":70,"line":566},[20,4686,4687],{},"    \u002F\u002F 缓动函数（默认使用二阶缓动 CubicEase）  \n",[20,4689,4690],{"class":70,"line":572},[20,4691,4692],{},"    public IEasingFunction EasingFunction  \n",[20,4694,4695],{"class":70,"line":578},[20,4696,4570],{},[20,4698,4699],{"class":70,"line":584},[20,4700,4701],{},"        get => (IEasingFunction)GetValue(EasingFunctionProperty);  \n",[20,4703,4704],{"class":70,"line":590},[20,4705,4706],{},"        set => SetValue(EasingFunctionProperty, value);  \n",[20,4708,4709],{"class":70,"line":595},[20,4710,4711],{},"    }    public static readonly DependencyProperty EasingFunctionProperty =  \n",[20,4713,4714],{"class":70,"line":601},[20,4715,4716],{},"        DependencyProperty.Register(  \n",[20,4718,4719],{"class":70,"line":607},[20,4720,4721],{},"            nameof(EasingFunction),   \n",[20,4723,4724],{"class":70,"line":613},[20,4725,4726],{},"typeof(IEasingFunction),   \n",[20,4728,4729],{"class":70,"line":619},[20,4730,4731],{},"typeof(AnimateToTargetSizeAction),   \n",[20,4733,4734],{"class":70,"line":625},[20,4735,4736],{},"new PropertyMetadata(new CubicEase { EasingMode = EasingMode.EaseInOut }));  \n",[20,4738,4739],{"class":70,"line":4114},[20,4740,4741],{},"    protected override void Invoke(object parameter)  \n",[20,4743,4744],{"class":70,"line":4120},[20,4745,4746],{},"    {        var target = TargetObject ?? AssociatedObject;  \n",[20,4748,4749],{"class":70,"line":4126},[20,4750,4751],{},"        if (target == null) return;  \n",[20,4753,4754],{"class":70,"line":4132},[20,4755,4756],{},"        \u002F\u002F 获取当前宽度和高度  \n",[20,4758,4759],{"class":70,"line":4138},[20,4760,4761],{},"        double currentWidth = target.Width;  \n",[20,4763,4764],{"class":70,"line":4143},[20,4765,4766],{},"        double currentHeight = target.Height;  \n",[20,4768,4769],{"class":70,"line":4149},[20,4770,4771],{},"        \u002F\u002F 如果当前值为 NaN 或 0，使用 ActualWidth\u002FActualHeight        if (double.IsNaN(currentWidth) || currentWidth == 0)  \n",[20,4773,4774],{"class":70,"line":4154},[20,4775,4776],{},"            currentWidth = target.ActualWidth;  \n",[20,4778,4779],{"class":70,"line":4160},[20,4780,4781],{},"        if (double.IsNaN(currentHeight) || currentHeight == 0)  \n",[20,4783,4784],{"class":70,"line":4165},[20,4785,4786],{},"            currentHeight = target.ActualHeight;  \n",[20,4788,4789],{"class":70,"line":4171},[20,4790,4791],{},"        \u002F\u002F 创建动画序列  \n",[20,4793,4794],{"class":70,"line":4177},[20,4795,4796],{},"        var storyboard = new Storyboard();  \n",[20,4798,4799],{"class":70,"line":4183},[20,4800,4801],{},"        \u002F\u002F 动画化宽度（如果目标宽度有效且与当前值不同）  \n",[20,4803,4804],{"class":70,"line":4189},[20,4805,4806],{},"        if (!double.IsNaN(TargetWidth) && Math.Abs(TargetWidth - currentWidth) > 0.01)  \n",[20,4808,4809],{"class":70,"line":4195},[20,4810,4811],{},"        {            var widthAnimation = CreateSecondOrderAnimation(currentWidth, TargetWidth);  \n",[20,4813,4814],{"class":70,"line":4200},[20,4815,4816],{},"            Storyboard.SetTarget(widthAnimation, target);  \n",[20,4818,4819],{"class":70,"line":4205},[20,4820,4821],{},"            Storyboard.SetTargetProperty(widthAnimation, new PropertyPath(FrameworkElement.WidthProperty));  \n",[20,4823,4824],{"class":70,"line":4211},[20,4825,4826],{},"            storyboard.Children.Add(widthAnimation);  \n",[20,4828,4829],{"class":70,"line":4216},[20,4830,4831],{},"        }        \u002F\u002F 动画化高度（如果目标高度有效且与当前值不同）  \n",[20,4833,4834],{"class":70,"line":4222},[20,4835,4836],{},"        if (!double.IsNaN(TargetHeight) && Math.Abs(TargetHeight - currentHeight) > 0.01)  \n",[20,4838,4839],{"class":70,"line":4228},[20,4840,4841],{},"        {            var heightAnimation = CreateSecondOrderAnimation(currentHeight, TargetHeight);  \n",[20,4843,4844],{"class":70,"line":4234},[20,4845,4846],{},"            Storyboard.SetTarget(heightAnimation, target);  \n",[20,4848,4849],{"class":70,"line":4240},[20,4850,4851],{},"            Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(FrameworkElement.HeightProperty));  \n",[20,4853,4854],{"class":70,"line":4246},[20,4855,4856],{},"            storyboard.Children.Add(heightAnimation);  \n",[20,4858,4859],{"class":70,"line":4251},[20,4860,4861],{},"        }        \u002F\u002F 如果至少有一个动画，则开始播放  \n",[20,4863,4864],{"class":70,"line":4256},[20,4865,4866],{},"        if (storyboard.Children.Count > 0)  \n",[20,4868,4869],{"class":70,"line":4262},[20,4870,4871],{},"        {            storyboard.Begin(target);  \n",[20,4873,4874],{"class":70,"line":4267},[20,4875,4876],{},"        }    }    \u002F\u002F\u002F \u003Csummary>  \n",[20,4878,4879],{"class":70,"line":4273},[20,4880,4881],{},"    \u002F\u002F\u002F 创建动画（使用指定的缓动函数，默认为二阶缓动 CubicEase）  \n",[20,4883,4884],{"class":70,"line":4278},[20,4885,4886],{},"    \u002F\u002F\u002F \u003C\u002Fsummary>  \n",[20,4888,4889],{"class":70,"line":4283},[20,4890,4891],{},"    private DoubleAnimation CreateSecondOrderAnimation(double from, double to)  \n",[20,4893,4894],{"class":70,"line":4288},[20,4895,4896],{},"    {        var animation = new DoubleAnimation  \n",[20,4898,4899],{"class":70,"line":4294},[20,4900,4901],{},"        {  \n",[20,4903,4904],{"class":70,"line":4299},[20,4905,4906],{},"            From = from,  \n",[20,4908,4909],{"class":70,"line":4305},[20,4910,4911],{},"            To = to,  \n",[20,4913,4914],{"class":70,"line":4310},[20,4915,4916],{},"            Duration = Duration,  \n",[20,4918,4919],{"class":70,"line":4316},[20,4920,4921],{},"            \u002F\u002F 使用指定的缓动函数（默认为 CubicEase，二阶缓动）  \n",[20,4923,4924],{"class":70,"line":4322},[20,4925,4926],{},"            EasingFunction = EasingFunction  \n",[20,4928,4929],{"class":70,"line":4328},[20,4930,4931],{},"        };  \n",[20,4933,4934],{"class":70,"line":4334},[20,4935,4936],{},"        return animation;  \n",[20,4938,4939],{"class":70,"line":4340},[20,4940,4941],{},"    }}\n",[4943,4944,4945],"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":65,"searchDepth":77,"depth":77,"links":4947},[4948,4953,4957,4962,4974,4979,4982,4988,4994,4999,5004,5007,5012,5013],{"id":39,"depth":77,"text":40,"children":4949},[4950,4951,4952],{"id":44,"depth":83,"text":45},{"id":58,"depth":83,"text":58},{"id":113,"depth":83,"text":113},{"id":193,"depth":77,"text":194,"children":4954},[4955,4956],{"id":197,"depth":83,"text":197},{"id":208,"depth":83,"text":208},{"id":261,"depth":77,"text":262,"children":4958},[4959,4960,4961],{"id":265,"depth":83,"text":266},{"id":1120,"depth":83,"text":1121},{"id":1471,"depth":83,"text":1472},{"id":1702,"depth":77,"text":1703,"children":4963},[4964,4965,4966,4967,4968,4969,4970,4971,4972,4973],{"id":1706,"depth":83,"text":1707},{"id":1720,"depth":83,"text":1721},{"id":1755,"depth":83,"text":1756},{"id":1861,"depth":83,"text":1861},{"id":1991,"depth":83,"text":1992},{"id":2071,"depth":83,"text":2071},{"id":2150,"depth":83,"text":2151},{"id":2310,"depth":83,"text":2310},{"id":2416,"depth":83,"text":2416},{"id":2470,"depth":83,"text":2470},{"id":2559,"depth":77,"text":2560,"children":4975},[4976,4977,4978],{"id":2563,"depth":83,"text":2564},{"id":2656,"depth":83,"text":2657},{"id":2789,"depth":83,"text":2790},{"id":2801,"depth":77,"text":2802,"children":4980},[4981],{"id":2805,"depth":83,"text":2806},{"id":2943,"depth":77,"text":2944,"children":4983},[4984,4985,4986,4987],{"id":2947,"depth":83,"text":2948},{"id":2966,"depth":83,"text":2967},{"id":3263,"depth":83,"text":3264},{"id":3350,"depth":83,"text":3351},{"id":3400,"depth":77,"text":3401,"children":4989},[4990,4991,4992,4993],{"id":3404,"depth":83,"text":3405},{"id":3473,"depth":83,"text":3474},{"id":3564,"depth":83,"text":3565},{"id":3589,"depth":83,"text":3590},{"id":3624,"depth":77,"text":3625,"children":4995},[4996,4997,4998],{"id":3628,"depth":83,"text":3629},{"id":3679,"depth":83,"text":3680},{"id":3766,"depth":83,"text":3767},{"id":2416,"depth":77,"text":3820,"children":5000},[5001,5002,5003],{"id":3823,"depth":83,"text":3824},{"id":3863,"depth":83,"text":3864},{"id":3882,"depth":83,"text":3883},{"id":3891,"depth":77,"text":3892,"children":5005},[5006],{"id":3895,"depth":83,"text":3896},{"id":4370,"depth":77,"text":4371,"children":5008},[5009,5010,5011],{"id":4374,"depth":83,"text":4374},{"id":182,"depth":83,"text":182},{"id":4426,"depth":83,"text":4426},{"id":4459,"depth":77,"text":4460},{"id":4488,"depth":77,"text":4489},"md",{},"\u002Fcsharp\u002Fwpf\u002F基于propertychangedtrigger-(属性变更触发器)驱动的显式动画",{"title":5,"description":65},"csharp\u002FWPF\u002F基于PropertyChangedTrigger (属性变更触发器)驱动的显式动画","B-1x7BRYBG29E1N5gTkYbnCfin1mrnrxZ7zEw_YvwlE",1791042466243]