web-dev-qa-db-fra.com

Comment créer WPF usercontrol qui contient des espaces réservés pour une utilisation ultérieure

Je ferais mieux de poser la question par l'exemple. Disons que j'ai UserControl et Window qui utilise ce contrôle.

Je voudrais concevoir ce contrôle (nommé MyControl) de cette manière (c'est la syntaxe de science-fiction!):

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

et utiliser de telle manière lors de la conception de ma fenêtre:

<MyControl/>

ou

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

ou

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

Bien sûr, j'aimerais avoir la possibilité d'ajouter encore plus d'éléments à MyControl dans Window. Donc, d'une certaine manière, cela devrait fonctionner comme conteneur (comme Grid, StackPanel, etc.). Le placement serait défini dans UserControl (dans cet exemple après le bouton "Juste un bouton") mais quoi ajouter (quels éléments) serait défini dans Window (où UserControl - MyControl - est utilisé).

J'espère que ce que j'aimerais réaliser est clair. Le point clé est d'utiliser XAML lors de la conception de Windows, donc ma classe ne devrait pas être pire que les autres contrôles.

Maintenant, la grande QUESTION est - comment faire?

Remarques: le style est hors de portée. Tout ce que je veux faire, c'est ajouter tous les contrôles que je veux à MyControl lors de la conception de Windows (pas lors de la conception de MyControl).

36
greenoldman

ContentControls & ItemsControls sont bons pour cela, vous pouvez les lier à une propriété de votre UserControl ou les exposer.

Utilisation d'un ContentControl (pour les espaces réservés dans plusieurs endroits déconnectés):

<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 (pour les collections en un seul endroit)

<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>

Avec UserControls, vous pouvez décider d'exposer certaines propriétés des contrôles internes; en plus du ItemsSource on voudra probablement exposer aussi des propriétés comme le ItemsControl.ItemTemplate, mais tout dépend de la façon dont vous souhaitez l'utiliser, si vous définissez simplement le Items, vous n'avez pas nécessairement besoin de tout cela.

49
H.B.

Je pense que vous souhaitez définir le ControlTemplate de votre UserControl avec un ContentPresenter situé à l'intérieur (afin que vous puissiez définir où le contenu sera présenté).

Votre UserControl personnalisé:

<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>

Usage:

<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>

enter image description here

Si vous avez besoin de plusieurs éléments dans votre section de contenu, placez-les simplement dans un conteneur comme une grille ou un panneau de pile:

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