Подтвердить что ты не робот

Как создать пользовательский контроль WPF, который содержит заполнители для последующего использования

Мне лучше спросить вопрос на примере. Скажем, у меня есть UserControl и Window, который использует этот элемент управления.

Я хотел бы спроектировать этот элемент управления (с именем MyControl) таким образом (это си-си-синтаксис!):

<Grid>
  <Button>Just a button</Button>
  <PlaceHolder Name="place_holder/>
</Grid> 

и использовать таким образом при разработке моего окна:

<MyControl/>

или

<MyControl>
  <place_holder>
    <Button>Button 1</Button>
  </place_holder>
</MyControl> 

или

<MyControl>
  <place_holder>
    <Button>Button 1</Button>
    <Button>Button 2</Button>
  </place_holder>
</MyControl> 

Конечно, я хотел бы иметь возможность добавлять еще больше элементов в MyControl в Window. Таким образом, он должен работать как контейнер (например, Grid, StackPanel и т.д.). Размещение будет определено в UserControl (в этом примере после кнопки "Только одна кнопка" ), но что добавить (какие элементы) будут определены в окне (где используется UserControl - MyControl).

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

Теперь большой ВОПРОС - как это сделать?

Примечания: стиль не входит в объем. Все, что я хочу сделать, это добавить любые элементы управления, которые я хочу использовать MyControl при разработке Window (не при разработке MyControl).

4b9b3361

Ответ 1

ContentControls и ItemsControls хороши для этого, вы можете связать их с свойством UserControl или выставить их.

Использование ContentControl (для заполнителей в нескольких отключенных местах):

<UserControl x:Class="Test.UserControls.MyUserControl2"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             Name="control">
    <Grid>
        <Button>Just a button</Button>
        <ContentControl Content="{Binding PlaceHolder1, ElementName=control}"/>
    </Grid>
</UserControl>
public partial class MyUserControl2 : UserControl
{
    public static readonly DependencyProperty PlaceHolder1Property =
        DependencyProperty.Register("PlaceHolder1", typeof(object), typeof(MyUserControl2), new UIPropertyMetadata(null));
    public object PlaceHolder1
    {
        get { return (object)GetValue(PlaceHolder1Property); }
        set { SetValue(PlaceHolder1Property, value); }
    }

    public MyUserControl2()
    {
        InitializeComponent();
    }
}
<uc:MyUserControl2>
    <uc:MyUserControl2.PlaceHolder1>
        <TextBlock Text="Test"/>
    </uc:MyUserControl2.PlaceHolder1>
</uc:MyUserControl2>

ItemsControl-Version (для коллекций в одном месте)

<UserControl x:Class="Test.UserControls.MyUserControl2"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             Name="control">
    <Grid>
        <Button>Just a button</Button>
        <ItemsControl Name="_itemsControl" ItemsSource="{Binding ItemsSource, ElementName=control}"/>
    </Grid>
</UserControl>
[ContentProperty("Items")]
public partial class MyUserControl2 : UserControl
{
    public static readonly DependencyProperty ItemsSourceProperty = 
        ItemsControl.ItemsSourceProperty.AddOwner(typeof(MyUserControl2));
    public IEnumerable ItemsSource
    {
        get { return (IEnumerable)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public ItemCollection Items
    {
        get { return _itemsControl.Items; }
    }

    public MyUserControl2()
    {
        InitializeComponent();
    }
}
<uc:MyUserControl2>
    <TextBlock Text="Test"/>
    <TextBlock Text="Test"/>
</uc:MyUserControl2>

С помощью UserControls вы можете решить выявить определенные свойства внутренних элементов управления; кроме ItemsSource, вероятно, захочется также выставить такие свойства, как ItemsControl.ItemTemplate, но все зависит от того, как вы хотите его использовать, если вы просто установите Items, то вам не обязательно нужно что-либо из этого.

Ответ 2

Я думаю, вы хотите установить свой UserControl ControlTemplate с помощью ContentPresenter, который находится внутри (так что вы можете определить, где будет представлен контент).

Пользовательский UserControl:

<UserControl x:Class="TestApp11.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Template>
        <ControlTemplate>
            <StackPanel>
                <TextBlock Text="Custom Control Text Area 1" />
                <ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
                <TextBlock Text="Custom Control Text Area 2" />
            </StackPanel>
        </ControlTemplate>
    </UserControl.Template>
</UserControl>

Использование:

<Window x:Class="TestApp11.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:l="clr-namespace:TestApp11"
    Title="Window1" Height="250" Width="200">
    <StackPanel>
        <l:UserControl1>
            <Button Content="My Control Content" />
        </l:UserControl1>
    </StackPanel>
</Window>

enter image description here

Если вам нужно несколько элементов в разделе содержимого, просто поместите их в контейнер, например, в сетку или в стеке:

<l:UserControl1>
    <StackPanel>
        <Button Content="Button 1" />
        <Button Content="Button 2" />
    </StackPanel>
</l:UserControl1>