Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storyboards can't find ControlTemplate elements

I've created some fairly simple XAML, and it works perfectly (at least in KAXML). The storyboards run perfectly when called from within the XAML, but when I try to access them from outside I get the error:

'buttonGlow' name cannot be found in the name scope of 'System.Windows.Controls.Button'.

I am loading the XAML with a stream reader, like this:

Button x = (Button)XamlReader.Load(stream);

And trying to run the Storyboard with:

Storyboard pressedButtonStoryboard =   
    Storyboard)_xamlButton.Template.Resources["ButtonPressed"];
pressedButtonStoryboard.Begin(_xamlButton);

I think that the problem is that fields I am animating are in the template and that storyboard is accessing the button.

Here is the XAML:

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:customControls="clr-namespace:pk_rodoment.SkinningEngine;assembly=pk_rodoment" 
    Width="150" Height="55">
    <Button.Resources>
        <Style TargetType="Button">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Grid Background="#00FFFFFF">
                            <Grid.BitmapEffect>
                                <BitmapEffectGroup>
                                    <OuterGlowBitmapEffect x:Name="buttonGlow" GlowColor="#A0FEDF00" GlowSize="0"/>
                                </BitmapEffectGroup>
                            </Grid.BitmapEffect>
                            <Border x:Name="background" Margin="1,1,1,1" CornerRadius="15">
                                <Border.Background>
                                    <SolidColorBrush Color="#FF0062B6"/>
                                </Border.Background>                                
                            </Border>                            
                            <ContentPresenter HorizontalAlignment="Center"
                                Margin="{TemplateBinding Control.Padding}"
                                VerticalAlignment="Center"
                                Content="{TemplateBinding ContentControl.Content}"
                                ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"/>
                        </Grid>
                        <ControlTemplate.Resources>
                            <Storyboard x:Key="ButtonPressed">
                                <Storyboard.Children>
                                    <DoubleAnimation Duration="0:0:0.4"
                                                  FillBehavior="HoldEnd"
                                                  Storyboard.TargetName="buttonGlow"
                                                  Storyboard.TargetProperty="GlowSize" To="4"/>
                                    <ColorAnimation Duration="0:0:0.6"
                                                  FillBehavior="HoldEnd"
                                                  Storyboard.TargetName="background"
                                                  Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)"
                                                  To="#FF844800"/>
                                </Storyboard.Children>
                            </Storyboard>
                            <Storyboard x:Key="ButtonReleased">
                                <Storyboard.Children>
                                    <DoubleAnimation Duration="0:0:0.2"
                                                  FillBehavior="HoldEnd"
                                                  Storyboard.TargetName="buttonGlow"
                                                  Storyboard.TargetProperty="GlowSize" To="0"/>
                                    <ColorAnimation Duration="0:0:0.2"
                                                  FillBehavior="Stop"
                                                  Storyboard.TargetName="background"
                                                  Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)"
                                                  To="#FF0062B6"/>
                                </Storyboard.Children>
                            </Storyboard>
                        </ControlTemplate.Resources>
                        <ControlTemplate.Triggers>
                            <Trigger Property="ButtonBase.IsPressed" Value="True">
                                <Trigger.EnterActions>
                                    <BeginStoryboard Storyboard="{StaticResource ButtonPressed}"/>
                                </Trigger.EnterActions>
                                <Trigger.ExitActions>
                                    <BeginStoryboard Storyboard="{StaticResource ButtonReleased}"/>
                                </Trigger.ExitActions>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Button.Resources>
    <DockPanel>
        <TextBlock x:Name="TextContent" FontSize="28" Foreground="White" >Test</TextBlock>        
    </DockPanel>
</Button>

Any suggestions from anyone who understands WPF and XAML a lot better than me?

Here is the error stacktrace:

at System.Windows.Media.Animation.Storyboard.ResolveTargetName(String targetName, INameScope nameScope, DependencyObject element)
at System.Windows.Media.Animation.Storyboard.ClockTreeWalkRecursive(Clock currentClock, DependencyObject containingObject, INameScope nameScope, DependencyObject parentObject, String parentObjectName, PropertyPath parentPropertyPath, HandoffBehavior handoffBehavior, HybridDictionary clockMappings, Int64 layer)
at System.Windows.Media.Animation.Storyboard.ClockTreeWalkRecursive(Clock currentClock, DependencyObject containingObject, INameScope nameScope, DependencyObject parentObject, String parentObjectName, PropertyPath parentPropertyPath, HandoffBehavior handoffBehavior, HybridDictionary clockMappings, Int64 layer)
at System.Windows.Media.Animation.Storyboard.BeginCommon(DependencyObject containingObject, INameScope nameScope, HandoffBehavior handoffBehavior, Boolean isControllable, Int64 layer)
at System.Windows.Media.Animation.Storyboard.Begin(FrameworkElement containingObject)
at pk_rodoment.SkinningEngine.ButtonControlWPF._button_MouseDown(Object sender, MouseButtonEventArgs e)
at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
at System.Windows.Input.InputManager.ProcessStagingArea()
at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Boolean isSingleParameter)
at System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.Run()
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at System.Windows.Application.Run(Window window)
at System.Windows.Application.Run()
at ControlTestbed.App.Main() in C:\svnprojects\rodomont\ControlsTestbed\obj\Debug\App.g.cs:line 0
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
like image 684
Kris Erickson Avatar asked Sep 30 '08 04:09

