ListBoxItem.Parent什么都不返回,无法通过VisualTreeHelper.GetParent获取它

时间:2010-04-13 20:53:03

标签: wpf xaml listbox listboxitem visual-tree

如何提取ListBoxItem的父容器? 在下面的例子中,我可以直到ListBoxItem,高于我得到Nothing:

<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

更新

这就是发生的事情(注意'parent'变量保持为null):

This is how it looks like

4 个答案:

答案 0 :(得分:4)

在按钮点击期间从lbAddress.ItemsSource删除项目是显而易见的。问题是,什么?仔细查看您发布的图像可以看出答案。这是代码中的错误:

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

   ...

btn.GetVisualAncestor(...)

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

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

答案 1 :(得分:2)

罪魁祸首似乎是你的GetParent函数,它使用Parent属性而不是VisualTreeHelper.GetParent。 Parent属性返回逻辑父级,而不是可视父级,因此在尝试遍历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)被设置为正确的非空值。

答案 2 :(得分:0)

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

From MSDN

  

如果是,则父级可能为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

答案 3 :(得分:0)

对于一个公认的hacky-XACKL解决方案,您可以使用样式设置器将父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>