共用方式為


WPF and Silverlight namescopes and DataContext differences

When authoring a UserControl, you will more often than not end up data binding properties of objects in your XAML file to properties of your UserControl itself. The most elegant way to do this would be without any code-behind at all, using ElementName bindings.

<UserControl x:Class="NameScopeExample.MyUserControl"… x:Name="ucName">
<TextBlock Foreground="White" Text="{Binding MyProperty.Description,ElementName=ucName}"/>

This scenario works well with WPF but is not supported by Silverlight. This is demonstrated in the attached solution, where the element name binding doesn’t return a value in the Silverlight project, but behaves as described with WPF.

image

The reason for this is that Silverlight only supports a single namescope, while WPF can handle nested namescopes for element names: meaning that a Silverlight control can only have a single x:Name. In the example, we are actually trying to give the instance of MyUserControl two distinct names, one in the UserControl’s declaration (MyUserControl.xaml) and another in the parent (MainPage.xaml).

MainPage.xaml
<local:MyUserControl … x:Name="ucNameInMainPage" MyProperty="{Binding MainPageProperty}"/>

MyUserControl.xaml
<UserControl x:Class="NameScopeExample.MyUserControl" … x:Name="ucName">

With Silverlight, MyUserControl will only keep the name given by the least nested control (ucNameInMainPage), thus causing a failure in the resolution of the ElementName binding. Working around this is quite simple, and works equally well for both frameworks: make use of implicit DataContexts and set the context of the topmost element of your XAML file (usually a Grid named “LayoutRoot” in Silverlight). You can do this after the call to InitializeComponent, and modify your bindings so as to remove the ElementName property.

void MyUserControl_Loaded(object sender, RoutedEventArgs e)

{

    // Implicit DataContexts work on both frameworks

    // Les DataContexts implicites fonctionnent sur les deux frameworks

    this.LayoutRoot.DataContext = this;

}

 

<UserControl x:Class="NameScopeExample.MyUserControl"

   xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

   Loaded="MyUserControl_Loaded"

   Width="450" Height="300" x:Name="ucName">

    …

<TextBlock Foreground="White" Text="{Binding MyProperty.Description}"/>

The sample solution attached to this post also demonstrates two tricks I sometimes use:

  • Use a link (https://msdn.microsoft.com/fr-fr/library/9f4t9t92.aspx) so that a source file is shared between a Silverlight and a .NET application (in our case, WPF). In the example, the MyUserControl and MyClass files are compiled independently for each platform.
  • You can escape an opening brace by prefixing it with the {} XAML escape sequence
    <TextBlock Foreground="LightGray" Text="{}{Binding MyProperty,ElementName=ucName}"/>

NameScopeExample.zip