WPF用户控制父级

时间:2008-11-19 18:13:48

标签: c# .net wpf

我有一个用户控件,我在运行时加载到MainWindow。我无法处理来自UserControl的包含窗口的句柄。

我尝试了this.Parent,但它始终为空。有谁知道如何从WPF中的用户控件获取包含窗口的句柄?

以下是控件的加载方式:

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

17 个答案:

答案 0 :(得分:331)

尝试使用以下

Window parentWindow = Window.GetWindow(userControlRefernce);

GetWindow方法将为您提供VisualTree,并找到托管您控件的窗口。

您应该在加载控件后运行此代码,以防止GetWindow方法返回null。例如。连线活动:

this.Loaded += new RoutedEventHandler(UserControl_Loaded); 

答案 1 :(得分:33)

我会添加我的经验。虽然使用Loaded事件可以完成这项工作,但我认为覆盖OnInitialized方法可能更合适。首次显示窗口后出现加载。 OnInitialized使您有机会进行任何更改,例如,在窗口呈现之前向窗口添加控件。

答案 2 :(得分:13)

我需要在Loaded事件处理程序中使用Window.GetWindow(this)方法。换句话说,我使用Ian Oakes的答案和Alex的答案来获得用户控件的父级。

public MainView()
{
    InitializeComponent();

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

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

    ...
}

答案 3 :(得分:13)

尝试使用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);
        }
    }

答案 4 :(得分:6)

这个怎么样:

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

答案 5 :(得分:6)

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

App.Current.MainWindow

答案 6 :(得分:6)

如果您发现此问题并且VisualTreeHelper不适合您或偶尔工作,您可能需要在算法中包含LogicalTreeHelper。

以下是我正在使用的内容:

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

答案 7 :(得分:5)

我发现UserControl的父级在构造函数中始终为null,但在任何事件处理程序中父级都是正确设置的。我想它必须与控件树的加载方式有关。因此,为了解决这个问题,您可以在控件Loaded事件中获取父级。

例如结帐此问题WPF User Control's DataContext is Null

答案 8 :(得分:3)

这对我来说没有用,因为它在树上走得太远,并且获得了整个应用程序的绝对根窗口:

Window parentWindow = Window.GetWindow(userControlReference);

然而,这有助于立即获得窗口:

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

答案 9 :(得分:3)

另一种方式:

var main = App.Current.MainWindow as MainWindow;

答案 10 :(得分:3)

这对我有用:

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

答案 11 :(得分:1)

如果您只想获取特定的父级,而不只是窗口,树结构中的特定父级,并且还不使用递归或硬中断循环计数器,则可以使用以下内容:

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

请不要将此调用放入构造函数中(因为Parent属性尚未初始化)。将其添加到加载事件处理程序或应用程序的其他部分。

答案 12 :(得分:1)

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

DependencyObject parent = GetTopParent(thisUserControl);

答案 13 :(得分:1)

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

答案 14 :(得分:0)

以上镀金版(我需要一个通用函数,可以在Window的上下文中推断出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()将在以下基础上正确推断出一个窗口:

    通过遍历可视化树
  • Window(如果在UserControl的上下文中使用)
  • 使用它的窗口(如果它在Window标记的上下文中使用)

答案 15 :(得分:0)

不同的方法和不同的策略。就我而言,我无法通过使用VisualTreeHelper或Telerik的扩展方法来找到对话框的窗口来查找给定类型的父级。相反,我找到了我的对话框视图,该视图接受使用Application.Current.Windows的自定义内容注入。

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

答案 16 :(得分:0)

Window.GetWindow(userControl)仅在初始化窗口(InitializeComponent()方法完成)之后才返回实际​​窗口。

这意味着,如果用户控件及其窗口一起初始化(例如,将用户控件放入窗口的xaml文件中),则在用户控件的OnInitialized事件中,您将不会获得该窗口( (它将为null),在这种情况下会导致用户控件的OnInitialized事件在初始化窗口之前触发。

这还意味着,如果您的用户控件在其窗口之后初始化,那么您可以在用户控件的构造函数中获取该窗口。