ListBoxItem.Parent returns nothing, cannot receive it through VisualTreeHelper.GetParent either

How to retrieve parent ListBoxItem container? In the following example, I can go to ListBoxItem, higher than I get 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 

Update

This is what happens (note that the β€œparent” variable remains empty):

This is how it looks like

+4
source share
4 answers

It is clear that something is removing the item from lbAddress.ItemsSource at the time the button is clicked. The question is what ? A closer look at the image you posted shows the answer. Here is the error in your code:

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

The first two lines update your data model. This immediately removes the ListBoxItem from the visual tree. When you later call GetVisualAncestor to find the ListBox, it fails because the ListBoxItem no longer has a parent of any type.

I am sure that the solution is now obvious to you: just find the ListBox ancestor before , you will remove the data from the data model, and you will be fine.

+4
source

The culprit is represented by your GetParent function, which uses the Parent property instead of VisualTreeHelper.GetParent. The Parent property returns the logical parent, not the visual parent, and therefore returns null when trying to jump from the DataTemplate. (It is also unclear how the GetVisualAncestor function is implemented - or is it a typo for GetAncestor?) Use VisualTreeHelper.GetParent consistently and forget about the parent property. For example, the following code works for me:

XAML:

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

Code behind:

 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)); } 

When I ran this, the ListBox ( lb ) variable in the Click handler is set to the correct non-zero value.

+2
source

The Parent property returns the logical parent element. You must use a visual parent because in some cases the logical parent will be null. For example, in your case, the Parent property of the button returns null.

From MSDN :

A parent can be null in cases where an element was created, but not tied to any logical tree that ultimately connects to the page level of the root element or application object.

Since the FrameworkElement.VisualParent property is protected, you can use the VisualTreeHelper.GetParent method:

 <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 
0
source

As for the generally accepted XAML hacker solution, you can use the style installer to populate the parent ListBox in the ListBoxItem's Tag property (if you are not using it for any other purpose):

 <ListBox Name="w_listbox" ItemsSource="{Binding MyItems}"> <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem"> <Setter Property="Tag" Value="{Binding ElementName=w_listbox}" /> </Style> </ListBox.ItemContainerStyle> <!-- etc, ie... !--> <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> 
0
source

Source: https://habr.com/ru/post/1306853/


All Articles