198

I have a user control that I load into a MainWindow at runtime. I cannot get a handle on the containing window from the UserControl.

I have tried this.Parent, but it's always null. Does anyone know how to get a handle to the containing window from a user control in WPF?

Here is how the control is loaded:

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);
}
lokusking
  • 7,396
  • 13
  • 38
  • 57
donniefitz2
  • 2,003
  • 2
  • 13
  • 6

17 Answers17

370

Try using the following:

Window parentWindow = Window.GetWindow(userControlReference);

The GetWindow method will walk the VisualTree for you and locate the window that is hosting your control.

You should run this code after the control has loaded (and not in the Window constructor) to prevent the GetWindow method from returning null. E.g. wire up an event:

this.Loaded += new RoutedEventHandler(UserControl_Loaded); 
Otiel
  • 18,404
  • 16
  • 78
  • 126
Ian Oakes
  • 10,153
  • 6
  • 36
  • 47
  • 6
    Still returns null. It's as if the control just has no parent. – donniefitz2 Nov 20 '08 at 16:16
  • 2
    I used the code above and get the parentWindow also returns null for me. – Peter Walke Jun 05 '09 at 15:38
  • 111
    I found out the reason it's returning null. I was putting this code into the constructor of my user control. You should run this code after the control has loaded. E.G. wire up an event: this.Loaded += new RoutedEventHandler(UserControl_Loaded); – Peter Walke Jun 05 '09 at 16:12
  • 2
    After reviewing the response from Paul, it might make sense to use the OnInitialized method instead of Loaded. – Peter Walke Aug 10 '11 at 20:13
  • after I get my parent window like this it throws exception at resource folders. Owner property..... – albatross Jan 03 '17 at 12:43
35

I'll add my experience. Although using the Loaded event can do the job, I think it may be more suitable to override the OnInitialized method. Loaded occurs after the window is first displayed. OnInitialized gives you chance to make any changes, for example, add controls to the window before it is rendered.

paul
  • 351
  • 3
  • 2
  • 8
    +1 for correct. Understanding which technique to use can be subtle at times, especially when you've got events and overrides thrown into the mix (Loaded event, OnLoaded override, Initialized event, OnInitialized override, etcetcetc). In this case, OnInitialized makes sense because you want to find the parent, and the control must be initialized for the parent to "exist". Loaded means something different. – Greg D Mar 20 '10 at 15:33
  • 3
    `Window.GetWindow` still returns `null` in `OnInitialized`. Seems to work in the `Loaded` event only. – Physikbuddha May 27 '15 at 08:25
  • The Initialized Event must be defined before InitializeComponent(); Anyway, my Binded (XAML) Elements couldn't resolve the source (Window). So i ended to use the Loaded Event. – Lenor Nov 10 '18 at 15:16
17

Use VisualTreeHelper.GetParent or the recursive function below to find the parent window.

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);
    }
}
Pang
  • 9,564
  • 146
  • 81
  • 122
Jobi Joy
  • 49,102
  • 20
  • 108
  • 119
  • I tried passing using this code from within my user control. I passed this into this method but it returned null, indicating that it is the end of the tree (according to your comment). Do you know why this is? The user control has a parent which is the containing form. How do I get a handle to this form? – Peter Walke Jun 05 '09 at 15:44
  • 2
    I found out the reason it's returning null. I was putting this code into the constructor of my user control. You should run this code after the control has loaded. E.G. wire up an event: this.Loaded += new RoutedEventHandler(UserControl_Loaded) – Peter Walke Jun 05 '09 at 16:12
  • Another issue is in the debugger. VS will execute the code of Load event, but it won't find the Window parent. – bohdan_trotsenko Jul 22 '09 at 08:30
  • 1
    If you are going to implement your own method, you should use a combination of VisualTreeHelper and LogicalTreeHelper. This is because some non-window controls (like Popup) do not have visual parents and it appears that controls generated from a data template do not have logical parents. – Brian Reichle Jul 13 '12 at 10:31
15

I needed to use the Window.GetWindow(this) method within Loaded event handler. In other words, I used both Ian Oakes' answer in combination with Alex's answer to get a user control's parent.

public MainView()
{
    InitializeComponent();

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

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

    ...
}
Alan Le
  • 8,683
  • 7
  • 36
  • 31
10

If you are finding this question and the VisualTreeHelper isn't working for you or working sporadically, you may need to include LogicalTreeHelper in your algorithm.

Here is what I am using:

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);
}
Jack B Nimble
  • 5,039
  • 4
  • 40
  • 62
GordoFabulous
  • 195
  • 2
  • 12
7

This approach worked for me but it is not as specific as your question:

App.Current.MainWindow
Anthony Main
  • 6,039
  • 12
  • 64
  • 89
6

Another way:

var main = App.Current.MainWindow as MainWindow;
Pnctovski
  • 565
  • 1
  • 9
  • 21
  • Worked for me, have to put it in the "Loaded" event rather than the constructor (bring up the properties window, double click and it will add the handler for you). – Contango Jan 06 '15 at 20:20
  • (My vote is for the accepted answer by Ian, this is just for the record) This did not work when the user control is in another window with ShowDialog, setting content to the user control. A similar approach is to walk through App.Current.Windows and use the window where the following condition, for idx from (Current.Windows.Count - 1) to 0 (App.Current.Windows[idx] == userControlRef) is **true**. If we do this in reverse order, its likely to be the last window and we get the correct window with just one iteration. userControlRef is typically *this* within the UserControl class. – msanjay Feb 17 '16 at 11:28
