Cómo crear usercontrol de WPF que contiene marcadores de posición para uso posterior
user-controls containers (2)
Será mejor que haga la pregunta con el ejemplo. Digamos que tengo UserControl y Window que usa este control.
Me gustaría diseñar este control (llamado MyControl) de esa manera (¡esta es la sintaxis de ciencia ficción!):
<Grid>
<Button>Just a button</Button>
<PlaceHolder Name="place_holder/>
</Grid>
y usar de esa manera al diseñar mi ventana:
<MyControl/>
o
<MyControl>
<place_holder>
<Button>Button 1</Button>
</place_holder>
</MyControl>
o
<MyControl>
<place_holder>
<Button>Button 1</Button>
<Button>Button 2</Button>
</place_holder>
</MyControl>
Por supuesto, me gustaría tener la capacidad de agregar aún más elementos a MyControl en la ventana. Entonces, de alguna manera debería funcionar como contenedor (como Grid, StackPanel, etc.). La ubicación se definiría en UserControl (en este ejemplo después del botón "Solo un botón"), pero qué agregar (qué elementos) se definiría en Ventana (donde se usa UserControl - MyControl -).
Espero que esto sea claro lo que me gustaría lograr. El punto clave es usar XAML al diseñar Window, por lo que mi clase no debería ser peor que otros controles.
Ahora, la gran PREGUNTA es: ¿cómo hacerlo?
Observaciones: el estilo está fuera de alcance. Todo lo que quiero hacer es agregar cualquier control que desee a MyControl cuando diseñe Windows (no cuando diseñe MyControl).
ContentControls y ItemsControles son buenos para esto, puedes vincularlos a una propiedad de tu UserControl o exponerlos.
Uso de ContentControl (para marcadores de posición en múltiples lugares desconectados):
<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 (para colecciones en un solo lugar)
<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>
Con UserControls puede decidir exponer ciertas propiedades de los controles internos; además de ItemsSource
uno probablemente también quiera exponer propiedades como ItemsControl.ItemTemplate
, pero todo depende de cómo quiera usarlo, si solo establece los Items
entonces no necesariamente necesita nada de eso.
Creo que quiere configurar ControlTemplate de UserControl con un ContentPresenter ubicado dentro (para que pueda definir dónde se presentará el contenido).
Su UserControl personalizado:
<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>
Uso:
<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''s Content" />
</l:UserControl1>
</StackPanel>
</Window>
Si necesita varios elementos en su sección de contenido, simplemente colóquelos en un contenedor como una cuadrícula o un panel de distribución:
<l:UserControl1>
<StackPanel>
<Button Content="Button 1" />
<Button Content="Button 2" />
</StackPanel>
</l:UserControl1>