Как вложить пользовательские элементы XAML?

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Title="Window1">
    <Grid>
        <local:ElementType x:Name="FirstElementName">
            <local:ElementType x:Name="SecondElementName" Grid.Column="1" Grid.Row="1" />
        </local:ElementType>
    </Grid>
</Window>  

и это в других файлах ...

<Grid x:Name="InternalElementName" x:Class="WpfApplication1.ElementType"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1">
</Grid>  

и ...

public partial class ElementType : System.Windows.Controls.Grid { }  

все работает нормально, кроме второй элемент.
Я получаю сообщение об ошибке:
невозможно задать значение атрибута Name ' SecondElementName 'для элемента'ElementType'. "ElementType" находится в области элемента "ElementType", у которого уже было зарегистрировано имя, когда оно было определено в другой области.

пользовательские сетки определены правильно. Код будет компилироваться и запускаться, если я достану свойство - - -

x:Name="SecondElementName"  

- - - в Window1.в XAML

что вызывает эту ошибку? Как мне обойти это? Мне нужно вложить одну из этих пользовательских сеток в другую, и мне нужны имена на обеих, чтобы я мог привязать их к отдельным данным.

спасибо заранее.

3 ответов


чтобы узнать, что делать с вложенными объектами разметки, анализатор XAML, среди прочего, рассмотрит, определяет ли класс .NET свойство "content" по умолчанию для использования в качестве контейнера для таких детей. Это делается с "отличным".

в вашем случае, поскольку я предполагаю, что вы хотите, чтобы вложенные объекты входили в сетку, и поскольку дочерние элементы сетки входят в коллекцию свойств "дети", вам просто нужно сделать следующее:

[ContentProperty("Children")]
public partial class ElementType : Grid
{
    // your code here...
}

Если вам нужно сделать некоторую логику при добавлении дочерних элементов в элемент управления (например, разрешить только определенные типы быть дочерними элементами элемента управления ElementType), вы можете наследовать от IAddChild и реализовать методы AddChild и AddText.

Что касается проблемы именования, кажется, что только lookless элементы управления могут иметь имена детей в области экземпляра. Таким образом, вы можете назвать детей внутри ElementType.xaml, но не именованные дочерние элементы в другой разметке, где создается экземпляр ElementType. Я думаю, что это из-за того, как они оптимизируют логическое дерево или что-то. Контроль lookless, с другой стороны, это контроль только код. Поэтому, если вы превратите свой класс в простой старый пустой подкласс Grid, он работает:

public class ElementType : Grid
{
}

Ура! Меньше кода!


Если вы хотите один в другом, вы хотите поместить внутренний в свойство Content первого:

<local:ElementType x:Name="FirstElementName">
    <local:ElementType.Content>
        <local:ElementType x:Name="SecondElementName" Grid.Column="1" Grid.Row="1" />
    </local:ElementType.Content>
</local:ElementType>

Если вы не хотите изменять UserControl, используйте прикрепленное поведение. Так что вам просто нужно это там, где XAML-компиляция не удалась! Только 1 поведение для каждого UserControl, который создает проблемы.

в XAML:

 <preview:PreviewControl>
    <i:Interaction.Behaviors>
       <behaviors:UserControlNameBehavior Name="ICanSetNames"/>
    </i:Interaction.Behaviors>
 </preview:PreviewControl>

в C#:

  public class UserControlNameBehavior : Behavior<UserControl>
    {
        public string Name { get; set; }

        protected override void OnAttached()
        {
            this.AssociatedObject.Loaded += OnLoaded;
            base.OnAttached();
        }

        private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
        {
            this.AssociatedObject.Name = this.Name;
        }
    }