Как создать UserControl с вложенным содержимым?

avatar
mike
8 августа 2021 в 20:16
149
2
0

Я создал следующий элемент управления:

MyContentControl.xaml.cs:

public partial class MyContentControl : UserControl
{
    public MyContentControl()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty MyContentProperty = DependencyProperty.Register(
        "MyContent", typeof(object), typeof(MyContentControl), new PropertyMetadata(default(object)));

    public object MyContent
    {
        get { return (object) GetValue(MyContentProperty); }
        set { SetValue(MyContentProperty, value); }
    }
}

MyContentControl.xaml:

<UserControl x:Class="MyContentControl"
             x:Name="self"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <Border BorderBrush="Gray" BorderThickness="1">
            <ContentPresenter Content="{Binding ElementName=self, Path=MyContent}"/> 
        </Border>
    </Grid>
</UserControl>

И это можно использовать так:

<controls:MyContentControl>
    <controls:MyContentControl.MyContent>
        <TextBox Text="Some text..."/>
    </controls:MyContentControl.MyContent>
</controls:MyContentControl>

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

<controls:MyContentControl>
    <TextBox Text="Some text..."/>
</controls:MyContentControl>

Я хотел бы определить внутреннее содержимое, например, как для StackPanel.

Как этого добиться?

Источник
XAMlMAX
8 августа 2021 в 21:31
0

Есть ли какая-то конкретная причина для имитации того, что будут делать StackPanel, DockPanel, Grid, Canvas, TabPanel?

mike
8 августа 2021 в 23:17
0

@XAMlMAX: Да, я хотел бы иметь возможность просто обернуть существующие элементы управления в свой пользовательский элемент управления, например. для предоставления индивидуального дизайна или других функций. Есть ли способ лучше?

XAMlMAX
9 августа 2021 в 00:47
0

Ага, понятно. Помогут ли WrapPanel или UniformGrid? Потому что, если вы хотите иметь пользовательскую логику, например, добавление элементов управления в определенную позицию, тогда лучшим вариантом будет пользовательский элемент управления, полученный из Panel. Для «другой функциональности» я бы предложил прикрепленное свойство. Таким образом, вы можете повторно использовать его в других местах.

mike
9 августа 2021 в 17:07
0

Если я наследую от Panel, могу ли я иметь свой собственный XAML?

Andy
9 августа 2021 в 17:12
0

Вы можете наследовать от управления контентом, а не от пользовательского управления. См. строку редактирования здесь social.technet.microsoft.com/wiki/contents/articles/…

mike
9 августа 2021 в 17:19
0

@Andy: Как и в случае с предложением Panel: (как) я могу иметь свой собственный XAML?

Andy
9 августа 2021 в 17:28
0

Вы не прошли по ссылке?

mike
9 августа 2021 в 18:36
0

@Энди: Извини за ответ, теперь я вижу это. Спасибо. Это требует организации компонентов немного по-другому, но может работать. Я проверю и посмотрю, есть ли у меня те же проблемы с именованными элементами, см. комментарий к ответу lidqy ниже.

Andy
9 августа 2021 в 18:58
0

Я не понимаю, что вы имеете в виду по поводу именованных элементов. Каждый элемент управления имеет свой собственный контекст для имен, поэтому каждый экземпляр editrow имеет контекст. Когда вы определяете элемент управления и делаете его содержимым строки редактирования, он не находится в этом контексте имени строки редактирования. Если вы хотите ссылаться на контент из строки редактирования, вы можете использовать content.property для ссылки. Из содержимого вы можете использовать относительный источник.

Ответы (2)

avatar
mike
9 августа 2021 в 20:42
0

Хотя ответ lidqy был наиболее близок к тому, что я изначально планировал, я в итоге последовал предложению Энди из комментариев к вопросу.

Ключ к этому подходу: желаемый пользовательский XAML определяется в стиле/шаблоне в любом доступном словаре ресурсов:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:controls="clr-namespace:MyControls">
    <Style TargetType="{x:Type controls:MyContentControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:MyContentControl}">
                        <Grid>
                            <Border BorderBrush="Gray" BorderThickness="1">
                                <ContentPresenter/> 
                            </Border>
                        </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Класс MyContentControl должен наследовать только ContentControl и не должен быть файлом UserControl (с .xaml и xaml.cs).

@Andy: Что касается вопроса об «именованных элементах»: исключение/ошибка: «Ошибка MC3093: невозможно установить значение атрибута имени «xxx» для элемента «yyy». «yyy» находится в области действия элемента «MyContentControl». , у которого уже было зарегистрировано имя, когда оно было определено в другой области.", очень похоже на здесь.

@lidqy: Я также подумал: "Почему бы просто не использовать свойство Content?", но при попытке я, по-видимому, создал какую-то рекурсивную привязку.

Спасибо вам обоим! :о)

ПРИМЕЧАНИЕ. Приведенный выше подход должен работать для всех ContentControl, включая UserControl, позволяя повторно создавать шаблоны практически любого пользовательского элемента управления ;o)

.
XAMlMAX
10 августа 2021 в 08:26
0

Таким образом, вам вообще не нужен пользовательский элемент управления. Вы можете просто определить этот стиль для элемента управления содержимым. И содержание этого может быть определено в шаблоне данных.

mike
10 августа 2021 в 19:35
1

@XAMlMAX: Совершенно верно. В моем случае пользовательский элемент управления уже существует, вопрос должен быть (теперь я знаю): (Как) я могу повторно создать шаблон элемента управления? Иногда WPF действительно преподносит приятные сюрпризы ;о)

avatar
lidqy
8 августа 2021 в 20:41
0

Вам необходимо дополнить класс MyContentControl атрибутом ContentPropertyAttribute https://docs.microsoft.com/de-de/dotnet/api/system.windows.markup.contentpropertyattribute?view=net- 5.0

[ContentProperty("MyContent")]
public partial class MyContentControl : UserControl
{ 
   ... 

Тогда вы сможете напрямую добавлять содержимое без явного указания "<controls:MyContentControl.MyContent>" в синтаксисе элемента свойства. Так что приведенная ниже разметка должна анализироваться и быть действительной:

<controls:MyContentControl>
    <TextBox Text="Some text..."/>
</controls:MyContentControl>
mike
9 августа 2021 в 18:30
0

Это работает нормально, пока в обернутом содержимом нет именованных элементов, как в этой проблеме.

lidqy
9 августа 2021 в 20:18
0

Кстати, зачем вам «MyContent»? UserControl — это ContentControl. Таким образом, вы можете/должны просто использовать свойство UserControls Content...