如何提取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):
答案 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。
如果是,则父级可能为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>