web-dev-qa-db-fra.com

Parent de contrôle utilisateur WPF

J'ai un contrôle utilisateur que je charge dans un MainWindow au moment de l'exécution. Je ne parviens pas à obtenir un descripteur de la fenêtre contenant le fichier UserControl.

J'ai essayé this.Parent, mais c'est toujours nul. Est-ce que quelqu'un sait comment obtenir un handle vers la fenêtre qui le contient à partir d'un contrôle utilisateur dans WPF?

Voici comment le contrôle est chargé:

private void XMLLogViewer_MenuItem_Click(object sender, RoutedEventArgs e)
{
    MenuItem application = sender as MenuItem;
    string parameter = application.CommandParameter as string;
    string controlName = parameter;
    if (uxPanel.Children.Count == 0)
    {
        System.Runtime.Remoting.ObjectHandle instance = Activator.CreateInstance(Assembly.GetExecutingAssembly().FullName, controlName);
        UserControl control = instance.Unwrap() as UserControl;
        this.LoadControl(control);
    }
}

private void LoadControl(UserControl control)
{
    if (uxPanel.Children.Count > 0)
    {
        foreach (UIElement ctrl in uxPanel.Children)
        {
            if (ctrl.GetType() != control.GetType())
            {
                this.SetControl(control);
            }
        }
    }
    else
    {
        this.SetControl(control);
    }
}

private void SetControl(UserControl control)
{
    control.Width = uxPanel.Width;
    control.Height = uxPanel.Height;
    uxPanel.Children.Add(control);
}
179
donniefitz2

Essayez d'utiliser ce qui suit:

Window parentWindow = Window.GetWindow(userControlReference);

La méthode GetWindow parcourra VisualTree pour vous et localisera la fenêtre qui héberge votre contrôle.

Vous devez exécuter ce code une fois le contrôle chargé (et non dans le constructeur de Windows) pour empêcher la méthode GetWindow de renvoyer null. Par exemple. connecter un événement:

this.Loaded += new RoutedEventHandler(UserControl_Loaded); 
337
Ian Oakes

Je vais ajouter mon expérience. Bien que l'utilisation de l'événement Loaded puisse faire l'affaire, je pense qu'il peut être plus approprié de remplacer la méthode OnInitialized. Chargé se produit après le premier affichage de la fenêtre. OnInitialized vous donne la possibilité d'apporter des modifications, par exemple, d'ajouter des contrôles à la fenêtre avant son rendu.

33
paul

J'avais besoin d'utiliser la méthode Window.GetWindow (this) dans le gestionnaire d'événements Loaded. En d'autres termes, j'ai utilisé la réponse de Ian Oakes à la fois avec celle d'Alex pour obtenir le parent d'un contrôle utilisateur.

public MainView()
{
    InitializeComponent();

    this.Loaded += new RoutedEventHandler(MainView_Loaded);
}

void MainView_Loaded(object sender, RoutedEventArgs e)
{
    Window parentWindow = Window.GetWindow(this);

    ...
}
13
Alan Le

Essayez d’utiliser VisualTreeHelper.GetParent ou utilisez la fonction ci-dessous récursive pour rechercher la fenêtre parente.

 public static Window FindParentWindow(DependencyObject child)
    {
        DependencyObject parent= VisualTreeHelper.GetParent(child);

        //CHeck if this is the end of the tree
        if (parent == null) return null;

        Window parentWindow = parent as Window;
        if (parentWindow != null)
        {
            return parentWindow;
        }
        else
        {
            //use recursion until it reaches a Window
            return FindParentWindow(parent);
        }
    }
13
Jobi Joy

Cette approche a fonctionné pour moi, mais elle n’est pas aussi spécifique que votre question:

App.Current.MainWindow
6
Anthony Main

Que dis-tu de ça:

DependencyObject parent = ExVisualTreeHelper.FindVisualParent<UserControl>(this);

public static class ExVisualTreeHelper
{
    /// <summary>
    /// Finds the visual parent.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sender">The sender.</param>
    /// <returns></returns>
    public static T FindVisualParent<T>(DependencyObject sender) where T : DependencyObject
    {
        if (sender == null)
        {
            return (null);
        }
        else if (VisualTreeHelper.GetParent(sender) is T)
        {
            return (VisualTreeHelper.GetParent(sender) as T);
        }
        else
        {
            DependencyObject parent = VisualTreeHelper.GetParent(sender);
            return (FindVisualParent<T>(parent));
        }
    } 
}
6
Eric Coulson

