Reputation: 474
I searched for this Problem on Stackoverflow, but in my opinion the other Posts do not cover this question.
In my Custom Control i am using a Visual State Manager. Inside the Visual State Manager there is an Animation that Animates the Height of an Element. When i try to bind to the Controls Properties i get following Error on StartUp:
Additional information: Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType=MyNameSpace.MyControl, AncestorLevel='1''. BindingExpression:Path=ActualHeight; DataItem=null; target element is 'DoubleAnimation' (HashCode=562002); target property is 'To' (type 'Nullable`1')
My Control looks like this:
<Style TargetType="{x:Type local:MyControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyControl}">
<Grid x:Name="RootGrid" >
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="someElement"
Storyboard.TargetProperty="Height"
From="0"
To="{Binding RelativeSource={RelativeSource AncestorType=local:MyControl}, Path=CustomControlProperty}"
Duration="0:0:.7" />
...
I tried all ways of Bindings, but it seems that the Animations always takes itself as Scope.
Thanks for your help again.
Upvotes: 3
Views: 2014
Reputation: 13458
Edit I realized my answer doesn't work after all.
See this related discussion https://social.msdn.microsoft.com/Forums/vstudio/en-US/027c364f-5d75-424f-aafd-7fb76b10b676/templatebinding-on-storyboard?forum=wpf
[...] The
To
property andFrom
property ofColorAnimation
can't be bind with, because of those properties need to be frozen (unchangeable) for the animation to work.So please use specific color to instead of binding.
This suggests that some animation elements are not supposed to change via dynamic properties. I actually don't know whether this applies to all animations or just to a specific subset.
So considering @EdPlunkett answer, it might be worth to investigate, whether subsequent property changes are actually reflected in the animation without errors.
You should use {TemplateBinding CustomControlProperty}
for one way binding or {Binding RelativeSource={RelativeSource TemplatedParent},Path=CustomControlProperty}
if you need more binding complexity, instead of trying to find the templated control by other means.
Upvotes: 0
Reputation: 37066
I was able to do this with a BindingProxy
. I find binding proxies to be nonintuitive. Sometimes they work on the first shot; this one took a little trial and error. Also, they're a little bit of a hail-mary workaround.
XAML:
<Grid
x:Name="RootGrid"
>
<Grid.Resources>
<!--
When defined in ControlTemplate.Resources, this failed.
TemplateBinding failed too.
-->
<local:BindingProxy
x:Key="CustomControlPropertyProxy"
Data="{Binding CustomControlProperty, RelativeSource={RelativeSource TemplatedParent}}"
/>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="someElement"
Storyboard.TargetProperty="Height"
From="0"
To="{Binding Data, Source={StaticResource CustomControlPropertyProxy}}"
Duration="0:0:5"
/>
</Storyboard>
C# (stolen, not for the first time, from this answer):
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),
typeof(BindingProxy));
}
Here's another variant of the XAML, in case you end up binding animation properties to more than one property of the templated parent:
<Grid
x:Name="RootGrid"
>
<Grid.Resources>
<local:BindingProxy
x:Key="TemplatedParentProxy"
Data="{Binding ., RelativeSource={RelativeSource TemplatedParent}}"
/>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="someElement"
Storyboard.TargetProperty="Height"
From="0"
To="{Binding Data.CustomControlProperty, Source={StaticResource TemplatedParentProxy}}"
Duration="0:0:5"
/>
</Storyboard>
After ruling out TemplateBinding
and {RelativeSource TemplatedParent}
, my next guess was to bind RootGrid.Tag
to CustomControlProperty
and use To="{Binding Tag, ElementName=RootGrid}"
. That did not work. While intellisense knew about RootGrid
in the XAML designer, the Binding
couldn't find RootGrid
at runtime:
<DoubleAnimation
Storyboard.TargetName="someElement"
Storyboard.TargetProperty="Height"
From="0"
To="{Binding Tag, ElementName=RootGrid, PresentationTraceSources.TraceLevel=High}"
Duration="0:0:1"
/>
Debug trace:
System.Windows.Data Warning: 67 : BindingExpression (hash=15221148): Resolving source
System.Windows.Data Warning: 69 : BindingExpression (hash=15221148): Framework mentor not found
System.Windows.Data Warning: 67 : BindingExpression (hash=15221148): Resolving source (last chance)
System.Windows.Data Warning: 69 : BindingExpression (hash=15221148): Framework mentor not found
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Tag; DataItem=null; target element is 'DoubleAnimation' (HashCode=44950942); target property is 'To' (type 'Nullable`1')
That "governing FrameworkElement or FrameworkContentElement" jazz is the essential problem with all of the other approaches as well. That's where binding proxies come in: Resource lookup isn't limited by that visual tree parent chain stuff.
Upvotes: 3