IT

WPF 사용자 컨트롤 부모

lottoking 2020. 5. 19. 08:30
반응형

WPF 사용자 컨트롤 부모


MainWindow런타임에 로드하는 사용자 정의 컨트롤이 있습니다. 에서 포함 창에 대한 핸들을 얻을 수 없습니다 UserControl.

시도 this.Parent했지만 항상 null입니다. 누구나 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를 안내하고 컨트롤을 호스팅하는 창을 찾습니다.

GetWindow메서드가 반환 되지 않도록하려면 컨트롤이로드 된 후 (창 생성자가 아닌)이 코드를 실행해야합니다 null. 예를 들어 이벤트를 연결하십시오.

this.Loaded += new RoutedEventHandler(UserControl_Loaded); 

내 경험을 추가하겠습니다. Loaded 이벤트를 사용하면 작업을 수행 할 수 있지만 OnInitialized 메서드를 재정의하는 것이 더 적합 할 수 있습니다. 창을 처음 표시 한 후로드가 발생합니다. 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);

    ...
}

이 방법은 저에게 효과적이지만 귀하의 질문만큼 구체적이지 않습니다.

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


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.

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

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. 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.

For an example checkout this question WPF User Control's DataContext is Null


Another way:

var main = App.Current.MainWindow as MainWindow;

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

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

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

DependencyObject parent = GetTopParent(thisUserControl);

Gold plated edition of the above (I need a generic function which can infer a Window within the context of a 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() will correctly infer a Window on the following basis:

  • the root Window by walking the visual tree (if used in the context of a UserControl)
  • the window within which it is used (if it is used in the context of a Window's markup)

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. Instead, I found my my dialog view which accepts custom injection of contents using Application.Current.Windows.

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

참고URL : https://stackoverflow.com/questions/302839/wpf-user-control-parent

반응형