2008-11-19 16 views
167

J'ai un contrôle utilisateur que je charge dans un MainWindow lors de l'exécution. Je ne peux pas obtenir un handle sur la fenêtre contenant à partir du UserControl. J'ai essayé this.Parent, mais c'est toujours null. Est-ce que quelqu'un sait comment obtenir un handle de la fenêtre contenant à partir d'un contrôle utilisateur dans WPF?Contrôle utilisateur WPF parent

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); 
} 

Répondre

10

Essayez d'utiliser VisualTreeHelper.GetParent ou utiliser la fonction récursive ci-dessous pour trouver la fenêtre parent.

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); 
     } 
    } 
+0

J'ai essayé de passer en utilisant ce code depuis mon contrôle utilisateur. J'ai passé ceci dans cette méthode mais elle a renvoyé la valeur null, indiquant que c'est la fin de l'arbre (selon votre commentaire). Savez-vous pourquoi c'est? Le contrôle utilisateur a un parent qui est le formulaire contenant. Comment puis-je gérer ce formulaire? –

+1

J'ai découvert la raison pour laquelle il retourne null. Je mettais ce code dans le constructeur de mon contrôle utilisateur. Vous devriez exécuter ce code après le chargement du contrôle. PAR EXEMPLE. connecter un événement: this.Loaded + = new RoutedEventHandler (UserControl_Loaded) –

+0

Un autre problème est dans le débogueur. VS va exécuter le code de l'événement Load, mais il ne trouvera pas le parent Window. –

314

Essayez d'utiliser les éléments suivants

Window parentWindow = Window.GetWindow(userControlRefernce); 

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

+6

Renvoie toujours la valeur null. C'est comme si le contrôle n'avait aucun parent. – donniefitz2

+2

J'ai utilisé le code ci-dessus et obtenir la parentWindow renvoie également null pour moi. –

+98

J'ai découvert la raison pour laquelle il retourne null. Je mettais ce code dans le constructeur de mon contrôle utilisateur. Vous devriez exécuter ce code après le chargement du contrôle. PAR EXEMPLE. reliez un événement: this.Loaded + = new RoutedEventHandler (UserControl_Loaded); –

5

J'ai découvert que le parent d'un contrôle UserControl est toujours null 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 l'événement Controls Loaded.

Pour une caisse exemple cette question WPF User Control's DataContext is Null

+1

Vous devez en quelque sorte attendre qu'il soit dans le "arbre" d'abord. Assez odieux parfois. – user7116

13

je devais utiliser la méthode Window.GetWindow (ce) dans gestionnaire d'événements Loaded. En d'autres termes, j'ai utilisé la réponse de Ian Oakes en combinaison avec la réponse 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); 

    ... 
} 
32

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

+8

+1 pour corriger. La compréhension de la technique à utiliser peut parfois être subtile, en particulier lorsque des événements et des substitutions sont lancés dans le mixage (événement Loaded, override OnLoaded, événement Initialized, override OnInitialized, etcetcetc). Dans ce cas, OnInitialized est logique car vous souhaitez rechercher le parent et le contrôle doit être initialisé pour que le parent "existe". Loaded signifie quelque chose de différent. –

+2

'Window.GetWindow' renvoie toujours' null' dans 'OnInitialized'. Semble fonctionner dans l'événement 'Loaded' seulement. – Physikbuddha

6

Que diriez-vous ceci:

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)); 
     } 
    } 
} 
1
DependencyObject parent = ExVisualTreeHelper.FindVisualParent<UserControl>(this); 
+0

Vous devriez lire l '[aide au formatage] (http://stackoverflow.com/editing-help). –

+0

Veuillez supprimer ceci et intégrer tout point qui n'est pas déjà couvert dans [votre autre réponse] (http: // stackoverflow.com/a/6048435/11635) (que j'ai upvoted comme une bonne réponse) –

6

Cette approche a fonctionné pour moi, mais ce n'est pas aussi précis que votre question:

App.Current.MainWindow 
1
DependencyObject GetTopParent(DependencyObject current) 
{ 
    while (VisualTreeHelper.GetParent(current) != null) 
    { 
     current = VisualTreeHelper.GetParent(current); 
    } 
    return current; 
} 

DependencyObject parent = GetTopParent(thisUserControl); 
3

Une autre façon:

var main = App.Current.MainWindow as MainWindow; 
+0

A travaillé pour moi, dois le mettre dans l'événement "Loaded" plutôt que le constructeur (afficher la fenêtre des propriétés, double-cliquez et ajoutera le gestionnaire pour vous). – Contango

+0

(Mon vote est pour la réponse acceptée par Ian, ceci est juste pour l'enregistrement) Cela ne fonctionnait pas lorsque le contrôle utilisateur se trouvait dans une autre fenêtre avec ShowDialog, en définissant le contenu sur le contrôle utilisateur. Une approche similaire est de parcourir App.Current.Windows et d'utiliser la fenêtre où la condition suivante, pour idx de (Current.Windows.Count - 1) à 0 (App.Current.Windows [idx] == userControlRef) est ** true **. Si nous faisons cela dans l'ordre inverse, c'est probablement la dernière fenêtre et nous obtenons la fenêtre correcte avec juste une itération. userControlRef est généralement * this * dans la classe UserControl. – msanjay

3

Il travaille pour moi:

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

Si vous trouvez cette question et la VisualTreeHelper ne fonctionne pas pour vous ou travailler de façon sporadique, vous devrez peut-être inclure LogicalTreeHelper dans votre algorithme.

Voici ce que je suis en utilisant:

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); 
} 
+0

Vous manquez un nom de méthode 'LogicalTreeHelper.GetParent' dans le code. – xmedeko

+0

C'était la meilleure solution pour moi. –

1

Cela ne fonctionne pas pour moi, car il est allé trop loin dans l'arbre, et a la fenêtre racine absolue pour l'ensemble de 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(); 
0

plaqué or édition de ce qui précède (je besoin d'un fu générique nction qui peut déduire un Window dans le cadre 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() sera correctement déduire une fenêtre sur la base suivante:

  • la racine Window en marchant l'arbre visuel (si elle est utilisée le contexte d'un UserControl)
  • la fenêtre dans laquelle il est utilisé (si elle est utilisée dans le contexte d'un balisage de » Window)
Questions connexes