【问题标题】:Find control's ancestor by type按类型查找控件的祖先
【发布时间】:2018-11-08 05:17:06
【问题描述】:

我在另一个UserControl 中有一个UserControl“孩子”(在TabControl 中充当TabItem)。在子 UserControl 和 TabItem 祖先之间是许多其他控件(例如:Grids、StackPanel、可能是 ScrollViewer 等)。

我想在我的孩子UserControl 中访问 TabItem UserControl 的一个属性,并自定义了一个沿 Visual 树向上遍历的 commonly suggested 递归函数。但是,这总是在第一次空检查时返回 true,直到我在逻辑树上添加查询。

代码:

public MyTabItem FindParentTabItem(DependencyObject child)
{
  DependencyObject parent = VisualTreeHelper.GetParent(child) ?? LogicalTreeHelper.GetParent(child);

  // are we at the top of the tree
  if (parent == null)
  {
      return null;
  }
  MyTabItem parentTabItem = parent as MyTabItem;
  if (parentTabItem != null)
  {
    return parentTabItem;
  }
  else
  {
    //use recursion until it reaches the control
    return FindParentTabItem(parent);
  }
}

不幸的是,这也返回 null。当单步执行该方法时,我看到它确实找到了正确的UserControl TabItem,但是当它通过返回递归(?)返回时,它将它恢复为 null,然后返回到调用方法(在子 @ 987654335@的Loaded事件):

MyTabItem tab = FindParentTabItem(this);

如何解决此问题,以便我的方法正确返回找到的 MyTabItem

【问题讨论】:

  • 用户控件不应直接访问其父元素之一的属性。为什么不简单地使用一个视图模型并绑定相关的控件属性呢?
  • @Clemens 这就是我正在做的事情,但时间限制意味着我正在一点一点做。在更改另一个区域以使用 ViewModel 并正确执行 mvvm 后,这一点坏了。在正确处理此问题之前,我需要快速(使其工作)修复。

标签: c# wpf visualtreehelper


【解决方案1】:

这是一个有效的单元测试解决方案。

public static T FindAncestor<T>(DependencyObject obj)
    where T : DependencyObject
{
    if (obj != null)
    {
        var dependObj = obj;
        do
        {
            dependObj = GetParent(dependObj);
            if (dependObj is T)
                return dependObj as T;
        }
        while (dependObj != null);
    }

    return null;
}

public static DependencyObject GetParent(DependencyObject obj)
{
    if (obj == null)
        return null;
    if (obj is ContentElement)
    {
        var parent = ContentOperations.GetParent(obj as ContentElement);
        if (parent != null)
            return parent;
        if (obj is FrameworkContentElement)
            return (obj as FrameworkContentElement).Parent;
        return null;
    }

    return VisualTreeHelper.GetParent(obj);
}

用法是

FindAncestor<MyTabItemType>(someChild);

编辑:

假设您的 xaml 看起来像您描述的那样:

<UserControl>
    <Grid></Grid>
    <StackPanel></StackPanel>
    <!-- Probably also something around your child -->
    <Grid>
        <UserControl x:Name="child"/>
    </Grid>
</UserControl>

您目前在您的 child-xaml.cs 中

void OnChildUserControlLoaded(object sender, RoutedEventArgs e)
{
    var parent = FindAncestor<ParentUserControlType>(this);
    DoSomething(parent.SomeProperty);
}

除非您执行未描述的操作,否则代码将按原样运行。
我建议您向MCVE 提供所有必要的信息。

【讨论】:

  • 这首先返回 null GetParent() 调用
  • 我的意思是把它描述为:&lt;UserControl&gt;&lt;Grid&gt;&lt;StackPanel&gt;&lt;MyChildControl /&gt;&lt;/StackPanel&gt;&lt;/Grid&gt;&lt;/UserControl&gt;(我的意思是“介于”之间),但没关系,我已经找到了一种不同的方法,从 MainWindow 开始并努力工作(而不是从我的孩子控制下)欢呼和感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-22
  • 1970-01-01
  • 2022-11-17
  • 1970-01-01
相关资源
最近更新 更多