У меня есть пользовательский элемент управления, который я загружаю во 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);
}
Ответ 1
Попробуйте использовать следующее:
Window parentWindow = Window.GetWindow(userControlReference);
Метод GetWindow
проведет за вами VisualTree и найдет окно, в котором находится ваш элемент управления.
Вы должны запустить этот код после того, как элемент управления загружено (а не в конструкторе окна), чтобы предотвратить GetWindow
метод возвращения из null
. Например, подключить событие:
this.Loaded += new RoutedEventHandler(UserControl_Loaded);
Ответ 2
Я добавлю свой опыт. Хотя использование события Loaded может выполнять эту работу, я думаю, что это может быть более подходящим для переопределения метода OnInitialized. Загружается после появления окна. OnInitialized дает вам возможность внести какие-либо изменения, например, добавить элементы управления в окно до его рендеринга.
Ответ 3
Попробуйте использовать 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
Мне нужно было использовать метод Window.GetWindow(this) в обработчике Loaded event. Другими словами, я использовал ответ Яна Оукса в сочетании с ответом Алекса, чтобы получить родительский контроль над пользователем.
public MainView()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainView_Loaded);
}
void MainView_Loaded(object sender, RoutedEventArgs e)
{
Window parentWindow = Window.GetWindow(this);
...
}
Ответ 5
Как насчет этого:
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));
}
}
}
Ответ 6
Этот подход работал у меня, но это не так специфично, как ваш вопрос:
App.Current.MainWindow
Ответ 7
Если вы находите этот вопрос, и 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);
}
Ответ 8
Я обнаружил, что родительский элемент UserControl всегда имеет значение null в конструкторе, но в любом случае обработчики родителя установлены правильно. Я предполагаю, что это должно иметь какое-то отношение к тому, как загружается дерево управления. Чтобы обойти это, вы можете просто получить родительский элемент в элементе Loaded.
В качестве примера рассмотрим этот вопрос DataContext WPF User Control - Null
Ответ 9
Другой способ:
var main = App.Current.MainWindow as MainWindow;
Ответ 10
Это работает для меня:
DependencyObject GetTopLevelControl(DependencyObject control)
{
DependencyObject tmp = control;
DependencyObject parent = null;
while((tmp = VisualTreeHelper.GetParent(tmp)) != null)
{
parent = tmp;
}
return parent;
}
Ответ 11
Это не сработало для меня, поскольку оно зашло слишком далеко по дереву и получило абсолютное корневое окно для всего приложения:
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();
Ответ 12
DependencyObject parent = ExVisualTreeHelper.FindVisualParent<UserControl>(this);
Ответ 13
DependencyObject GetTopParent(DependencyObject current)
{
while (VisualTreeHelper.GetParent(current) != null)
{
current = VisualTreeHelper.GetParent(current);
}
return current;
}
DependencyObject parent = GetTopParent(thisUserControl);
Ответ 14
Позолоченная версия выше (мне нужна общая функция, которая может вывести 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
Разные подходы и разные стратегии. В моем случае я не смог найти окно моего диалога ни с помощью VisualTreeHelper, ни с помощью методов расширения от Telerik, чтобы найти родителя данного типа. Вместо этого я нашел свое диалоговое окно, которое принимает пользовательское внедрение содержимого с использованием Application.Current.Windows.
public Window GetCurrentWindowOfType<TWindowType>(){
return Application.Current.Windows?.OfType<TWindowType>().FirstOrDefault();
}