通过 hWnd 获取 WPF 窗口

14

我想要获取一个WPF窗口并遍历其控件。我能够获取hWnd并找到了其他帖子,可以使用DependencyObject遍历控件。如何从hWnd获取DependencyObject?这是否可行?


你是在同一进程内还是从另一个进程中进行操作?通常情况下,你只能在同一AppDomain中访问WPF对象(这也限制了你只能在同一进程中进行操作)。你能否提供更多关于为什么要遍历控件的背景信息 - 也许是自动化/测试方面的需求? - BrendanMcK
4个回答

20
Window window = (Window)HwndSource.FromHwnd(hWnd).RootVisual

1
这就是我在尝试的,但RootVisual始终为空。 - Dustin Davis
也许你的hWnd针对的是不属于WPF的窗口?你是如何获取hWnd的? - Marat Khasanov
能否告诉我们为什么需要找到这个窗口?也许有其他解决方案,而不是使用本地句柄 ;) - Marat Khasanov
解决方案资源管理器仍然是本地的。 - Marat Khasanov
@Marat,你觉得呢?我该如何获取树的上下文以便于操作它? - Dustin Davis
显示剩余6条评论

3
据我所知,WPF完全取代了WinApi模型及其中的所有HWND。主窗口当然有HWND,因为它作为WinApi和WPF之间的容器。您可以使用WindowInteropHelper类访问HWND,就像这个一样。但是您将无法像在原生或WinForms应用程序中那样遍历控件。请查看VisualTreeHelper以在WPF中遍历控件树。

1
在获取窗口本身之后(就像Marat指出的那样),您需要搜索可视树。以下是两个辅助函数:

查找类型的所有子项

public static IEnumerable<T> FindChildren<T>(this DependencyObject source) where T : DependencyObject
    {
      if (source != null)
      {
        var childs = GetChildObjects(source);
        foreach (DependencyObject child in childs)
        {
          //analyze if children match the requested type
          if (child != null && child is T)
          {
            yield return (T)child;
          }

          //recurse tree
          foreach (T descendant in FindChildren<T>(child))
          {
            yield return descendant;
          }
        }
      }
    }

https://sites.google.com/site/bobstechwiki/home/wpf-ji-shu-1/find-element-by-visual-tree

通过名称查找子元素:

 public static T FindChild<T>(DependencyObject parent, string childName)
       where T : DependencyObject
    {    
      // Confirm parent and childName are valid. 
      if (parent == null) return null;

      T foundChild = null;

      int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
      for (int i = 0; i < childrenCount; i++)
      {
        var child = VisualTreeHelper.GetChild(parent, i);
        // If the child is not of the request child type child
        T childType = child as T;
        if (childType == null)
        {
          // recursively drill down the tree
          foundChild = FindChild<T>(child, childName);

          // If the child is found, break so we do not overwrite the found child. 
          if (foundChild != null) break;
        }
        else if (!string.IsNullOrEmpty(childName))
        {
          var frameworkElement = child as FrameworkElement;
          // If the child's name is set for search
          if (frameworkElement != null && frameworkElement.Name == childName)
          {
            // if the child's name is of the request name
            foundChild = (T)child;
            break;
          }
        }
        else
        {
          // child element found.
          foundChild = (T)child;
          break;
        }
      }

      return foundChild;
    }

如何通过名称或类型查找WPF控件?


我已经有了,就像我在问题中所述的那样。我正在尝试从hWnd获取窗口,以便我可以到达这一点。 - Dustin Davis
确实,Marat的答案应该可以做到这一点。我只是为了完整性而添加了这个,以防有人偶然发现并需要知道其余部分 :) - Lugoues

1

这里有一个特殊情况尚未记录。可能是因为顶级控件不是标准的WPF Window。例如,在Visual Studio 2010中就是这种情况。我在编写Visual Studio插件时发现了这一点:我想将一些WPF控件注入到可视树中,但需要找到WPF树的起点。

幸运的是,有一个解决方案:

  var hwnd = _dte.MainWindow.HWnd;
  var window = HwndSource.FromHwnd((IntPtr)hwnd);
  dynamic customWindow = window.RootVisual;
  UIElement content = customWindow.Content;

技巧在于将customWindow声明为dynamic,这样就不需要知道或指定其类型。按照良好的WPF方式,它具有一个Content属性,其中包含所有窗口内容,然后一切都正常了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接