c# - How to access a storyboard within an element resources from XAML? -


consider code:

<usercontrol x:class="myapp.mycontrol"              ...          xmlns:local="clr-namespace:myapp"          datacontext="{binding relativesource={relativesource mode=self}}">      <usercontrol.template>         <controltemplate>             <controltemplate.resources>                 <storyboard x:key="mystory">                     <coloranimationusingkeyframes storyboard.targetproperty="(border.borderbrush).(solidcolorbrush.color)" storyboard.targetname="brdbase">                         <splinecolorkeyframe keytime="0:0:1" value="red"/>                     </coloranimationusingkeyframes>                 </storyboard>             </controltemplate.resources>              <border x:name="brdbase" borderthickness="1" borderbrush="cyan" background="black">                 ...             </border>              <controltemplate.triggers>                 <trigger sourcename="brdbase" property="ismouseover" value="true">                     <trigger.enteractions>                         <beginstoryboard storyboard="{staticresource mystory}"/>                     </trigger.enteractions>                 </trigger>             </controltemplate.triggers>         </controltemplate>     </usercontrol.template> </usercontrol> 

the above code works no problem. now, wanna bind key-frame value of mystory dp (named specialcolor) of user-control so:

<storyboard x:key="mystory">     <coloranimationusingkeyframes storyboard.targetproperty="(border.borderbrush).(solidcolorbrush.color)" storyboard.targetname="brdbase">         <splinecolorkeyframe keytime="0:0:1" value="{binding relativesource={relativesource mode=findancestor, ancestortype={x:type local:mycontrol}}, path=specialcolor}"/>     </coloranimationusingkeyframes> </storyboard> 

which makes error:

cannot freeze storyboard timeline tree use across threads.

it's possible using code behind. how can in xaml only?


code-behind aided solution:

step 1: putting mystory storyboard brdbase resources.

<usercontrol.template>     <controltemplate>         <border x:name="brdbase" borderthickness="1" borderbrush="cyan" background="black">             <border.resources>                 <storyboard x:key="mystory">                     <coloranimationusingkeyframes storyboard.targetproperty="(border.borderbrush).(solidcolorbrush.color)" storyboard.targetname="brdbase">                         <splinecolorkeyframe keytime="0:0:1" value="{binding relativesource={relativesource mode=findancestor, ancestortype={x:type local:mycontrol}}, path=specialcolor}"/>                     </coloranimationusingkeyframes>                 </storyboard>             </border.resources>             ...         </border>          <controltemplate.triggers>             <trigger sourcename="brdbase" property="ismouseover" value="true">                 <trigger.enteractions>                     <beginstoryboard storyboard="{staticresource mystory}"/>                 </trigger.enteractions>             </trigger>         </controltemplate.triggers>     </controltemplate> </usercontrol.template> 

error: cannot find resource named 'mystory'. resource names case sensitive.

step 2: eliminating trigger on ismouseover property , begin mystory code behind.

<usercontrol.template>     <controltemplate>         <border x:name="brdbase" borderthickness="1" borderbrush="cyan" background="black" mouseenter="brdbase_mouseenter">             <border.resources>                 <storyboard x:key="mystory">                     <coloranimationusingkeyframes storyboard.targetproperty="(border.borderbrush).(solidcolorbrush.color)" storyboard.targetname="brdbase">                         <splinecolorkeyframe keytime="0:0:1" value="{binding relativesource={relativesource mode=findancestor, ancestortype={x:type local:mycontrol}}, path=specialcolor}"/>                     </coloranimationusingkeyframes>                 </storyboard>             </border.resources>         </border>     </controltemplate> </usercontrol.template> 

c# code-behind:

private void brdbase_mouseenter(object sender, mouseeventargs e) {    border grdroot = (border)this.template.findname("brdbase", this);    storyboard story = grdroot.resources["mystory"] storyboard;     story.begin(this, this.template); } 

step 3: solution done, doesn't work @ first time. fortunately, there workaround issue. it's enough put controltemplate in style.

(i need other trigger types eventtrigger , must wrap usercontrol elements controltemplate.)


update:

the idea using objectdataprovider failed.

  1. an objectdataprovider resource cannot used provide storyboard!!! error report is:
    • xamlparseexception: set property 'system.windows.media.animation.beginstoryboard.storyboard' threw exception.
    • innerexception: 'system.windows.data.objectdataprovider' not valid value property 'storyboard'.
  2. the associatedcontrol dp null.

here code:

<usercontrol.template>     <controltemplate>         <controltemplate.resources>             <local:storyboardfinder x:key="storyboardfinder1" associatedcontrol="{binding elementname=brdbase}"/>             <objectdataprovider x:key="dataprovider" objectinstance="{staticresource storyboardfinder1}" methodname="finder">                 <objectdataprovider.methodparameters>                     <sys:string>mystory</sys:string>                 </objectdataprovider.methodparameters>             </objectdataprovider>         </controltemplate.resources>          <border x:name="brdbase" borderthickness="1" borderbrush="cyan" background="black">             <border.resources>                 <storyboard x:key="mystory">                     <coloranimationusingkeyframes storyboard.targetproperty="(border.borderbrush).(solidcolorbrush.color)" storyboard.targetname="brdbase">                         <splinecolorkeyframe keytime="0:0:1" value="{binding relativesource={relativesource mode=findancestor, ancestortype={x:type local:mycontrol}}, path=specialcolor}"/>                     </coloranimationusingkeyframes>                 </storyboard>             </border.resources>             ...         </border>          <controltemplate.triggers>             <trigger sourcename="brdbase" property="ismouseover" value="true">                 <trigger.enteractions>                     <beginstoryboard storyboard="{staticresource dataprovider}"/>                 </trigger.enteractions>             </trigger>         </controltemplate.triggers>     </controltemplate> </usercontrol.template> 

the storyboardfinder class:

public class storyboardfinder : dependencyobject {     #region ________________________________________  associatedcontrol      public control associatedcontrol     {         { return (control)getvalue(associatedcontrolproperty); }         set { setvalue(associatedcontrolproperty, value); }     }      public static readonly dependencyproperty associatedcontrolproperty =         dependencyproperty.register("associatedcontrol",                                     typeof(control),                                     typeof(storyboardfinder),                                     new frameworkpropertymetadata(null, frameworkpropertymetadataoptions.none));      #endregion      public storyboard finder(string resourcename)     {         //         // associated control null :(         //         return new storyboard();     } } 

well, can't bind "to" nor from, because storyboard has frozen, in order work efficiently cross-threading.

solution1) simplest solution without hacks(involves code-behind): add mouseover event handler & in event handler, locate necessary animation, set "to" property directly, won't use binding , "freezing" can done. way won't hardcode :).

solution2) there cool hack supports xaml only( little bit of converter magic ofcourse ), not suggest it. it's cool nonetheless :) wpf animation: binding "to" attribute of storyboard animation see answer jason.

there few things more can try:

solution3) don't use dependency properties, rather implement inotifyproperthchanged. way still can bind "to". note think should theoretically work, have not tried.

solution4) apply mode=onetime binding. maybe works?

solution5) write own attached behavior evaluate dependency property on correct thread , set "to" property. think nice solution.

here duplicate too: wpf animation "cannot freeze storyboard timeline tree use across threads"


Comments

Popular posts from this blog

ios - iPhone/iPad different view orientations in different views , and apple approval process -

java Extracting Zip file -

C# WinForm - loading screen -