Comment créer un contrôle D'utilisateur WPF avec du contenu nommé

j'ai un ensemble de contrôles avec des commandes attachées et de la logique qui sont constamment réutilisés de la même manière. J'ai décidé de créer un contrôle d'utilisateur qui contient tous les contrôles communs et la logique.

cependant j'ai aussi besoin de la commande pour être en mesure de tenir le contenu qui peut être nommé. J'ai essayé le suivant:

<UserControl.ContentTemplate>
    <DataTemplate>
        <Button>a reused button</Button>
        <ContentPresenter Content="{TemplateBinding Content}"/>
        <Button>a reused button</Button>
    </DataTemplate>
</UserControl.ContentTemplate>

cependant, il semble que tout contenu placé à l'intérieur du contrôle de l'utilisateur ne peut pas être nommé. Par exemple si j'utilise le contrôle dans les éléments suivants way:

<lib:UserControl1>
     <Button Name="buttonName">content</Button>
</lib:UserControl1>

je reçois l'erreur suivante:

ne peut pas définir la valeur de L'attribut Name 'buttonName' sur l'élément "Bouton". "Bouton" est dans le champ d'application de l'élément "UserControl1" , qui disposait déjà d'un dénomination enregistrée lorsqu'il a été défini en un autre champ d'application.

si je supprime le nom de la fesse, alors il compile, cependant j'ai besoin de pouvoir nommer le contenu. Comment puis-je y parvenir?

78
demandé sur H.B. 2009-04-15 15:41:25

9 réponses

la réponse est de ne pas utiliser un UserControl pour le faire.

créer une classe qui étend ContentControl

public class MyFunkyControl : ContentControl
{
    public static readonly DependencyProperty HeadingProperty =
        DependencyProperty.Register("Heading", typeof(string),
        typeof(HeadingContainer), new PropertyMetadata(HeadingChanged));

    private static void HeadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((HeadingContainer) d).Heading = e.NewValue as string;
    }

    public string Heading { get; set; }
}

, puis utiliser un style pour spécifier le contenu

<Style TargetType="control:MyFunkyControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="control:MyFunkyContainer">
                <Grid>
                    <ContentControl Content="{TemplateBinding Content}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

et enfin-l'utiliser

<control:MyFunkyControl Heading="Some heading!">            
    <Label Name="WithAName">Some cool content</Label>
</control:MyFunkyControl>
32
répondu Sybrand 2011-05-28 18:04:33

il semble que ce ne soit pas possible lorsque XAML est utilisé. Contrôles personnalisés semblent être exagéré quand j'ai fait tous les contrôles dont j'ai besoin, mais juste besoin de les regrouper avec un petit peu de logique et de permettre nommé le contenu.

la solution sur le blog de JD comme mackenir suggère, semble avoir le meilleur compromis. Une façon d'étendre la solution de JD pour permettre que les contrôles soient encore définis dans XAML pourrait être la suivante:

    protected override void OnInitialized(EventArgs e)
    {
        base.OnInitialized(e);

        var grid = new Grid();
        var content = new ContentPresenter
                          {
                              Content = Content
                          };

        var userControl = new UserControlDefinedInXAML();
        userControl.aStackPanel.Children.Add(content);

        grid.Children.Add(userControl);
        Content = grid;           
    }

dans mon exemple ci-dessus, j'ai créé un contrôle d'utilisateur appelé UserControlDefinedInXAML qui est défini comme tout contrôle d'utilisateur normal utilisant XAML. Dans mon UserControlDefinedInXAML J'ai un StackPanel appelé aStackPanel dans lequel je veux que mon contenu nommé apparaître.

16
répondu Ryan 2009-04-16 20:47:17

une autre alternative que j'ai utilisée est de simplement définir la propriété Name dans l'événement Loaded .

dans mon cas, j'avais un contrôle assez complexe que je ne voulais pas créer dans le code-derrière, et il cherchait un contrôle optionnel avec un nom spécifique pour certains comportements, et puisque j'ai remarqué que je pouvais définir le nom dans un DataTemplate j'ai pensé que je pouvais le faire dans l'événement Loaded aussi.

private void Button_Loaded(object sender, RoutedEventArgs e)
{
    Button b = sender as Button;
    b.Name = "buttonName";
}
3
répondu Rachel 2011-08-23 20:10:59

parfois, vous pourriez juste avoir besoin de faire référence à l'élément de C#. Selon le cas d'utilisation, vous pouvez alors définir un x:Uid au lieu d'un x:Name et accéder aux éléments en appelant une méthode Uid finder comme Get object par son Uid dans WPF .

3
répondu Dani 2017-05-23 10:31:06

vous pouvez utiliser ce helper pour définir le nom à l'intérieur du contrôle de l'utilisateur:

