简体   繁体   English

WPF 用户控件父级

[英]WPF User Control Parent

I have a user control that I load into a MainWindow at runtime.我有一个在运行时加载到MainWindow的用户控件。 I cannot get a handle on the containing window from the UserControl .我无法从UserControl获取包含窗口的句柄。

I have tried this.Parent , but it's always null.我试过this.Parent ,但它总是为空。 Does anyone know how to get a handle to the containing window from a user control in WPF?有谁知道如何从 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);
}

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. GetWindow方法将为您遍历 VisualTree 并找到承载您的控件的窗口。

You should run this code after the control has loaded (and not in the Window constructor) to prevent the GetWindow method from returning null .您应该在控件加载后(而不是在 Window 构造函数中)运行此代码,以防止GetWindow方法返回null Eg wire up an event:例如连接一个事件:

this.Loaded += new RoutedEventHandler(UserControl_Loaded); 

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 事件可以完成这项工作,但我认为重写 OnInitialized 方法可能更合适。 Loaded occurs after the window is first displayed. Loaded 发生在窗口第一次显示之后。 OnInitialized gives you chance to make any changes, for example, add controls to the window before it is rendered. OnInitialized 使您有机会进行任何更改,例如,在呈现窗口之前向窗口添加控件。

Use VisualTreeHelper.GetParent or the recursive function below to find the parent window.使用 VisualTreeHelper.GetParent 或下面的递归函数来查找父窗口。

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

I needed to use the Window.GetWindow(this) method within Loaded event handler.我需要在 Loaded 事件处理程序中使用 Window.GetWindow(this) 方法。 In other words, I used both Ian Oakes' answer in combination with Alex's answer to get a user control's parent.换句话说,我将 Ian Oakes 的答案与 Alex 的答案结合使用,以获取用户控件的父级。

public MainView()
{
    InitializeComponent();

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

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

    ...
}

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.如果您发现这个问题并且 VisualTreeHelper 不适合您或偶尔工作,您可能需要在您的算法中包含 LogicalTreeHelper。

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

这种方法对我有用,但不像你的问题那么具体:

App.Current.MainWindow

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

其它的办法:

var main = App.Current.MainWindow as MainWindow;

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.我发现 UserControl 的父级在构造函数中始终为 null,但在任何事件处理程序中,父级都设置正确。 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.因此,要解决此问题,您只需在控件 Loaded 事件中获取父级即可。

For an example checkout this question WPF User Control's DataContext is Null例如结帐这个问题WPF User Control's DataContext is Null

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

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

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).只是不要将此调用放在构造函数中(因为Parent属性尚未初始化)。 Add it in the loading event handler, or in other parts of your application.将其添加到加载事件处理程序或应用程序的其他部分。

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

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

The Window.GetWindow(userControl) will return the actual window only after the window was initialized ( InitializeComponent() method finished). Window.GetWindow(userControl)仅在窗口初始化( InitializeComponent()方法完成)后才会返回实际窗口。

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.这意味着,如果您的用户控件与其窗口一起初始化(例如,您将用户控件放入窗口的 xaml 文件中),那么在用户控件的OnInitialized事件中,您将不会获得该窗口(它将为空),导致在这种情况下,用户控件的OnInitialized事件会在窗口初始化之前触发。

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.这也意味着如果您的用户控件在其窗口之后初始化,那么您可以在用户控件的构造函数中获取该窗口。

Gold plated edition of the above (I need a generic function which can infer a Window within the context of a MarkupExtension :-上述的镀金版本(我需要一个通用函数,它可以在MarkupExtension的上下文中推断一个Window :-

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: MyWrapper.Owner()将在以下基础上正确推断 Window:

  • the root Window by walking the visual tree (if used in the context of a UserControl )通过遍历可视化树来访问根Window (如果在UserControl的上下文中使用)
  • the window within which it is used (if it is used in the context of a Window 's markup)使用它的窗口(如果它在Window标记的上下文中使用)

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.在我的情况下,通过使用 VisualTreeHelper 或 Telerik 的扩展方法来查找给定类型的父级,我无法找到对话框的窗口。 Instead, I found my my dialog view which accepts custom injection of contents using Application.Current.Windows.相反,我发现我的对话框视图接受使用 Application.Current.Windows 的自定义内容注入。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM