C# WPF用户控件父级

C# WPF用户控件父级,c#,.net,wpf,C#,.net,Wpf,我有一个在运行时加载到main窗口中的用户控件。我无法从UserControl获取包含窗口的句柄 我尝试了this.Parent,但它总是空的。有人知道如何从WPF中的用户控件获取包含窗口的句柄吗 以下是控件的加载方式: private void XMLLogViewer_MenuItem_Click(object sender, RoutedEventArgs e) { MenuItem application = sender as MenuItem; string para

我有一个在运行时加载到
main窗口中的用户控件。我无法从
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);
}

使用VisualTreeHelper.GetParent或下面的递归函数查找父窗口

公共静态窗口FindParentWindow(DependencyObject子对象)
{
DependencyObject父对象=VisualTreeHelper.GetParent(子对象);
//检查这是否是树的结尾
if(parent==null)返回null;
窗口父窗口=父窗口;
if(parentWindow!=null)
{
返回父窗口;
}
其他的
{
//使用递归直到它到达一个窗口
返回FindParentWindow(父级);
}
}

尝试使用以下方法:

Window parentWindow = Window.GetWindow(userControlReference);
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;
}
GetWindow
方法将为您遍历VisualTree并找到承载控件的窗口

应该在加载控件后(而不是在窗口构造函数中)运行此代码,以防止
GetWindow
方法返回
null
。例如,连接事件:

this.Loaded += new RoutedEventHandler(UserControl_Loaded); 

我发现UserControl的父级在构造函数中始终为null,但在任何事件处理程序中,父级都设置正确。我想这一定与控制树的加载方式有关。因此,要解决这个问题,您只需在控件加载事件中获取父控件


例如,我需要在加载的事件处理程序中使用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);

    ...
}

我会补充我的经验。虽然使用Loaded事件可以完成这项工作,但我认为重写OnInitialized方法可能更合适。在第一次显示窗口后发生加载。OnInitialized使您有机会进行任何更改,例如,在渲染窗口之前将控件添加到窗口中。

这样如何:

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));
        }
    } 
}
DependencyObject父对象=ExVisualTreeHelper.FindVisualParent(此);
公共静态类ExVisualTreeHelper
{
/// 
///查找可视父对象。
/// 
/// 
///发送者。
/// 
公共静态T FindVisualParent(DependencyObject发送方),其中T:DependencyObject
{
if(发送方==null)
{
返回(空);
}
如果(VisualTreeHelper.GetParent(发送方)为T),则为else
{
返回(VisualTreeHelper.GetParent(发送方)作为T);
}
其他的
{
DependencyObject parent=VisualTreeHelper.GetParent(发送方);
返回(FindVisualParent(父级));
}
} 
}
DependencyObject父对象=ExVisualTreeHelper.FindVisualParent(此);

这种方法对我很有效,但没有你的问题那么具体:

App.Current.MainWindow
另一种方式:

var main = App.Current.MainWindow as MainWindow;
这对我有用:

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

如果您发现此问题,而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);
}
公共静态T TryFindParent(DependencyObject当前),其中T:class
{
DependencyObject parent=VisualTreeHelper.GetParent(当前);
如果(父项==null)
parent=LogicalTreeHelper.GetParent(当前);
如果(父项==null)
返回null;
if(父项为T)
将父对象返回为T;
其他的
返回TryFindParent(父级);
}

这对我来说不起作用,因为它在树上走得太远,为整个应用程序获得了绝对根窗口:

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

上面的镀金版本(我需要一个通用函数,它可以在
标记扩展的上下文中推断
窗口
:-

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));
    }
}
公共密封类MyExtension:MarkupExtension
{
公共覆盖对象ProviderValue(IServiceProvider serviceProvider)=>
新的MyWrapper(ResolveRootObject(serviceProvider));
对象ResolveRootObject(IServiceProvider服务提供者)=>
GetService(serviceProvider).RootObject;
}
类MyWrapper
{
对象_rootObject;
Window OwnerWindow()=>WindowFromRootObject(\u rootObject);
静态窗口WindowFromRootObject(对象根)=>
(根作为窗口)??VisualParent((依赖对象)根);
静态T VisualParent(DependencyObject节点),其中T:class
{
if(node==null)
抛出新的InvalidOperationException(“找不到父项”+typeof(T).Name);
var target=节点作为T;
如果(目标!=null)
回报目标;
返回VisualParent(VisualTreeHelper.GetParent(节点));
}
}
MyWrapper.Owner()
将根据以下基础正确推断窗口:

  • 通过遍历可视树(如果在
    用户控件的上下文中使用)来显示根
    窗口
  • 使用它的窗口(如果在
    窗口的标记上下文中使用)

不同的方法和不同的策略。在我的情况下,我无法通过使用VisualTreeHelper或Telerik中的扩展方法找到对话框窗口,以查找给定类型的父对象。相反,我找到了我的对话框视图,它接受使用Application.Current.Windows自定义内容注入

public Window GetCurrentWindowOfType<TWindowType>(){
 return Application.Current.Windows.OfType<TWindowType>().FirstOrDefault() as Window;
}
公共窗口GetCurrentWindowOfType(){
返回Application.Current.Windows.OfType().FirstOrDefault()作为窗口;
}
Window.GetWindow(userControl)
仅在初始化窗口后才会返回实际窗口(
InitializeComponent()
方法完成)

这意味着,如果用户控件与其窗口一起初始化(例如,将用户控件放入窗口的xaml文件中),
public Window GetCurrentWindowOfType<TWindowType>(){
 return Application.Current.Windows.OfType<TWindowType>().FirstOrDefault() as Window;
}
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;
}