using System;
using System.Reflection;
using System.Windows;
using System.Windows.Media;
namespace UI.Helpers
{
    public class UserControlNameHelper
    {
        public static string GetName(DependencyObject d)
        {
            return (string)d.GetValue(UserControlNameHelper.NameProperty);
        }

        public static void SetName(DependencyObject d, string val)
        {
            d.SetValue(UserControlNameHelper.NameProperty, val);
        }

        public static readonly DependencyProperty NameProperty =
            DependencyProperty.RegisterAttached("Name",
                typeof(string),
                typeof(UserControlNameHelper),
                new FrameworkPropertyMetadata("",
                    FrameworkPropertyMetadataOptions.None,
                    (d, e) =>
                    {
                        if (!string.IsNullOrEmpty((string)e.NewValue))
                        {
                            string[] names = e.NewValue.ToString().Split(new char[] { ',' });

                            if (d is FrameworkElement)
                            {
                                ((FrameworkElement)d).Name = names[0];
                                Type t = Type.GetType(names[1]);
                                if (t == null)
                                    return;
                                var parent = FindVisualParent(d, t);
                                if (parent == null)
                                    return;
                                var p = parent.GetType().GetProperty(names[0], BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty);
                                p.SetValue(parent, d, null);
                            }
                        }
                    }));

        public static DependencyObject FindVisualParent(DependencyObject child, Type t)
        {
            // get parent item
            DependencyObject parentObject = VisualTreeHelper.GetParent(child);

            // we’ve reached the end of the tree
            if (parentObject == null)
            {
                var p = ((FrameworkElement)child).Parent;
                if (p == null)
                    return null;
                parentObject = p;
            }

            // check if the parent matches the type we’re looking for
            DependencyObject parent = parentObject.GetType() == t ? parentObject : null;
            if (parent != null)
            {
                return parent;
            }
            else
            {
                // use recursion to proceed with next level
                return FindVisualParent(parentObject, t);
            }
        }
    }
}

et votre fenêtre ou le Code de contrôle derrière définir vous contrôle par la propriété:

 public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

    }

    public Button BtnOK { get; set; }
}

votre fenêtre xaml:

    <Window x:Class="user_Control_Name.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:test="clr-namespace:user_Control_Name"
            xmlns:helper="clr-namespace:UI.Helpers" x:Name="mainWindow"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <test:TestUserControl>
                <Button helper:UserControlNameHelper.Name="BtnOK,user_Control_Name.MainWindow"/>
            </test:TestUserControl>
            <TextBlock Text="{Binding ElementName=mainWindow,Path=BtnOK.Name}"/>
        </Grid>
    </Window>

UserControlNameHelper obtenez votre nom de contrôle et votre nom de classe pour le contrôle de jeu à la propriété.

1
répondu Ali Yousefie 2015-07-06 10:57:27

j'ai choisi de créer une propriété supplémentaire pour chaque élément que je dois obtenir:

    public FrameworkElement First
    {
        get
        {
            if (Controls.Count > 0)
            {
                return Controls[0];
            }
            return null;
        }
    }

cela me permet d'accéder aux éléments enfants dans XAML:

<TextBlock Text="{Binding First.SelectedItem, ElementName=Taxcode}"/>
0
répondu aliceraunsbaek 2013-07-16 13:57:19
<Popup>
    <TextBox Loaded="BlahTextBox_Loaded" />
</Popup>

Code derrière:

public TextBox BlahTextBox { get; set; }
private void BlahTextBox_Loaded(object sender, RoutedEventArgs e)
{
    BlahTextBox = sender as TextBox;
}

La vraie solution serait de Microsoft pour résoudre ce problème, ainsi que tous les autres, cassé visual arbres. Hypothétiquement parlant.

0
répondu Ed Plunkett 2015-10-28 14:27:31

encore une autre solution: référez l'élément comme RelativeSource .

0
répondu voccoeisuoi 2016-05-13 14:45:14

j'ai eu le même problème en utilisant un TabControl pour placer un tas de contrôles nommés.

ma solution était d'utiliser un modèle de contrôle qui contient tous mes contrôles à afficher dans une page d'onglet. À l'intérieur du modèle, vous pouvez utiliser la propriété Nom et aussi lier des données aux propriétés du contrôle nommé à partir d'autres contrôles au moins à l'intérieur du même modèle.

comme contenu de la commande TabItem, utilisez une commande simple et réglez le ControlTemplate en conséquence:

<Control Template="{StaticResource MyControlTemplate}"/>

accéder à ceux nommés contrôle à l'intérieur du modèle à partir du code derrière vous devrait utiliser l'arbre visuel.

0
répondu David Wellna 2016-06-30 07:16:50