[{"data":1,"prerenderedAt":973},["ShallowReactive",2],{"page-\u002Fcsharp\u002Fwpf\u002F依赖属性和附加属性":3},{"id":4,"title":5,"body":6,"description":12,"extension":967,"meta":968,"navigation":148,"path":969,"seo":970,"stem":971,"__hash__":972},"content\u002Fcsharp\u002FWPF\u002F依赖属性和附加属性.md","依赖属性和附加属性",{"type":7,"value":8,"toc":958},"minimark",[9,13,42,57,60,222,226,233,340,351,354,385,388,395,479,482,496,510,516,530,568,571,628,661,667,670,673,683,690,720,735,738,788,791,794,912,915,928,954],[10,11,12],"p",{},"依赖属性是扩展对象自身的属性功能，比如说是数据绑定、动画，属性是属于定义它的类的，最经典的场景是Button的background。依赖属性的主要作用是提供一种比普通的 .NET 属性更强大的内存管理和功能支持。",[14,15,16,24,30,36],"ul",{},[17,18,19,23],"li",{},[20,21,22],"strong",{},"数据绑定："," 只有依赖属性才能作为 Data Binding 的目标。",[17,25,26,29],{},[20,27,28],{},"样式与模板："," 支持通过 Style 或 ControlTemplate 进行统一修改。",[17,31,32,35],{},[20,33,34],{},"属性值优先级："," 它可以根据优先级（如动画 > 本地值 > 样式 > 默认值）自动计算最终结果。",[17,37,38,41],{},[20,39,40],{},"资源节约："," 如果属性没有被赋值，它不会占用实例内存，而是指向默认值。",[10,43,44,47,48,52,53,56],{},[20,45,46],{},"示例："," ",[49,50,51],"code",{},"Button"," 的 ",[49,54,55],{},"Content"," 属性。按钮自己拥有这个属性，并负责显示它。",[10,58,59],{},"怎样自己定义一个依赖属性呢？C#属性的定义大家再熟悉不过了：",[61,62,67],"pre",{"className":63,"code":64,"language":65,"meta":66,"style":66},"language-cs shiki shiki-themes github-light github-dark","\u002F\u002F 1. 使类型继承DependencyObject类\n    public class Person : DependencyObject\n    {\n        \u002F\u002F 2. 声明一个静态只读的DependencyProperty 字段\n        public static readonly DependencyProperty nameProperty;\n       \n        static Person()\n        {\n            \u002F\u002F 3. 注册定义的依赖属性\n            nameProperty = DependencyProperty.Register(\"Name\", typeof(string), typeof(Person), \n                new PropertyMetadata(\"Learning Hard\",OnValueChanged)); \n        }\n\n        \u002F\u002F 4. 属性包装器，通过它来读取和设置我们刚才注册的依赖属性\n        public string Name\n        {\n            get { return (string)GetValue(nameProperty); }\n            set { SetValue(nameProperty, value); }\n        }\n\n        private static void OnValueChanged(DependencyObject dpobj, DependencyPropertyChangedEventArgs e)\n        {\n            \u002F\u002F 当只发生改变时回调的方法\n        }\n\n    }\n","cs","",[49,68,69,77,83,89,95,101,107,113,119,125,131,137,143,150,156,162,167,173,179,184,189,195,200,206,211,216],{"__ignoreMap":66},[70,71,74],"span",{"class":72,"line":73},"line",1,[70,75,76],{},"\u002F\u002F 1. 使类型继承DependencyObject类\n",[70,78,80],{"class":72,"line":79},2,[70,81,82],{},"    public class Person : DependencyObject\n",[70,84,86],{"class":72,"line":85},3,[70,87,88],{},"    {\n",[70,90,92],{"class":72,"line":91},4,[70,93,94],{},"        \u002F\u002F 2. 声明一个静态只读的DependencyProperty 字段\n",[70,96,98],{"class":72,"line":97},5,[70,99,100],{},"        public static readonly DependencyProperty nameProperty;\n",[70,102,104],{"class":72,"line":103},6,[70,105,106],{},"       \n",[70,108,110],{"class":72,"line":109},7,[70,111,112],{},"        static Person()\n",[70,114,116],{"class":72,"line":115},8,[70,117,118],{},"        {\n",[70,120,122],{"class":72,"line":121},9,[70,123,124],{},"            \u002F\u002F 3. 注册定义的依赖属性\n",[70,126,128],{"class":72,"line":127},10,[70,129,130],{},"            nameProperty = DependencyProperty.Register(\"Name\", typeof(string), typeof(Person), \n",[70,132,134],{"class":72,"line":133},11,[70,135,136],{},"                new PropertyMetadata(\"Learning Hard\",OnValueChanged)); \n",[70,138,140],{"class":72,"line":139},12,[70,141,142],{},"        }\n",[70,144,146],{"class":72,"line":145},13,[70,147,149],{"emptyLinePlaceholder":148},true,"\n",[70,151,153],{"class":72,"line":152},14,[70,154,155],{},"        \u002F\u002F 4. 属性包装器，通过它来读取和设置我们刚才注册的依赖属性\n",[70,157,159],{"class":72,"line":158},15,[70,160,161],{},"        public string Name\n",[70,163,165],{"class":72,"line":164},16,[70,166,118],{},[70,168,170],{"class":72,"line":169},17,[70,171,172],{},"            get { return (string)GetValue(nameProperty); }\n",[70,174,176],{"class":72,"line":175},18,[70,177,178],{},"            set { SetValue(nameProperty, value); }\n",[70,180,182],{"class":72,"line":181},19,[70,183,142],{},[70,185,187],{"class":72,"line":186},20,[70,188,149],{"emptyLinePlaceholder":148},[70,190,192],{"class":72,"line":191},21,[70,193,194],{},"        private static void OnValueChanged(DependencyObject dpobj, DependencyPropertyChangedEventArgs e)\n",[70,196,198],{"class":72,"line":197},22,[70,199,118],{},[70,201,203],{"class":72,"line":202},23,[70,204,205],{},"            \u002F\u002F 当只发生改变时回调的方法\n",[70,207,209],{"class":72,"line":208},24,[70,210,142],{},[70,212,214],{"class":72,"line":213},25,[70,215,149],{"emptyLinePlaceholder":148},[70,217,219],{"class":72,"line":218},26,[70,220,221],{},"    }\n",[223,224,225],"h2",{"id":225},"依赖属性的优先级",[10,227,228,229,232],{},"WPF允许在多个地方设置依赖属性的值，则自然就涉及到依赖属性获取值的优先级问题。例如下面XMAL代码，",[20,230,231],{},"我们在三个地方设置了按钮的背景颜色，那最终按钮会读取那个设置的值呢","？是Green、Yellow还是Red？",[61,234,238],{"className":235,"code":236,"language":237,"meta":66,"style":66},"language-xml shiki shiki-themes github-light github-dark","\u003CWindow x:Class=\"DPSample.MainWindow\"\n        xmlns=\"http:\u002F\u002Fschemas.microsoft.com\u002Fwinfx\u002F2006\u002Fxaml\u002Fpresentation\"\n        xmlns:x=\"http:\u002F\u002Fschemas.microsoft.com\u002Fwinfx\u002F2006\u002Fxaml\"\n        Title=\"MainWindow\" Height=\"350\" Width=\"525\">\n    \u003CGrid>\n        \u003CButton x:Name=\"myButton\" Background=\"Green\" Width=\"100\" Height=\"30\">\n            \u003CButton.Style>\n                \u003CStyle TargetType=\"{x:Type Button}\">\n                    \u003CSetter Property=\"Background\" Value=\"Yellow\"\u002F>\n                    \u003CStyle.Triggers>\n                        \u003CTrigger Property=\"IsMouseOver\" Value=\"True\">\n                            \u003CSetter Property=\"Background\" Value=\"Red\" \u002F>\n                        \u003C\u002FTrigger>\n                    \u003C\u002FStyle.Triggers>\n                \u003C\u002FStyle>\n            \u003C\u002FButton.Style>\n            Click Me \n        \u003C\u002FButton>\n    \u003C\u002FGrid>\n\u003C\u002FWindow>\n","xml",[49,239,240,245,250,255,260,265,270,275,280,285,290,295,300,305,310,315,320,325,330,335],{"__ignoreMap":66},[70,241,242],{"class":72,"line":73},[70,243,244],{},"\u003CWindow x:Class=\"DPSample.MainWindow\"\n",[70,246,247],{"class":72,"line":79},[70,248,249],{},"        xmlns=\"http:\u002F\u002Fschemas.microsoft.com\u002Fwinfx\u002F2006\u002Fxaml\u002Fpresentation\"\n",[70,251,252],{"class":72,"line":85},[70,253,254],{},"        xmlns:x=\"http:\u002F\u002Fschemas.microsoft.com\u002Fwinfx\u002F2006\u002Fxaml\"\n",[70,256,257],{"class":72,"line":91},[70,258,259],{},"        Title=\"MainWindow\" Height=\"350\" Width=\"525\">\n",[70,261,262],{"class":72,"line":97},[70,263,264],{},"    \u003CGrid>\n",[70,266,267],{"class":72,"line":103},[70,268,269],{},"        \u003CButton x:Name=\"myButton\" Background=\"Green\" Width=\"100\" Height=\"30\">\n",[70,271,272],{"class":72,"line":109},[70,273,274],{},"            \u003CButton.Style>\n",[70,276,277],{"class":72,"line":115},[70,278,279],{},"                \u003CStyle TargetType=\"{x:Type Button}\">\n",[70,281,282],{"class":72,"line":121},[70,283,284],{},"                    \u003CSetter Property=\"Background\" Value=\"Yellow\"\u002F>\n",[70,286,287],{"class":72,"line":127},[70,288,289],{},"                    \u003CStyle.Triggers>\n",[70,291,292],{"class":72,"line":133},[70,293,294],{},"                        \u003CTrigger Property=\"IsMouseOver\" Value=\"True\">\n",[70,296,297],{"class":72,"line":139},[70,298,299],{},"                            \u003CSetter Property=\"Background\" Value=\"Red\" \u002F>\n",[70,301,302],{"class":72,"line":145},[70,303,304],{},"                        \u003C\u002FTrigger>\n",[70,306,307],{"class":72,"line":152},[70,308,309],{},"                    \u003C\u002FStyle.Triggers>\n",[70,311,312],{"class":72,"line":158},[70,313,314],{},"                \u003C\u002FStyle>\n",[70,316,317],{"class":72,"line":164},[70,318,319],{},"            \u003C\u002FButton.Style>\n",[70,321,322],{"class":72,"line":169},[70,323,324],{},"            Click Me \n",[70,326,327],{"class":72,"line":175},[70,328,329],{},"        \u003C\u002FButton>\n",[70,331,332],{"class":72,"line":181},[70,333,334],{},"    \u003C\u002FGrid>\n",[70,336,337],{"class":72,"line":186},[70,338,339],{},"\u003C\u002FWindow>\n",[10,341,342,343,346,347,350],{},"上面按钮的背景颜色是Green。之所以背景色是Green，",[20,344,345],{},"是因为WPF每访问一个依赖属性","，它都会",[20,348,349],{},"按照下面的顺序由高到底处理该值","。具体优先级如下图所示：",[10,352,353],{},"![[Pasted image 20251213045459.png]]",[10,355,356,357,360,361,364,365,368,369,372,373,376,377,380,381,384],{},"按钮的",[20,358,359],{},"本地值设置的是Green","，自定义Style ",[20,362,363],{},"Trigger设置的为Red","，自定义的",[20,366,367],{},"Style Setter设置的为Yellow","，由于这里的",[20,370,371],{},"本地值的优先级最高","，所以按钮的",[20,374,375],{},"背景色或者的是Green值","。如果此时把本地值Green去掉的话，此时",[20,378,379],{},"按钮的背景颜色是Yellow而不是Red","。这里",[20,382,383],{},"尽管Style Trigger的优先级比Style Setter高，但是由于此时Style Trigger的IsMouseOver属性为false","，即鼠标没有移到按钮上，一旦鼠标移到按钮上时，此时按钮的颜色就为Red。此时才会体现出Style Trigger的优先级比Style Setter优先级高。所以上图中优先级是比较理想情况下，很多时候还需要具体分析。",[223,386,387],{"id":387},"依赖属性的继承",[10,389,390,391,394],{},"依赖属性是可以被继承的，即",[20,392,393],{},"父元素的相关设置会自动传递给所有的子元素","。下面代码演示了依赖属性的继承。",[61,396,398],{"className":235,"code":397,"language":237,"meta":66,"style":66},"\u003CWindow x:Class=\"Custom_DPInherited.DPInherited\"\n      xmlns=\"http:\u002F\u002Fschemas.microsoft.com\u002Fwinfx\u002F2006\u002Fxaml\u002Fpresentation\"\n      xmlns:x=\"http:\u002F\u002Fschemas.microsoft.com\u002Fwinfx\u002F2006\u002Fxaml\"\n      xmlns:mc=\"http:\u002F\u002Fschemas.openxmlformats.org\u002Fmarkup-compatibility\u002F2006\" \n      xmlns:d=\"http:\u002F\u002Fschemas.microsoft.com\u002Fexpression\u002Fblend\u002F2008\" \n      mc:Ignorable=\"d\" \n      d:DesignHeight=\"300\" d:DesignWidth=\"300\"\n      FontSize=\"18\"\n      Title=\"依赖属性的继承\">\n    \u003CStackPanel >\n        \u003CLabel Content=\"继承自Window的FontSize\" \u002F>\n        \u003CLabel Content=\"显式设置FontSize\" \n               TextElement.FontSize=\"36\"\u002F>\n        \u003CStatusBar>Statusbar没有继承自Window的FontSize\u003C\u002FStatusBar>\n    \u003C\u002FStackPanel>\n\u003C\u002FWindow>\n",[49,399,400,405,410,415,420,425,430,435,440,445,450,455,460,465,470,475],{"__ignoreMap":66},[70,401,402],{"class":72,"line":73},[70,403,404],{},"\u003CWindow x:Class=\"Custom_DPInherited.DPInherited\"\n",[70,406,407],{"class":72,"line":79},[70,408,409],{},"      xmlns=\"http:\u002F\u002Fschemas.microsoft.com\u002Fwinfx\u002F2006\u002Fxaml\u002Fpresentation\"\n",[70,411,412],{"class":72,"line":85},[70,413,414],{},"      xmlns:x=\"http:\u002F\u002Fschemas.microsoft.com\u002Fwinfx\u002F2006\u002Fxaml\"\n",[70,416,417],{"class":72,"line":91},[70,418,419],{},"      xmlns:mc=\"http:\u002F\u002Fschemas.openxmlformats.org\u002Fmarkup-compatibility\u002F2006\" \n",[70,421,422],{"class":72,"line":97},[70,423,424],{},"      xmlns:d=\"http:\u002F\u002Fschemas.microsoft.com\u002Fexpression\u002Fblend\u002F2008\" \n",[70,426,427],{"class":72,"line":103},[70,428,429],{},"      mc:Ignorable=\"d\" \n",[70,431,432],{"class":72,"line":109},[70,433,434],{},"      d:DesignHeight=\"300\" d:DesignWidth=\"300\"\n",[70,436,437],{"class":72,"line":115},[70,438,439],{},"      FontSize=\"18\"\n",[70,441,442],{"class":72,"line":121},[70,443,444],{},"      Title=\"依赖属性的继承\">\n",[70,446,447],{"class":72,"line":127},[70,448,449],{},"    \u003CStackPanel >\n",[70,451,452],{"class":72,"line":133},[70,453,454],{},"        \u003CLabel Content=\"继承自Window的FontSize\" \u002F>\n",[70,456,457],{"class":72,"line":139},[70,458,459],{},"        \u003CLabel Content=\"显式设置FontSize\" \n",[70,461,462],{"class":72,"line":145},[70,463,464],{},"               TextElement.FontSize=\"36\"\u002F>\n",[70,466,467],{"class":72,"line":152},[70,468,469],{},"        \u003CStatusBar>Statusbar没有继承自Window的FontSize\u003C\u002FStatusBar>\n",[70,471,472],{"class":72,"line":158},[70,473,474],{},"    \u003C\u002FStackPanel>\n",[70,476,477],{"class":72,"line":164},[70,478,339],{},[10,480,481],{},"![[Pasted image 20251213045731.png]]",[10,483,484,485,488,489,491,492,495],{},"在上面XAML代码中。",[20,486,487],{},"Window.FontSize设置会影响所有内部子元素字体大小","，这就是",[20,490,387],{},"。如第一个Label没有定义FontSize，",[20,493,494],{},"所以它继承了Window.FontSize值","。但一旦子元素提供了显式设置，这种继承就会被打断，所以Window.FontSize值对于第二个Label不再起作用。",[10,497,498,501,502,505,506,509],{},[20,499,500],{},"适用场景","：\n- 自定义控件的属性（如 ",[49,503,504],{},"Button.Content","、",[49,507,508],{},"TextBox.Text","）。\n- 需要支持数据绑定、动画、样式等 WPF 功能的属性。",[223,511,513],{"id":512},"附加属性attached-property",[20,514,515],{},"附加属性（Attached Property）",[10,517,518,519,522,523,505,526,529],{},"用途​​：​​为其他控件（尤其是",[20,520,521],{},"无法修改源码的控件","）扩展功能​​（如 ",[49,524,525],{},"Grid",[49,527,528],{},"Canvas","）中为子控件定义额外的属性。通过静态方法跨类附加属性，解耦功能与控件。",[61,531,533],{"className":63,"code":532,"language":65,"meta":66,"style":66},"public static readonly DependencyProperty MyAttachedPropertyProperty =\n    DependencyProperty.RegisterAttached(\n        \"MyAttachedProperty\",        \u002F\u002F 属性名\n        typeof(int),                 \u002F\u002F 属性类型\n        typeof(OwnerClass),          \u002F\u002F 定义该属性的类\n        new PropertyMetadata(0));    \u002F\u002F 默认值\n\n",[49,534,535,540,545,553,558,563],{"__ignoreMap":66},[70,536,537],{"class":72,"line":73},[70,538,539],{},"public static readonly DependencyProperty MyAttachedPropertyProperty =\n",[70,541,542],{"class":72,"line":79},[70,543,544],{},"    DependencyProperty.RegisterAttached(\n",[70,546,547,550],{"class":72,"line":85},[70,548,549],{},"        \"MyAttachedProperty\",",[70,551,552],{},"        \u002F\u002F 属性名\n",[70,554,555],{"class":72,"line":91},[70,556,557],{},"        typeof(int),                 \u002F\u002F 属性类型\n",[70,559,560],{"class":72,"line":97},[70,561,562],{},"        typeof(OwnerClass),          \u002F\u002F 定义该属性的类\n",[70,564,565],{"class":72,"line":103},[70,566,567],{},"        new PropertyMetadata(0));    \u002F\u002F 默认值\n",[10,569,570],{},"访问方式：",[61,572,574],{"className":63,"code":573,"language":65,"meta":66,"style":66},"\u002F\u002F Getter\npublic static int GetMyAttachedProperty(DependencyObject obj)\n{\n    return (int)obj.GetValue(MyAttachedPropertyProperty);\n}\n\n\u002F\u002F Setter\npublic static void SetMyAttachedProperty(DependencyObject obj, int value)\n{\n    obj.SetValue(MyAttachedPropertyProperty, value);\n}\n\n",[49,575,576,581,586,591,596,601,605,610,615,619,624],{"__ignoreMap":66},[70,577,578],{"class":72,"line":73},[70,579,580],{},"\u002F\u002F Getter\n",[70,582,583],{"class":72,"line":79},[70,584,585],{},"public static int GetMyAttachedProperty(DependencyObject obj)\n",[70,587,588],{"class":72,"line":85},[70,589,590],{},"{\n",[70,592,593],{"class":72,"line":91},[70,594,595],{},"    return (int)obj.GetValue(MyAttachedPropertyProperty);\n",[70,597,598],{"class":72,"line":97},[70,599,600],{},"}\n",[70,602,603],{"class":72,"line":103},[70,604,149],{"emptyLinePlaceholder":148},[70,606,607],{"class":72,"line":109},[70,608,609],{},"\u002F\u002F Setter\n",[70,611,612],{"class":72,"line":115},[70,613,614],{},"public static void SetMyAttachedProperty(DependencyObject obj, int value)\n",[70,616,617],{"class":72,"line":121},[70,618,590],{},[70,620,621],{"class":72,"line":127},[70,622,623],{},"    obj.SetValue(MyAttachedPropertyProperty, value);\n",[70,625,626],{"class":72,"line":133},[70,627,600],{},[629,630,631],"ol",{},[17,632,633,635,636],{},[20,634,500],{},"：\n",[14,637,638,652],{},[17,639,640,641,644,645,505,648,651],{},"布局控件的",[20,642,643],{},"子元素属性","（如 ",[49,646,647],{},"Grid.Row",[49,649,650],{},"Canvas.Left","）。",[17,653,654,655,644,658,651],{},"扩展",[20,656,657],{},"已有控件的功能",[49,659,660],{},"ToolTipService.ToolTip",[662,663,665],"h3",{"id":664},"关键区别",[20,666,664],{},[10,668,669],{},"![[Pasted image 20251213181131.png]]",[662,671,672],{"id":672},"时钟控制法",[10,674,675,676,679,680,651],{},"这个方法的原理核心可以总结为：",[20,677,678],{},"“借鸡生蛋”","（借用动画系统的计算能力，",[20,681,682],{},"手动执行属性赋值",[10,684,685,686,689],{},"通常你写 ",[49,687,688],{},"\u003CDoubleAnimation Storyboard.TargetProperty=\"Width\" ...\u002F>"," 时，WPF 内部发生的事情是：",[629,691,692,702],{},[17,693,694,697,698,701],{},[20,695,696],{},"计算："," 动画系统根据时间（0s -> 1s）和",[20,699,700],{},"缓动函数（Easing）","，算出一个当前的数值（比如 100 -> 150）。",[17,703,704,707,708,711,712,715,716,719],{},[20,705,706],{},"赋值："," 动画系统",[20,709,710],{},"直接","把这个 ",[49,713,714],{},"double"," 数值赋给",[20,717,718],{},"目标属性","。\n但这种方法是对GridLength 没用，因为这个类型体是GridLength而不是double，这个是区别。\n说回到时钟控制上，这个实际上是吧计算和赋值给分开，",[61,721,723],{"className":63,"code":722,"language":65,"meta":66,"style":66},"var animation = new DoubleAnimation { To = 5, ... };\nvar clock = animation.CreateClock(); \u002F\u002F 创建一个时钟\n",[49,724,725,730],{"__ignoreMap":66},[70,726,727],{"class":72,"line":73},[70,728,729],{},"var animation = new DoubleAnimation { To = 5, ... };\n",[70,731,732],{"class":72,"line":79},[70,733,734],{},"var clock = animation.CreateClock(); \u002F\u002F 创建一个时钟\n",[10,736,737],{},"这里这个clock就是计时器，他知道当前的技术，也知道当前的进度，可以给我们计算后面的数值。",[61,739,741],{"className":63,"code":740,"language":65,"meta":66,"style":66},"clock.CurrentTimeInvalidated += (s, e) =>  \n{  \n    var currentVal = (double)clock.CurrentProgress * (To - fromValue) + fromValue;  \n    if (currentVal \u003C 0) currentVal = 0;  \n    if (target is ColumnDefinition c)  \n        c.Width = new GridLength(currentVal, GridUnitType.Star); \u002F\u002F 强制设为 Star 单位  \n    else if (target is RowDefinition r)  \n        r.Height = new GridLength(currentVal, GridUnitType.Star);  \n};\n",[49,742,743,748,753,758,763,768,773,778,783],{"__ignoreMap":66},[70,744,745],{"class":72,"line":73},[70,746,747],{},"clock.CurrentTimeInvalidated += (s, e) =>  \n",[70,749,750],{"class":72,"line":79},[70,751,752],{},"{  \n",[70,754,755],{"class":72,"line":85},[70,756,757],{},"    var currentVal = (double)clock.CurrentProgress * (To - fromValue) + fromValue;  \n",[70,759,760],{"class":72,"line":91},[70,761,762],{},"    if (currentVal \u003C 0) currentVal = 0;  \n",[70,764,765],{"class":72,"line":97},[70,766,767],{},"    if (target is ColumnDefinition c)  \n",[70,769,770],{"class":72,"line":103},[70,771,772],{},"        c.Width = new GridLength(currentVal, GridUnitType.Star); \u002F\u002F 强制设为 Star 单位  \n",[70,774,775],{"class":72,"line":109},[70,776,777],{},"    else if (target is RowDefinition r)  \n",[70,779,780],{"class":72,"line":115},[70,781,782],{},"        r.Height = new GridLength(currentVal, GridUnitType.Star);  \n",[70,784,785],{"class":72,"line":121},[70,786,787],{},"};\n",[10,789,790],{},"这里他会拦截每一个帧数，把进度转换为double，然后计算出当前的数值，然后赋值。它的优势就是分开去计算。",[662,792,793],{"id":793},"传入动画达到复用率",[61,795,797],{"className":63,"code":796,"language":65,"meta":66,"style":66},"public DoubleAnimation Animation { get; set; }  \n  \nprotected override void Invoke(object parameter)  \n{  \n    var target = TargetObject as UIElement ?? AssociatedObject as UIElement;  \n    if (target == null || string.IsNullOrEmpty(PropertyName)) return;  \n    DoubleAnimation animToRun;  \n    if (this.Animation != null)  \n    {        animToRun = this.Animation.Clone();  \n    }    else  \n    {  \n        animToRun = new DoubleAnimation  \n        {  \n            EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }  \n        };    }    if (!double.IsNaN(this.To))  \n    {        animToRun.To = this.To;  \n    }    \u002F\u002F 当 Action 设置了 Duration 时才覆盖  \n    if (this.Duration != default(Duration))  \n    {        animToRun.Duration = this.Duration;  \n    }    target.BeginAnimation(  \n        DependencyPropertyDescriptor.FromName(PropertyName, target.GetType(), target.GetType()).DependencyProperty,  \n        Animation);  \n}\n",[49,798,799,804,809,814,818,823,828,833,838,843,848,853,858,863,868,873,878,883,888,893,898,903,908],{"__ignoreMap":66},[70,800,801],{"class":72,"line":73},[70,802,803],{},"public DoubleAnimation Animation { get; set; }  \n",[70,805,806],{"class":72,"line":79},[70,807,808],{},"  \n",[70,810,811],{"class":72,"line":85},[70,812,813],{},"protected override void Invoke(object parameter)  \n",[70,815,816],{"class":72,"line":91},[70,817,752],{},[70,819,820],{"class":72,"line":97},[70,821,822],{},"    var target = TargetObject as UIElement ?? AssociatedObject as UIElement;  \n",[70,824,825],{"class":72,"line":103},[70,826,827],{},"    if (target == null || string.IsNullOrEmpty(PropertyName)) return;  \n",[70,829,830],{"class":72,"line":109},[70,831,832],{},"    DoubleAnimation animToRun;  \n",[70,834,835],{"class":72,"line":115},[70,836,837],{},"    if (this.Animation != null)  \n",[70,839,840],{"class":72,"line":121},[70,841,842],{},"    {        animToRun = this.Animation.Clone();  \n",[70,844,845],{"class":72,"line":127},[70,846,847],{},"    }    else  \n",[70,849,850],{"class":72,"line":133},[70,851,852],{},"    {  \n",[70,854,855],{"class":72,"line":139},[70,856,857],{},"        animToRun = new DoubleAnimation  \n",[70,859,860],{"class":72,"line":145},[70,861,862],{},"        {  \n",[70,864,865],{"class":72,"line":152},[70,866,867],{},"            EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }  \n",[70,869,870],{"class":72,"line":158},[70,871,872],{},"        };    }    if (!double.IsNaN(this.To))  \n",[70,874,875],{"class":72,"line":164},[70,876,877],{},"    {        animToRun.To = this.To;  \n",[70,879,880],{"class":72,"line":169},[70,881,882],{},"    }    \u002F\u002F 当 Action 设置了 Duration 时才覆盖  \n",[70,884,885],{"class":72,"line":175},[70,886,887],{},"    if (this.Duration != default(Duration))  \n",[70,889,890],{"class":72,"line":181},[70,891,892],{},"    {        animToRun.Duration = this.Duration;  \n",[70,894,895],{"class":72,"line":186},[70,896,897],{},"    }    target.BeginAnimation(  \n",[70,899,900],{"class":72,"line":191},[70,901,902],{},"        DependencyPropertyDescriptor.FromName(PropertyName, target.GetType(), target.GetType()).DependencyProperty,  \n",[70,904,905],{"class":72,"line":197},[70,906,907],{},"        Animation);  \n",[70,909,910],{"class":72,"line":202},[70,911,600],{},[10,913,914],{},"我们定义一个叫做Animation的属性，用来接收，如果没有传入那就使用提前设定好的默认动画。",[10,916,917,918,47,924,927],{},"if (!double.IsNaN(this.To)) : 如果开发者在外层Action上手动填了To的值，",[20,919,920,921,923],{},"在 C# 和 WPF 里，",[49,922,714],{}," 类型的默认值通常是",[49,925,926],{},"0","。",[10,929,930,931,934,935,938,939,942,943,946,947,950,951,927],{},"if (this.Duration != default(Duration))：",[49,932,933],{},"Duration"," 是一个结构体（Struct），它不能为空（null），它的默认状态叫 ",[49,936,937],{},"Automatic","。如果用户在",[20,940,941],{},"外层标签","写了 ",[49,944,945],{},"Duration=\"0:0:1\"","，那么它就不等于默认值，我们就用这个时长",[20,948,949],{},"强制覆盖","内部",[20,952,953],{},"动画的时长",[955,956,957],"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":66,"searchDepth":79,"depth":79,"links":959},[960,961,962],{"id":225,"depth":79,"text":225},{"id":387,"depth":79,"text":387},{"id":512,"depth":79,"text":515,"children":963},[964,965,966],{"id":664,"depth":85,"text":664},{"id":672,"depth":85,"text":672},{"id":793,"depth":85,"text":793},"md",{},"\u002Fcsharp\u002Fwpf\u002F依赖属性和附加属性",{"description":12},"csharp\u002FWPF\u002F依赖属性和附加属性","Mb2dbz6FgdAF4RczEtJdellLvD9TIpEIAgqApP-hzGg",1791042466174]