簡體   English   中英

WPF 用戶控件父級

[英]WPF User Control Parent

我有一個在運行時加載到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);
}

嘗試使用以下方法:

Window parentWindow = Window.GetWindow(userControlReference);

GetWindow方法將為您遍歷 VisualTree 並找到承載您的控件的窗口。

您應該在控件加載后(而不是在 Window 構造函數中)運行此代碼,以防止GetWindow方法返回null 例如連接一個事件:

this.Loaded += new RoutedEventHandler(UserControl_Loaded); 

我來補充一下我的經驗。 雖然使用 Loaded 事件可以完成這項工作,但我認為重寫 OnInitialized 方法可能更合適。 Loaded 發生在窗口第一次顯示之后。 OnInitialized 使您有機會進行任何更改,例如,在呈現窗口之前向窗口添加控件。

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

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

    ...
}

如果您發現這個問題並且 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);
}

這種方法對我有用,但不像你的問題那么具體:

App.Current.MainWindow

這個怎么樣:

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;

我發現 UserControl 的父級在構造函數中始終為 null,但在任何事件處理程序中,父級都設置正確。 我想它一定與控制樹的加載方式有關。 因此,要解決此問題,您只需在控件 Loaded 事件中獲取父級即可。

例如結帳這個問題WPF User Control's DataContext is Null

它對我有用:

DependencyObject GetTopLevelControl(DependencyObject control)
{
    DependencyObject tmp = control;
    DependencyObject parent = null;
    while((tmp = VisualTreeHelper.GetParent(tmp)) != null)
    {
        parent = tmp;
    }
    return 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();

如果您只想獲取特定的父級,不僅是窗口、樹結構中的特定父級,而且也不使用遞歸或硬中斷循環計數器,則可以使用以下命令:

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屬性尚未初始化)。 將其添加到加載事件處理程序或應用程序的其他部分。

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

Window.GetWindow(userControl)僅在窗口初始化( InitializeComponent()方法完成)后才會返回實際窗口。

這意味着,如果您的用戶控件與其窗口一起初始化(例如,您將用戶控件放入窗口的 xaml 文件中),那么在用戶控件的OnInitialized事件中,您將不會獲得該窗口(它將為空),導致在這種情況下,用戶控件的OnInitialized事件會在窗口初始化之前觸發。

這也意味着如果您的用戶控件在其窗口之后初始化,那么您可以在用戶控件的構造函數中獲取該窗口。

上述的鍍金版本(我需要一個通用函數,它可以在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()將在以下基礎上正確推斷 Window:

  • 通過遍歷可視化樹來訪問根Window (如果在UserControl的上下文中使用)
  • 使用它的窗口(如果它在Window標記的上下文中使用)

不同的方法和不同的策略。 在我的情況下,通過使用 VisualTreeHelper 或 Telerik 的擴展方法來查找給定類型的父級,我無法找到對話框的窗口。 相反,我發現我的對話框視圖接受使用 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