【问题标题】:ListBoxItem.Parent returns nothing, unable to get it thru VisualTreeHelper.GetParent eitherListBoxItem.Parent 什么也不返回,也无法通过 VisualTreeHelper.GetParent 得到它
【发布时间】:2010-04-13 20:53:02
【问题描述】:

如何提取 ListBoxItem 的父容器? 在下面的示例中,我可以一直到 ListBoxItem,比我得到的更高:

<ListBox Name="lbAddress">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <Button Click="Button_Click"/>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

Private Sub Button_Click(sender As Button, e As RoutedEventArgs)
  Dim lbAddress = GetAncestor(Of ListBox) 'Result: Nothing
End Sub

Public Shared Function GetAncestor(Of T)(reference As DependencyObject) As T
  Dim parent = GetParent(reference)

  While parent IsNot Nothing AndAlso Not parent.GetType.Equals(GetType(T))
    parent = GetAncestor(Of T)(parent)
  End While

  If parent IsNot Nothing Then _
    Return If(parent.GetType Is GetType(T), parent, Nothing) 

  Return Nothing    
End Sub

Public Function GetParent(reference As DependencyObject) As DependencyObject
  Dim parent As DependencyObject = Nothing

 If TypeOf reference Is FrameworkElement Then
    parent = DirectCast(reference, FrameworkElement).Parent
  ElseIf TypeOf reference Is FrameworkContentElement Then
    parent = DirectCast(reference, FrameworkContentElement).Parent
  End If

  Return If(parent, VisualTreeHelper.GetParent(reference))
End Function

更新

会发生这种情况(注意“父”变量保持为空):

【问题讨论】:

  • 请注意,我希望方向是这种方式,而不是从顶部项目中搜索名称“lbAddress”。
  • GetVisualAncestor/GetAncestor 是扩展方法吗?
  • 是的,不过和上面的sn-p一样。

标签: wpf xaml listbox listboxitem visual-tree


【解决方案1】:

很明显,在单击按钮期间某些东西正在从lbAddress.ItemsSource 中删除一个项目。问题是,什么?仔细查看您发布的图像会发现答案。这是您代码中的错误:

My.Context.DeleteObject(context)
My.Context.SaveChanges()

   ...

btn.GetVisualAncestor(...)

前两行更新您的数据模型。这会立即从可视树中删除 ListBoxItem。当您稍后调用 GetVisualAncestor 以查找 ListBox 时,它会失败,因为 ListBoxItem 不再具有任何类型的父级。

我相信解决方案现在对您来说很明显:只需找到 ListBox 祖先从数据模型中删除数据之前,您就可以开始了。

【讨论】:

    【解决方案2】:

    罪魁祸首似乎是您的 GetParent 函数,它使用 Parent 属性而不是 VisualTreeHelper.GetParent。 Parent 属性返回 logical 父级,而不是可视父级,因此在尝试遍历 DataTemplate 时将返回 null。 (也不清楚 GetVisualAncestor 是如何实现的——或者这是 GetAncestor 的拼写错误?)始终使用 VisualTreeHelper.GetParent,忘记 Parent 属性。例如,以下代码适用于我:

    XAML:

    <DataTemplate x:Key="SimpleItemTemplate">
      <Button Click="Button_Click">In DataTemplate</Button>
    </DataTemplate>
    

    后面的代码:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
      Button btn = (Button)sender;
      ListBox lb = FindAncestor<ListBox>(btn);
      Debug.WriteLine(lb);
    }
    
    public static T FindAncestor<T>(DependencyObject from)
      where T : class
    {
      if (from == null)
      {
        return null;
      }
    
      T candidate = from as T;
      if (candidate != null)
      {
        return candidate;
      }
    
      return FindAncestor<T>(VisualTreeHelper.GetParent(from));
    }
    

    当我运行它时,Click 处理程序中的 ListBox 变量 (lb) 被设置为正确的非空值。

    【讨论】:

    • 您可以使用循环避免递归;)
    • 我上面带的例子,是嵌套在其他父ItemControls中的。但是在我的代码中, ListBoxItem 不会返回其父级。它应该。还有什么其他原因?我使用 Mole (codeproject.com/KB/macros/MoleForVisualStudioEdit.aspx) 和上面的 ListBoxItem 我确实没有看到 nuttn。
    • 无论如何,GetParent 函数应该可以正常工作,注意它末尾的 If 运算符,如果您不使用 VB,那么这个键将用作 csharp 的 ??运算符。
    • Shimmy,如果你运行我的代码,你会得到什么?正如我所说,它绝对适用于我的测试环境。
    • 奇怪。我想这一定与所有额外的嵌套有关,尽管我无法想象为什么这会阻止 VisualTreeHelper 找到 ListBoxItem 的可视父级。我只能建议您尝试将其修剪为简单的重现,并使用完整的 XAML 更新您的问题,以便人们可以尝试重现您看到的问题。抱歉,我不能提供更多帮助。哦,也许还值得说一下你使用的是哪个版本的框架——我只在 3.5 上测试过。
    【解决方案3】:

    Parent 属性返回 逻辑 父级。您应该使用 visual 父级,因为在某些情况下逻辑父级将为空。例如,在您的情况下,按钮的 Parent 属性返回 null。

    From MSDN

    在以下情况下,Parent 可能为 null 元素被实例化,但不是 附加到任何逻辑树 最终连接到页面级别 根元素或应用程序 对象。

    由于FrameworkElement.VisualParent 属性受到保护,您可以使用VisualTreeHelper.GetParent 方法:

    <System.Runtime.CompilerServices.Extension> _
    Public Shared Function FindAncestor(Of T As DependencyObject)(ByVal obj As DependencyObject) As T
        Return TryCast(obj.FindAncestor(GetType(T)), T)
    End Function
    
    <System.Runtime.CompilerServices.Extension> _
    Public Shared Function FindAncestor(ByVal obj As DependencyObject, ByVal ancestorType As Type) As DependencyObject
        Dim tmp = VisualTreeHelper.GetParent(obj)
        While tmp IsNot Nothing AndAlso Not ancestorType.IsAssignableFrom(tmp.[GetType]())
            tmp = VisualTreeHelper.GetParent(tmp)
        End While
        Return tmp
    End Function
    

    【讨论】:

    • 如果你看我的代码,你可以看到 VisualTreeHelper 是用来获取它的,它什么也没返回,也许问题是别的。
    【解决方案4】:

    至于公认的 hacky XAML-only 解决方案,您可以使用 Style setter 将父 ListBox 填充到 ListBoxItem's Tag 属性中(如果您没有将其用于任何其他目的):

    <ListBox Name="w_listbox" ItemsSource="{Binding MyItems}">
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <Setter Property="Tag" Value="{Binding ElementName=w_listbox}" />
            </Style>
        </ListBox.ItemContainerStyle>
        <!-- etc, i.e.... !-->
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0" Text="{Binding MyFoo}"></TextBlock>
                <TextBlock Grid.Column="1" Text="{Binding MyBar}"></TextBlock>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-12
      • 2021-12-27
      • 1970-01-01
      • 2015-02-06
      • 1970-01-01
      相关资源
      最近更新 更多