Как вложить пользовательские элементы 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

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

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


person Giffyguy    schedule 23.07.2009    source источник


Ответы (3)


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

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

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

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

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

public class ElementType : Grid
{
}

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

person Ludovic Chabant    schedule 23.07.2009
comment
Это тоже интересно, но по-прежнему не позволяет мне давать родительскому и дочернему объектам ElementType разные имена. Кто-нибудь знает, как я могу назвать их отдельно друг от друга? - person Giffyguy; 24.07.2009
comment
Ах да, извините... Я закончу свой ответ выше. - person Ludovic Chabant; 24.07.2009

Если вы хотите, чтобы один внутри другого, вы хотели бы поместить внутренний в свойство 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>

Кроме того, я не уверен, чего вы пытаетесь достичь здесь, но я боюсь этого.

person Anderson Imes    schedule 23.07.2009
comment
Похоже, это правильное направление. Относительно вашего последнего приговора: ХА! Я намеренно опустил все, что могло бы выдать подробности --- поверьте мне, вы не хотите знать. Что касается вашего решения, я получаю сообщение об ошибке: присоединяемое свойство «Содержимое» не было найдено в типе «ElementType». Я пробовал вместо него Grid.Content и получаю примерно ту же ошибку. Любые дальнейшие идеи? - person Giffyguy; 24.07.2009
comment
Я имел в виду детей, а не содержание. Хотя, похоже, ты понял. - person Anderson Imes; 24.07.2009

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

в XAML:

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

in 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;
        }
    }
person Steffen Gehring    schedule 16.07.2014
comment
Что такое класс поведения? Что я:? - person Johan Aspeling; 11.01.2016