Why: InvalidOperationException name cannot be found in the name scope of 'System.Windows.Controls.ControlTemplate'
(edit : correction following Nick's comment, about which thread actually creates the elements)
This exception is confusing for multiple reasons: the code causing it does works on some occasions, the storyboard(s) referencing the infringing name are valid, an element does exist with the name, and has been declared prior to its reference in the storyboard(s) in XAML. This post describes one of the cause for this exception:
Below is a XAML snippet demonstrating the issue (available in the project accompanying this article)
<EventTrigger RoutedEvent="ButtonBase.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<!-- The reference to myRectangle can fail! -->
<ColorAnimation Storyboard.TargetName="myRectangle"
Storyboard.TargetProperty="Fill.Color"
To="Blue" AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
and
Button btn = new Button();
btn.Style = this.FindResource("myFailingStyle") as Style;
gbxNewParent.Content = btn;
btn.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent)); // Crash
At first glance, everything should work:
- The button is created and added to the GroupBox
- The Click event is fired on the button
- which in turns triggers the EventTrigger which will animation the myRectangle element of the button's ControlTemplate
Unfortunately, running the program will cause an InvalidOperationException. The reason? The threads responsible for WPF's layout. As opposed to Windows Forms -which is completely single threaded- the WPF runtime does not wait until everything is created and rendered to give back control to the UI thread. In this example, and as Nick Kramer explains in his post, the UI thread queues messages that will be handled by the very same thread, but at a later time (during the layout pass). In our case, the ControlTemplate's myRectangle element has not yet been created when the storyboard tries to reference it, thus causing the exception. As a side note, no exception is thrown when changing the button's parent, as it does not cause a complete re-creation of the control.
As of 3.5 SP1, there is unfortunately no way to determine exactly when an element has been created by the rendering thread. The workaround I use is quite simple, queue the RaiseEvent call to the Dispatcher thread with a (very) low priority.
this.Dispatcher.BeginInvoke(
new Action(delegate { btn.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent)); }),
System.Windows.Threading.DispatcherPriority.ContextIdle);
Another workaround, provided by Nick himself (thanks!), is to call UpdateLayout() prior to raising the event.
Button btn = new Button();
btn.Style = this.FindResource("myFailingStyle") as Style;
gbxNewParent.Content = btn;
UpdateLayout();
btn.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent)); // No crash
Comments
Anonymous
December 01, 2008
PingBack from http://blog.a-foton.ru/index.php/2008/12/02/why-invalidoperationexception-name-cannot-be-found-in-the-name-scope-of-systemwindowscontrolscontroltemplate/Anonymous
December 02, 2008
Actually, this is independent of the render thread/UI thread split, all the interesting stuff in this example is happening on the UI thread: element creation, FindName, template instantiation, animations ticking, etc. Sounds like what you're seeing is the animation is being triggered before the button's template has been instantiated -- instantiation happens during layout, which is done asynchronously but on the UI thread. Your workaround is correct -- calling Dispatcher.BeginInvoke with a priority lower than layout. Another approach would be to call UpdateLayout() before doing the step that triggers the animation.Anonymous
December 02, 2008
Hello Nick, thanks a lot for clearing things up.