Si vous trouvez cette question et que VisualTreeHelper ne fonctionne pas pour vous ou de manière sporadique, vous devrez peut-être inclure LogicalTreeHelper dans votre algorithme.

Voici ce que j'utilise:

public static T TryFindParent<T>(DependencyObject current) where T : class
{
    DependencyObject parent = VisualTreeHelper.GetParent(current);
    if( parent == null )
        parent = LogicalTreeHelper.GetParent(current);
    if( parent == null )
        return null;

    if( parent is T )
        return parent as T;
    else
        return TryFindParent<T>(parent);
}
6
GordoFabulous

J'ai constaté que le parent d'un UserControl est toujours nul dans le constructeur, mais dans tous les gestionnaires d'événements, le parent est défini correctement. Je suppose que cela doit avoir quelque chose à voir avec la façon dont l'arbre de contrôle est chargé. Donc, pour contourner cela, vous pouvez simplement obtenir le parent dans les contrôles Événement chargé.

Pour un exemple, consultez cette question Le DataContext de WPF User Control est Null

5
Alex

Cela fonctionne pour moi:

DependencyObject GetTopLevelControl(DependencyObject control)
{
    DependencyObject tmp = control;
    DependencyObject parent = null;
    while((tmp = VisualTreeHelper.GetParent(tmp)) != null)
    {
        parent = tmp;
    }
    return parent;
}
3
Nalan Madheswaran

Autrement:

var main = App.Current.MainWindow as MainWindow;
3
Pnct

Cela ne fonctionnait pas pour moi, car cela remontait trop loin dans l'arborescence et obtenait la fenêtre racine absolue pour toute l'application:

Window parentWindow = Window.GetWindow(userControlReference);

Cependant, cela a fonctionné pour obtenir la fenêtre immédiate:

DependencyObject parent = uiElement;
int avoidInfiniteLoop = 0;
while ((parent is Window)==false)
{
    parent = VisualTreeHelper.GetParent(parent);
    avoidInfiniteLoop++;
    if (avoidInfiniteLoop == 1000)
    {
        // Something is wrong - we could not find the parent window.
        break;
    }
}
Window window = parent as Window;
window.DragMove();
3
Contango
DependencyObject parent = ExVisualTreeHelper.FindVisualParent<UserControl>(this);
1
Eric Coulson
DependencyObject GetTopParent(DependencyObject current)
{
    while (VisualTreeHelper.GetParent(current) != null)
    {
        current = VisualTreeHelper.GetParent(current);
    }
    return current;
}

DependencyObject parent = GetTopParent(thisUserControl);
1
Agus Syahputra

Différentes approches et différentes stratégies. Dans mon cas, je ne pouvais pas trouver la fenêtre de ma boîte de dialogue en utilisant VisualTreeHelper ou des méthodes d'extension de Telerik pour trouver le parent d'un type donné. Au lieu de cela, j'ai trouvé ma vue de dialogue qui accepte l'injection personnalisée de contenu à l'aide d'Application.Current.Windows.

public Window GetCurrentWindowOfType<TWindowType>(){
 return Application.Current.Windows.OfType<TWindowType>().FirstOrDefault() as Window;
}
0
Tore Aurstad

L'édition plaquée or de ce qui précède (j'ai besoin d'une fonction générique pouvant déduire un Window dans le contexte d'un MarkupExtension: -

public sealed class MyExtension : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider) =>
        new MyWrapper(ResolveRootObject(serviceProvider));
    object ResolveRootObject(IServiceProvider serviceProvider) => 
         GetService<IRootObjectProvider>(serviceProvider).RootObject;
}

class MyWrapper
{
    object _rootObject;

    Window OwnerWindow() => WindowFromRootObject(_rootObject);

    static Window WindowFromRootObject(object root) =>
        (root as Window) ?? VisualParent<Window>((DependencyObject)root);
    static T VisualParent<T>(DependencyObject node) where T : class
    {
        if (node == null)
            throw new InvalidOperationException("Could not locate a parent " + typeof(T).Name);
        var target = node as T;
        if (target != null)
            return target;
        return VisualParent<T>(VisualTreeHelper.GetParent(node));
    }
}

MyWrapper.Owner() déduira correctement une fenêtre sur les bases suivantes:

  • la racine Window en parcourant l'arborescence visuelle (si utilisé dans le contexte d'un UserControl)
  • la fenêtre dans laquelle il est utilisé (s'il est utilisé dans le contexte d'un balisage Window)
0
Ruben Bartelink