6

How about this:

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));
        }
    } 
}
Tono Nam
  • 34,064
  • 78
  • 298
  • 470
Eric Coulson
  • 79
  • 1
  • 1
5

I've found that the parent of a UserControl is always null in the constructor, but in any event handlers the parent is set correctly. I guess it must have something to do with the way the control tree is loaded. So to get around this you can just get the parent in the controls Loaded event.

For an example checkout this question WPF User Control's DataContext is Null

Community
  • 1
  • 1
Alex
  • 34,776
  • 10
  • 53
  • 68
4

It's working for me:

DependencyObject GetTopLevelControl(DependencyObject control)
{
    DependencyObject tmp = control;
    DependencyObject parent = null;
    while((tmp = VisualTreeHelper.GetParent(tmp)) != null)
    {
        parent = tmp;
    }
    return parent;
}
Nalan Madheswaran
  • 10,136
  • 1
  • 57
  • 42
2

This didn't work for me, as it went too far up the tree, and got the absolute root window for the entire application:

Window parentWindow = Window.GetWindow(userControlReference);

However, this worked to get the immediate window:

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();
Contango
  • 76,540
  • 58
  • 260
  • 305
  • You should use a null check instead of an arbitrary 'avoidInfiniteLoop' variable. Change your 'while' to check for null first, and if not null, then check if it's not a window. Otherwise, just break/exit. – Mark A. Donohoe Dec 03 '19 at 06:36
  • @MarquelV I hear you. Generally, I add an "avoidInfiniteLoop" check to *every* loop that could in theory get stuck if something goes wrong. Its part of defensive programming. Every so often, it pays good dividends as the program avoids a hang. Very useful during debugging, and very useful in production if the overrun is logged. I use this technique (among many others) to enable writing robust code that just works. – Contango Dec 03 '19 at 11:45
  • I get defensive programming, and I agree in principle about it, but as a code reviewer, I think this would get flagged for introducing arbitrary data that isn't part of the actual logic flow. You already have all the information needed to stop infinite recursion by checking for null as it is impossible to recurse up a tree infinitely. Sure you could forget to update the parent and have an infinite loop, but you could just as easily forget to update that arbitrary variable. In other words, it is *already* defensive programming to check for null without the introduction of new, unrelated data. – Mark A. Donohoe Dec 03 '19 at 15:46
  • 1
    @MarquelIV I have to agree. Adding an additional null check is better defensive programming. – Contango Dec 04 '19 at 09:22
2

If you just want to get a specific parent, not only the window, a specific parent in the tree structure, and also not using recursion, or hard break loop counters, you can use the following:

public static T FindParent<T>(DependencyObject current)
    where T : class 
{
    var dependency = current;

    while((dependency = VisualTreeHelper.GetParent(dependency) ?? LogicalTreeHelper.GetParent(dependency)) != null
        && !(dependency is T)) { }

    return dependency as T;
}

Just don't put this call in a constructor (since the Parent property is not yet initialized). Add it in the loading event handler, or in other parts of your application.

Lucaci Andrei
  • 398
  • 9
  • 20
1
DependencyObject GetTopParent(DependencyObject current)
{
    while (VisualTreeHelper.GetParent(current) != null)
    {
        current = VisualTreeHelper.GetParent(current);
    }
    return current;
}

DependencyObject parent = GetTopParent(thisUserControl);
Agus Syahputra
  • 436
  • 4
  • 12
1
DependencyObject parent = ExVisualTreeHelper.FindVisualParent<UserControl>(this);
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
Eric Coulson
  • 79
  • 1
  • 1
  • Please delete this and integrate any point this makes that is not already covered into [your other answer](http://stackoverflow.com/a/6048435/11635) (which I've upvoted as its a good answer) – Ruben Bartelink Jun 02 '16 at 16:18
1

The Window.GetWindow(userControl) will return the actual window only after the window was initialized (InitializeComponent() method finished).

This means, that if your user control is initialized together with its window (for instance you put your user control into the window's xaml file), then on the user control's OnInitialized event you will not get the window (it will be null), cause in that case the user control's OnInitialized event fires before the window is initialized.

This also means that if your user control is initialized after its window, then you can get the window already in the user control's constructor.

Szabolcs Antal
  • 877
  • 4
  • 15
  • 27
0

Gold plated edition of the above (I need a generic function which can infer a Window within the context of a 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() will correctly infer a Window on the following basis:

  • the root Window by walking the visual tree (if used in the context of a UserControl)
  • the window within which it is used (if it is used in the context of a Window's markup)
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
0

Different approaches and different strategies. In my case I could not find the window of my dialog either through using VisualTreeHelper or extension methods from Telerik to find parent of given type. Instead, I found my my dialog view which accepts custom injection of contents using Application.Current.Windows.

public Window GetCurrentWindowOfType<TWindowType>(){
 return Application.Current.Windows.OfType<TWindowType>().FirstOrDefault() as Window;
}
Tore Aurstad
  • 3,189
  • 1
  • 27
  • 22