2015-07-18 8 views
0

У меня есть прикрепленное поведение, которое имеет одно присоединенное свойство типа StoryBoard. Я хочу установить это свойство для каждого элемента в ListView. XAML выглядит примерно так:Объект StoryBoard становится доступным только для чтения, если задано стилем

<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 в отладчике, я вижу, что для его свойств IsSealed и IsFrozen установлено значение true.

В противоположность этому, если я изложу MyBehavior.Animation непосредственно на ListView, так что мне не нужно использовать Style, то StoryBoard прибывает вскрыты, и я могу поставить перед собой цель и запустить его успешно. Но я не хочу этого делать.

Почему мой StoryBoard запечатан, и есть ли что-нибудь, что я могу сделать, чтобы предотвратить это?

Обновление: Я могу решить мою проблему, добавив это право после нулевой проверки:

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

Но я все еще интересно, что происходит. Видимо что-то где-то (Style? Setter?) Замерзает/запечатывает объект в Setter.Value.

+0

Это имеет смысл, когда вы отступаете и думаете об этом (я думаю). Xaml 'Style' узел предположительно переводится в один объект' Style'. Этот единственный объект вместе с объектами «Value» в его «Setter's» потенциально распределяется между несколькими объектами. Поэтому, если один из них каким-то образом перехватывает объект «Value» («PropertyChangedCalledback» в моем случае) и изменяет его, он будет непреднамеренно влиять на все остальные объекты, которые разделяют стиль. Разработчики инфраструктуры ожидали этого и запечатали «Value's», чтобы убедиться, что этого не произойдет. По крайней мере, это мое предположение. – dlf

+0

Этот процесс мыслей привел меня к этому [связанный вопрос] (http://stackoverflow.com/q/9235428/3549027) – dlf

+0

[Также по теме] (http://www.pcreview.co.uk/threads/wpf- using-storyboards in-custom-control.3591259 /) – dlf

ответ

1

Я далек от эксперта в WPF, поэтому я не могу объяснить более тонкие детали того, почему это был выбор Microsoft. Но, как я понимаю, основная проблема заключается в том, что объект, объявленный как ресурс, скорее всего, будет использоваться совместно с несколькими другими объектами. Таким образом, вам запрещено изменять его.

Если вы все еще хотите, чтобы идти по пути ресурсов, то возможно, что вы можете обращаться к ресурсу, как {DynamicResource...} вместо {StaticResource...} и что может позволить вам изменить объект, который был использован для другого объекта. Как я уже сказал, я не эксперт в WPF, и я признаю, что все еще немного облачно на разных между DynamicResource и StaticResource, но у меня есть смутное воспоминание о том, что он рассматривает этот сценарий. :)

+0

«DynamicResource» был одной из вещей, которые я пробовал, но это не помогло. Но посмотрите мой комментарий с оригинальным вопросом; Я понимаю, что происходит. – dlf

1

Я провел некоторое исследование по этому вопросу и думаю, что я разработал большую часть того, что происходит. Короткий ответ заключается в том, что это поведение по дизайну. На странице MSDN Styling and Templating .net 4.0 говорится, что «когда стиль был применен, он запечатан и не может быть изменен». Комментарии с Style.IsSealed заново. Но это сам Style; Я имею дело с объектом, содержащимся в StyleSetterValue. Хорошо, Style.Seal уплотняет все его Setter s, и Setter.Seal печатает его Value. С этой информацией в руке (голова) ничто из того, что здесь произошло, особенно шокирует. Но до сих пор нет объяснений, почему все это уплотнение выполняется в первую очередь. Имеются формулы: here и here, что связано с безопасностью потока.Это кажется разумным, но я бы предположил далее, что если все объекты, которые потребляют определенный Style, делят один объект Style (и я не знаю, так ли это или нет), печать может быть выполнена по той простой причине, что вы не хотите, чтобы один потребитель изменял Style и случайно менял всех остальных.

Все это, по-видимому, означает, что общее решение проблемы отсутствует, и ее необходимо будет решать в каждом конкретном случае. В моем случае решение было просто клонировать Storyboard, а затем работать с этим клоном.

+0

Как я уже упоминал, вероятная причина заключается в том, что объекты, предназначенные для совместного использования, не изменяются (т. Е. Затрагивают многие зависимые объекты). Это может быть проблемой производительности (слишком много изменений, распространяющихся через систему привязки) или просто проблема использования API. Это не проблема с потоками, потому что объекты WPF привязаны к определенному потоку и не могут использоваться в разных потоках. –