dotNed

Welkom bij dotNed Inloggen | Aanmelden | Help
in Zoeken

Dennis' avonturen in .net

Named usercontrols en named children.

Neem even de volgende code. Het is een standaard WPF Window met een Grid en een Button in de Grid.

<Window x:Class="NameScopeTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid Name="myGrid" >
        <Button Name="myButton"/>
    </Grid>
</Window>

Niet echt spannend, zou ik denken. Nou heb ik echter in mijn code een eigen container nodig, om redenen die er even niet toe doen. Ik heb daarvoor een UserControl gemaakt en die ziet er als volgt uit

XAML:

<UserControl x:Class="NameScopeTest.MyContainer"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">
    <Grid>
        <!-- This will hold the child controls -->
        <StackPanel Orientation="Horizontal" Name="myStackPanel"/>        
    </Grid>
</UserControl>

En de bijbehorende C# class:

using System;
using System.Windows.Controls;
 
namespace NameScopeTest
{
    /// <summary>
    /// Interaction logic for MyContainer.xaml
    /// </summary>
    public partial class MyContainer : UserControl
    {
 
        public UIElementCollection Children { get; set; }
        public MyContainer()
        {
            this.Children = new UIElementCollection(this, this);
            InitializeComponent();
        }
    }
 
}

In de Window1 XAML file krijg je dan het volgende:

<Window x:Class="NameScopeTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:myStuff="clr-namespace:NameScopeTest"
    Title="Window1" Height="300" Width="300">
    <Grid Name="myGrid" >
        <myStuff:MyContainer>
            <Button />
        </myStuff:MyContainer>
    </Grid>
</Window>

 

Tot zover geen enkel probleem. Ik heb een namespace reference toegevoegd en vervolgens mijn MyContainer met daarin een Button in de Grid gezet. Het verschil met de eerste xaml file is dat ik in het eerste voorbeeld de elementen een naam gegeven hebt.

Aangezien MyContainer afgeleid is van UserControl hebben we de Name property. Dus die kunnen we zetten, en ook op de button doen we dat gelijk maar even:

<Window x:Class="NameScopeTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:myStuff="clr-namespace:NameScopeTest"
    Title="Window1" Height="300" Width="300">
    <Grid Name="myGrid" >
        <myStuff:MyContainer Name="myContainer" >
            <Button Name="myButton"/>
        </myStuff:MyContainer>
    </Grid>
</Window>

 

Compile, en. helaas. Dit gaat dus niet. Visual Studio geeft me een hele mooie foutmelding:

Error    1    Because 'MyContainer' is implemented in the same assembly, you must set the x:Name attribute rather than the Name attribute. Line 7 Position 30.    C:\Users\Dennis\Documents\Visual Studio 2008\Projects\NameScopeTest\NameScopeTest\Window1.xaml    7    30    NameScopeTest

We krijgen te horen dat we niet de Name property moeten gebruiken maar de x:Name. Deze twee zijn verschillend, maar functioneel doen ze min of meer hetzelfde (x:Name is de instance name van het object dat je maakt, net zoals je in de code behind je variabele een naam geeft. Name is echter een DependencyProperty. Nogmaals: functioneel zijn ze vrijwel gelijk).

Ok, dan passen we de code aan en vervangen we <myStuff:MyContainer Name="myContainer"> door <myStuff:MyContainer x:Name="myContainer">

Compile en. nee.  Nog steeds niet:

Error    1    Cannot set Name attribute value 'myButton' on element 'Button'. 'Button' is under the scope of element 'MyContainer', which already had a name registered when it was defined in another scope. Line 8 Position 21.    C:\Users\Dennis\Documents\Visual Studio 2008\Projects\NameScopeTest\NameScopeTest\Window1.xaml    8    21    NameScopeTest

WTF? Na lang zoeken blijkt dat dit standaard gedrag is. Een UserControl met een XAML erbij kan geen named-child elementen bevatten. Lastig, erg lastig. De oplossing? Geen UserControl als baseclass gebruiken maar Control. En dan de rendering zelf regelen.

Dit werkt dan wel, maar het is wel veel lastiger (je moet nu alles wat je eerst in de XAML van je usercontrol deed zelf doen in de OnRender()). Ik snap niet goed wat de reden van deze beperking is, maar er zal wel over nagedacht zijn Weet iemand de reden voor dit alles?

Published Monday, March 23, 2009 10:33 AM door dvroegop
Filed Under: ,

Comments

 

sevensteps said:

Zeker weten?

Ik gebruik x:Name (als http://schemas.microsoft.com/winfx/2006/xaml tenminste niet de default namespace is :-)) met child elementen met x:Name attribuut zonder problemen. Zie hieronder. Uit mijn hoofd weet ik niet zeker of SurfaceUserControl (waar mijn user control van is afgeleid) een subclass is van UserControl of niet, maar ik denk dat het principe hetzelfde is. Kan het zijn dat het komt omdat je bij de parent Name gebruikt en bij de child x:Name (uit een andere namespace dus)?

<sc:S7SurfaceUserControl
   x:Class="ObjectViewer.S7SurfaceVideoPlayer"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:s="http://schemas.microsoft.com/surface/2008"
   xmlns:sc="clr-namespace:ObjectViewer">

  <UserControl.Resources>
  <Style x:Key="VideoPlayer" TargetType="{x:Type MediaElement}">
<Setter Property="LoadedBehavior" Value="Manual" />
<Setter Property="UnloadedBehavior" Value="Manual"/>
</Style>
</UserControl.Resources>

<Grid Name="main">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="32"/>
</Grid.RowDefinitions>

<MediaElement Stretch="Uniform" x:Name="video" Grid.Row="0" LoadedBehavior="Manual" Style="{StaticResource VideoPlayer}" MediaEnded="video_MediaEnded" Source="Bear.wmv"/>


<Grid Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<s:SurfaceButton Height="32" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" x:Name="PlayButton" Grid.Column="0" Click="PlayButton_Click">
<Image Source="control_play.png" />
</s:SurfaceButton>
<s:SurfaceButton Height="32" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" x:Name="StopButton" Grid.Column="1"  Click="StopButton_Click">
<Image Source="control_stop.png" />
</s:SurfaceButton>
</Grid>
</Grid>

</sc:S7SurfaceUserControl>
April 8, 2009 3:06 PM
Anonymous comments are disabled

About dvroegop

Programmeert al sinds 1982. Is nu werkzaam als software architect bij Detrio Consultancy b.v.
Powered by Community Server, by Telligent Systems