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);
}
173968 次浏览

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

尝试使用以下方法:

Window parentWindow = Window.GetWindow(userControlReference);

GetWindow方法将为您遍历VisualTree并定位托管您的控件的窗口。

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

this.Loaded += new RoutedEventHandler(UserControl_Loaded);

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

例如,将此问题WPF用户控件';%s的DataContext为空签出

我需要在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);


...
}

我会加上我的经验。虽然使用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 parent = ExVisualTreeHelper.FindVisualParent<UserControl>(this);

这种方法对我来说很有效,但它不像你的问题那么具体:

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


DependencyObject parent = GetTopParent(thisUserControl);

另一种方法:

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

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

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

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

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

不同的方法和不同的策略。在我的例子中,我无法找到我的对话框的窗口,无论是通过使用VisualTreeHelper还是Telerik的扩展方法来查找给定类型的父类。相反,我找到了我的对话框视图,它接受使用application.current.windows的自定义内容注入。

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

只有在窗口初始化后(ABC_1方法完成_),Window.GetWindow(userControl)才会返回实际窗口。

这意味着,如果您的用户控件与其窗口一起初始化(例如,您将用户控件放入窗口的XAML文件中),则在用户控件的OnInitialized事件上,您将无法获得该窗口(它将为空),因为在这种情况下,用户控件的OnInitialized事件将在窗口初始化之前触发。

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

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

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属性尚未初始化)。将其添加到Loading事件处理程序或应用程序的其他部分中。