StoryBoard对象被样式设置后变为只读
本文关键字:只读 设置 对象 样式 StoryBoard | 更新日期: 2023-09-27 18:03:23
我有一个附加行为,它有一个类型为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>
到目前为止一切顺利。然后'MyBehavior'中的代码尝试这样做:
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();
}
但是在调用StoryBoard.SetTarget()
时抛出了InvalidOperationException
:"不能在对象上设置属性"System.Windows.Media.Animation。"因为它处于只读状态"如果我在调试器中检查Storyboard
,我可以看到它的IsSealed
和IsFrozen
属性都设置为true
。
相反,如果我直接在ListView
上设置MyBehavior.Animation
,这样我就不需要使用Style
,那么StoryBoard
到达时是未密封的,我就能够设置目标并成功运行它。但那不是我想要的地方。
为什么我的StoryBoard
被密封,我能做些什么来防止这种情况吗?
更新:我可以通过在null检查后添加这个来解决我的问题:
if(sb.IsSealed)
sb = sb.Clone();
但是我还是很好奇发生了什么。显然是某个地方(Style
?)Setter
?)正在冻结/密封Setter.Value
中的对象
我不是WPF方面的专家,所以我无法详细解释为什么微软选择了这个。但据我所知,主要问题是被声明为资源的对象很可能与多个其他对象共享。因此,您不能修改它。
如果您仍然想要走资源路线,可能您可以将资源视为{DynamicResource...}
而不是{StaticResource...}
,这可能允许您修改已用于其他对象的对象。正如我所说,我不是WPF的专家,我承认在DynamicResource
和StaticResource
之间的区别上仍然有点模糊,但我有一个模糊的回忆,它解决了这种情况。:)
我对此做了一些研究,我想我已经弄清楚了发生的大部分事情。简单地说,这种行为是设计出来的。.net 4.0的MSDN样式和模板页面直截了当地说:"一旦样式被应用,它就被密封,不能被更改。"有风格的评论。我把它封起来了。但这就是Style
本身;我正在处理一个包含在Style
的Setter
的Value
中的对象。嗯,风格。印章印章所有的Setter
s,和Setter。Seal密封其Value
。有了这些信息,这里发生的一切都不是特别令人震惊。但目前还没有解释为什么要首先进行所有这些密封。这里和这里都有人声称它与线程安全有关。这似乎是合理的,但我进一步推测,如果所有使用特定Style
的对象共享单个Style
对象(我不知道情况是否如此),密封可能是因为您不希望一个消费者修改Style
并意外地改变其他所有人。
所有这些似乎都意味着这个问题没有通用的解决方案,它需要逐个解决。在我的例子中,解决方案只是克隆Storyboard
,然后对该克隆进行操作。