我有一个附加行为,它具有StoryBoard
类型的单个附加属性。我想在ListView中的每个项目上设置此属性。 XAML看起来像这样:
<Grid>
<Grid.Resources>
<Storyboard x:Key="TheAnimation" x:Shared="False">
<DoubleAnimation From="0.0" To="1.0" Duration="0:0:0.20"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</Grid.Resources>
<ListView>
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="local:MyBehavior.Animation"
Value="{StaticResource TheAnimation}" />
</Style>
</ListView.Resources>
</ListView>
</Grid>
到目前为止一切顺利。那么&#39; MyBehavior&#39;中的代码试图这样做:
private static void AnimationChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var listViewItem = d as ListViewItem;
if (d == null)
return;
var sb = e.NewValue as Storyboard;
if (sb == null)
return;
Storyboard.SetTarget(sb, listViewItem);
sb.Begin();
}
但是对InvalidOperationException
的调用抛出了StoryBoard.SetTarget()
:&#34;无法在对象&#39; System.Windows.Media.Animation.Storyboard&#39;上设置属性。因为它处于只读状态。&#34;如果我检查调试器中的Storyboard
,我可以看到其IsSealed
和IsFrozen
属性都设置为true
。
相比之下,如果我直接在MyBehavior.Animation
上设置ListView
,以便我不需要使用Style
,那么StoryBoard
就会启封,而{I}我能够设置目标并成功运行它。但那不是我想要的地方。
为什么我的StoryBoard
被封存了,我能做些什么来防止这种情况发生?
更新:我可以通过在空检查后添加此项来解决我的问题:
if(sb.IsSealed)
sb = sb.Clone();
但我仍然很好奇发生了什么。显然某个地方(Style
?Setter
?)冻结/密封Setter.Value
中的对象。
答案 0 :(得分:1)
我远不是WPF的专家,所以我无法解释为什么这是微软做出的选择的细节。但据我了解,主要问题是声明为资源的对象可能与多个其他对象共享。因此,您无法修改它。
如果你仍然想要进入资源路径,那么可能你可以将资源视为{DynamicResource...}
而不是{StaticResource...}
,这可能会让你修改已被用于其他对象的对象。正如我所说的,我不是WPF的专家,我承认DynamicResource
和StaticResource
之间的差异仍然有点混乱,但我有一个模糊的回忆,它解决了这种情况。 :)
答案 1 :(得分:1)
我已经对此做了一些研究,并认为我已经解决了大部分正在发生的事情。简短的回答是这种行为是设计的。 .net 4.0的MSDN Styling and Templating页面表示,“一旦应用了某个样式,它就会被密封并且无法更改。”有Style.IsSealed的评论支持这一点。但那是Style
本身;我正在处理Style
的{{1}} Setter
中包含的对象。好吧,Style.Seal将Value
封为所有Setter
,而Setter.Seal封印其Value
。掌握了这些信息(头部),这里发生的一切都不是特别令人震惊。但是,仍然没有解释为什么所有这些密封首先要完成。声称here和here与线程安全相关。这似乎是合理的,但我会进一步推测,如果所有使用特定Style
的对象共享一个Style
对象(并且我不知道是否是这种情况),那么密封可能这样做的原因很简单,你不希望一个消费者修改Style
并意外地改变其他人。
这一切似乎意味着问题没有一般解决方案,需要逐案解决。就我而言,解决方案只是克隆Storyboard
,然后对该克隆进行操作。