我有一个具有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();
}


但是,对InvalidOperationException的调用会引发StoryBoard.SetTarget():“无法在对象'System.Windows.Media.Animation.Storyboard'上设置属性,因为它处于只读状态。”如果在调试器中检查Storyboard,则可以看到其IsSealedIsFrozen属性都设置为true

相比之下,如果我直接在MyBehavior.Animation上设置ListView而不需要使用Style,则StoryBoard会被密封,并且我可以设置目标并成功运行它。但这不是我想要的。

为什么我的StoryBoard被密封了,我能做些什么来防止这种情况发生?

更新:我可以通过在null检查之后添加以下内容来解决我的问题:

if(sb.IsSealed)
   sb = sb.Clone();


但是我仍然很好奇发生了什么。显然某个地方(StyleSetter?)冻结/密封了Setter.Value中的对象。

最佳答案

我对此进行了一些研究,并认为我已经解决了大部分正在发生的事情。简短的答案是,这种行为是设计使然。 .net 4.0的MSDN Styling and Templating页明确指出“一旦应用样式,它就会被密封,无法更改”。带有Style.IsSealed的注释可以支持此操作。但这就是Style本身。我正在处理StyleSetterValue中包含的对象。好吧,Style.Seal密封所有Setter,而Setter.Seal密封其Value。掌握了这些信息(头),这里发生的一切都没有特别令人震惊。但是,对于为什么所有这些密封工作都是首先进行的,仍然没有任何解释。声明herehere与线程安全有关。这似乎是合理的,但我还要进一步推测,如果所有消耗特定Style的对象共享一个Style对象(并且我不知道是否是这种情况),则可以对您不希望某个使用者修改Style并意外更改其他所有人的简单原因。

所有这一切似乎都意味着无法普遍解决该问题,因此需要逐案解决。就我而言,解决方案只是克隆Storyboard然后对该克隆进行操作。

关于c# - 由样式设置时,StoryBoard对象变为只读,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31487089/

10-11 22:54
查看更多