I am currently working at Uniblue Systems as a .Net Specialist. I love C# and addicted to the WPF technology…. In my blog (http://marlongrech.wordpress.com/) you will find posts mainly on WPF yet sometimes I blog on other .Net stuff…. I also have an OpenSource project AvalonControlsLibrary(http://marlongrech.wordpress.com/) that I constantly work on. Marlon has posted 12 posts at DZone. View Full User Profile

NameScope In C-Sharp And WPF: My Name Is Marlon You Know...

08.05.2008
| 9001 views |
  • submit to reddit

Did you ever wonder why you can have the same name registered for different parts of your XAML? Here is an example (notice the name “border” in different parts of the XAML)

<ContentControl Content="Hello world" x:Name="border">
<ContentControl.ContentTemplate>
<DataTemplate>
<Border BorderBrush="Silver" BorderThickness="1" CornerRadius="5" Margin="20" x:Name="border">
<StackPanel>
<TextBlock Text="I am a DataTemplate" HorizontalAlignment="Center"/>
<ContentPresenter Content="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</StackPanel>
</Border>
</DataTemplate>

</ContentControl.ContentTemplate>

<ContentControl.Template>

<ControlTemplate TargetType="ContentControl">
<Border BorderBrush="Silver" BorderThickness="1" CornerRadius="5" x:Name="border" >
<StackPanel>
<TextBlock Text="I am a ControlTemplate" HorizontalAlignment="Center"/>
<ContentPresenter />
</StackPanel>
</Border>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>

As you can see the XAML above "border" is registered 3 times. This is possible because of WPF Namescopes. Basically the Window(that I am using to put the ContentControl in) has it’s own NameScope the DataTemplate also has it’s own and also the ControlTemplate (some actually implement the INameScope interface themselves).

Namescopes are a very important concept that one must understand. Why? Let me give you an example.

<StackPanel>

<ContentControl Content="Hello world" x:Name="border">

<ContentControl.ContentTemplate>

<DataTemplate>

<Border BorderBrush="Silver" BorderThickness="1" CornerRadius="5" Margin="20" x:Name="border">

<StackPanel>

<TextBlock Text="I am a DataTemplate" HorizontalAlignment="Center"/>

<ContentPresenter Content="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center"/>

<CheckBox x:Name="checkbox" Content="Checkbox here" />

</StackPanel>

</Border>

</DataTemplate>

</ContentControl.ContentTemplate>

</ContentControl>

<ToggleButton Content="Toggle button bound to checkbox" IsChecked="{Binding ElementName=checkbox, Path=IsChecked}"/>

</StackPanel>

The XAML above has a ContentControl that has a ContentTemplate with a CheckBox and a Toggle button that is trying to bind to that checkbox. The binding is using ElementName to try to find the checkbox yet that will NOT be successful because the “checkbox” is registered in a different NameScope (remember a DataTemplate has it’s own NameScope!)

Namescope and Animations

Another scenario where NameScopes are heavily used are animation. When you create a Storyboard you specify a Storyboard.TargetName attached property to specify which element you want to animate. Something like this:

<Window.Resources>

<Storyboard x:Key="animation">

<DoubleAnimation To="50" AutoReverse="True" RepeatBehavior="Forever" Storyboard.TargetName="button" Storyboard.TargetProperty="Height"/>

</Storyboard>

</Window.Resources>


<Window.Triggers>

<EventTrigger RoutedEvent="FrameworkElement.Loaded">

<BeginStoryboard Storyboard="{StaticResource animation}"/>

</EventTrigger>

</Window.Triggers>


<StackPanel>

<ContentControl Content="Hello world" x:Name="border">

<ContentControl.ContentTemplate>

<DataTemplate>

<Border BorderBrush="Silver" BorderThickness="1" CornerRadius="5" Margin="20" x:Name="border">

<StackPanel>

<TextBlock Text="I am a DataTemplate" HorizontalAlignment="Center"/>

