web-dev-qa-db-ja.com

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

以下を使用してみてください。

Window parentWindow = Window.GetWindow(userControlReference);

GetWindowメソッドは、VisualTreeを調べて、コントロールをホストしているウィンドウを見つけます。

GetWindowメソッドがnullを返さないようにするには、コントロールがロードされた後で(ウィンドウコンストラクターではなく)このコードを実行する必要があります。例えば。イベントを結びつける:

this.Loaded += new RoutedEventHandler(UserControl_Loaded); 
337
Ian Oakes

私の経験を追加します。 Loadedイベントを使用するとジョブを実行できますが、OnInitializedメソッドをオーバーライドする方が適していると思います。ロードは、ウィンドウが最初に表示された後に発生します。 OnInitializedを使用すると、たとえば、ウィンドウがレンダリングされる前にコントロールを追加するなど、変更を加えることができます。

33
paul

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

    ...
}
13
Alan Le

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);
        }
    }
13
Jobi Joy

このアプローチはうまくいきましたが、あなたの質問ほど具体的ではありません:

App.Current.MainWindow
6
Anthony Main

これはどう:

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
Eric Coulson

この質問を見つけていて、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);
}
6
GordoFabulous

UserControlの親はコンストラクターで常にnullであることがわかりましたが、イベントハンドラーでは親は正しく設定されています。コントロールツリーのロード方法と関係があるはずです。そのため、これを回避するには、コントロールのLoadedイベントで親を取得するだけです。

チェックアウトの例については、この質問 WPFユーザーコントロールのDataContextはNullです

5
Alex

それは私のために働いています:

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

別の方法:

var main = App.Current.MainWindow as MainWindow;
3
Pnct

これは私にとってはうまくいきませんでした。ツリーをはるかに上に移動し、アプリケーション全体の絶対ルートウィンドウを取得したためです。

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();
3
Contango
DependencyObject parent = ExVisualTreeHelper.FindVisualParent<UserControl>(this);
1
Eric Coulson
DependencyObject GetTopParent(DependencyObject current)
{
    while (VisualTreeHelper.GetParent(current) != null)
    {
        current = VisualTreeHelper.GetParent(current);
    }
    return current;
}

DependencyObject parent = GetTopParent(thisUserControl);
1
Agus Syahputra

異なるアプローチと異なる戦略。私の場合、VisualTreeHelperまたはTelerikの拡張メソッドを使用して特定のタイプの親を見つけることでも、ダイアログのウィンドウを見つけることができませんでした。代わりに、Application.Current.Windowsを使用してコンテンツのカスタムインジェクションを受け入れるダイアログビューを見つけました。

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

上記の金メッキ版(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を正しく推測します。

  • ビジュアルツリーをたどってルートWindowUserControlのコンテキストで使用する場合)
  • 使用されるウィンドウ(Windowのマークアップのコンテキストで使用される場合)
0
Ruben Bartelink