Kris Erickson


4 Answers

Finally found it. When you call Begin on storyboards that reference elements in the ControlTemplate, you must pass in the control template as well.

Changing:

pressedButtonStoryboard.Begin(_xamlButton);

To:

pressedButtonStoryboard.Begin(_xamlButton, _xamlButton.Template);

Fixed everything.

like image 84
Kris Erickson Avatar answered Oct 23 '22 23:10

Kris Erickson


I made it work by restructuring the XAML so that the SolidColorBrush and OuterGlowBitmapEffect were resources of the button and thus referenced are shared by the Storyboards and the elements they're applied to. I retrieved the Storyboard and called Begin() on it just as you did, but here is the modified XAML for the Button:

(Please note the keys "buttonGlow" and "borderBackground" and all StaticResource markup extensions referencing them.)

<Button
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="150"
    Height="55">
    <Button.Resources>
        <OuterGlowBitmapEffect
            x:Key="buttonGlow"
            GlowColor="#A0FEDF00"
            GlowSize="0" />
        <SolidColorBrush
            x:Key="borderBackground"
            Color="#FF0062B6" />
        <Style
            TargetType="Button">
            <Setter
                Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate
                        TargetType="Button">
                        <Grid
                            Name="outerGrid"
                            Background="#00FFFFFF"
                            BitmapEffect="{StaticResource buttonGlow}">
                            <Border
                                x:Name="background"
                                Margin="1,1,1,1"
                                CornerRadius="15"
                                Background="{StaticResource borderBackground}">
                            </Border>
                            <ContentPresenter
                                HorizontalAlignment="Center"
                                Margin="{TemplateBinding Control.Padding}"
                                VerticalAlignment="Center"
                                Content="{TemplateBinding ContentControl.Content}"
                                ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" />
                        </Grid>
                        <ControlTemplate.Resources>
                            <Storyboard
                                x:Key="ButtonPressed">
                                <Storyboard.Children>
                                    <DoubleAnimation
                                        Duration="0:0:0.4"
                                        FillBehavior="HoldEnd"
                                        Storyboard.Target="{StaticResource buttonGlow}"
                                        Storyboard.TargetProperty="GlowSize"
                                        To="4" />
                                    <ColorAnimation
                                        Duration="0:0:0.6"
                                        FillBehavior="HoldEnd"
                                        Storyboard.Target="{StaticResource borderBackground}"
                                        Storyboard.TargetProperty="Color"
                                        To="#FF844800" />
                                </Storyboard.Children>
                            </Storyboard>
                            <Storyboard
                                x:Key="ButtonReleased">
                                <Storyboard.Children>
                                    <DoubleAnimation
                                        Duration="0:0:0.2"
                                        FillBehavior="HoldEnd"
                                        Storyboard.Target="{StaticResource buttonGlow}"
                                        Storyboard.TargetProperty="GlowSize"
                                        To="0" />
                                    <ColorAnimation
                                        Duration="0:0:0.2"
                                        FillBehavior="Stop"
                                        Storyboard.Target="{StaticResource borderBackground}"
                                        Storyboard.TargetProperty="Color"
                                        To="#FF0062B6" />
                                </Storyboard.Children>
                            </Storyboard>
                        </ControlTemplate.Resources>
                        <ControlTemplate.Triggers>
                            <Trigger
                                Property="ButtonBase.IsPressed"
                                Value="True">
                                <Trigger.EnterActions>
                                    <BeginStoryboard
                                        Storyboard="{StaticResource ButtonPressed}" />
                                </Trigger.EnterActions>
                                <Trigger.ExitActions>
                                    <BeginStoryboard
                                        Storyboard="{StaticResource ButtonReleased}" />
                                </Trigger.ExitActions>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Button.Resources>
    <DockPanel>
        <TextBlock
            x:Name="TextContent"
            FontSize="28"
            Foreground="White">Test</TextBlock>
    </DockPanel>
</Button>
like image 25
Joel B Fant Avatar answered Oct 23 '22 23:10

Joel B Fant


I think just had this problem.

Let me refer you to my blog entry on the matter: http://www.cplotts.com/2008/09/26/dr-wpf-namescopes/

Basically, the trick is that you need to call Begin with an argument that is an object in the same name scope that the storyboards are targeting.

In particular, from your sample above, I would try to call Begin and send in a reference to the _background element in your template.

Let me know if this doesn't solve your problem.

Update:

I like Erickson's solution better than mine ... and it worked for me too. I don't know how I missed that overload of the Begin method!

like image 2
cplotts Avatar answered Oct 23 '22 23:10

cplotts


(@ Sam Meldrum) To get STOP working, add 'true for "isControllable" at the begin

pressedButtonStoryboard.Begin(_xamlButton, _xamlButton.Template);

change to

pressedButtonStoryboard.Begin(_xamlButton, _xamlButton.Template,true);

and now

pressedButtonStoryboard.Stop(xamlButton)

will work

like image 1
Emmanuel Avatar answered Oct 23 '22 23:10

Emmanuel