<ContentPresenter Content="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center"/>

<CheckBox x:Name="checkbox" Content="Checkbox here" />

</StackPanel>

</Border>

</DataTemplate>

</ContentControl.ContentTemplate>

</ContentControl>

<ToggleButton Content="Toggle button bound to checkbox" Height="25" x:Name="button"/>

</StackPanel>

The above code would execute as expected, the ToggleButton named "button" would start animating it’s height when the window is Loaded. But what if you want to animate something in the DataTemplate? If you try to create a Storyboard in the Resources section of the window to animate the Checkbox  named "checkbox" you would get an InvalidOperationException saying 'checkbox' name cannot be found in the name scope of  'NamescopeExample.Window1'. This is because the checkbox lives in a different NameScope from the storyboard. One thing you can do is to move the Storyboard in the DataTemplate.Resources so that the storyboard would be created in the same Namescope of the checkbox. Something like this:

<DataTemplate>

<DataTemplate.Resources>

<Storyboard x:Key="checkboxAnimation" RepeatBehavior="Forever">

<ColorAnimation To="Red" AutoReverse="True" Storyboard.TargetName="checkbox" Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)"/>

</Storyboard>

</DataTemplate.Resources>

<Border BorderBrush="Silver" BorderThickness="1" CornerRadius="5" Margin="20" x:Name="border">

<StackPanel>

<TextBlock Text="I am a DataTemplate" HorizontalAlignment="Center"/>

<ContentPresenter Content="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center"/>

<CheckBox x:Name="checkbox" Content="Checkbox here" Background="Black" />

</StackPanel>

</Border>

<DataTemplate.Triggers>

<EventTrigger RoutedEvent="FrameworkElement.Loaded">

<BeginStoryboard Storyboard="{StaticResource checkboxAnimation}"/>

</EventTrigger>

</DataTemplate.Triggers>

</DataTemplate>

Namescope and C# code

But what if you are creating elements in code... One would say out "Easy... Just set the name property" like so:

ToggleButton button = new ToggleButton();
button.Height = 25;
button.Content = "Toggle Button";
button.Name = "button";
container.Children.Add(button);

No no no and NO... This does not work! Setting the Name property of the control will not register that Control to the NameScope. So how to do it? Easy.. have a look

ToggleButton button = new ToggleButton();
button.Height = 25;
button.Content = "Toggle Button";
NameScope.GetNameScope(this).RegisterName("button", button);
container.Children.Add(button);

All you have to do is, first find in which NameScope you want to register the element (in this case I want to add it to the Window NameScope), secondly register the element to that NameScope by using the RegisterName method of the INameScope interface.

You can even use the RegisterName method of the FrameworkElement. For example if I want to add a Button to a StackPanel, you can ask the StackPanel to register the name of the Button. In my case I could have done like this:

ToggleButton button = new ToggleButton();
button.Height = 25;
button.Content = "Toggle Button";
container.RegisterName("button", button);
container.Children.Add(button);

Some other tips and related articles…

If you have an element in the Resources section you will not be allowed to set a name for that element. One way of enabling that element with a name is by using the RegisterName method of the NameScope class as explained above (you might want to set the name for animations for example).

You might want to also implement your own NameScope by implementing the INameScope interface. I saw a very clever solution by doing such a thing, by WPF super hero Andrew Smith. Josh Smith the WPF Rock star and his Element Spy is also a brilliant article that must be read.

References
Published at DZone with permission of its author, Marlon Grech. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

rapid force replied on Fri, 2009/01/09 - 7:16pm

Really nice article!!!!!! gave the solution to problem I was facing. Just curious to know, If I am using five elements in my storyboard, do i need to register all the names. As they are part of control template, I have to find them (ofcourse through code!) & then register those objects with their name in top window scope.

 Is there a way(may be some syntax) that I can refer instead of registering name in top window namescope. 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.