wpf - dependency - ListBoxItem.Parent no devuelve nada, no se puede obtener a través de VisualTreeHelper.GetParent tampoco
dependency properties in wpf (4)
El culpable parece ser su función GetParent, que usa la propiedad Parent en lugar de VisualTreeHelper.GetParent. La propiedad Parent devuelve el elemento primario lógico , no el primario visual, y por lo tanto devolverá nulo cuando intente salir de DataTemplate. (Tampoco está claro cómo se implementa GetVisualAncestor, ¿o es un error tipográfico para GetAncestor?) Use VisualTreeHelper.GetParent de forma coherente y olvídese de la propiedad Parent. Por ejemplo, el siguiente código funciona para mí:
XAML:
<DataTemplate x:Key="SimpleItemTemplate">
<Button Click="Button_Click">In DataTemplate</Button>
</DataTemplate>
Código detrás:
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));
}
Cuando ejecuto esto, la variable ListBox ( lb
) en el controlador Click se establece en el valor correcto no nulo.
¿Cómo extraigo el contenedor padre de un ListBoxItem? En el siguiente ejemplo puedo ir hasta el ListBoxItem, más alto que el que obtengo 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
Actualizar
Esto es lo que ocurre (tenga en cuenta que la variable ''principal'' sigue siendo nula):
La propiedad Parent
devuelve el elemento primario lógico . Debería usar el elemento primario visual , porque en algunos casos el elemento primario lógico será nulo. Por ejemplo, en su caso, la propiedad principal del botón devuelve nulo.
El elemento primario puede ser nulo en los casos en que se creó una instancia de un elemento, pero no se adjunta a ningún árbol lógico que eventualmente se conecte al elemento raíz de nivel de página, o al objeto de la aplicación.
Como la propiedad FrameworkElement.VisualParent
está protegida, puede usar el método 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
Es totalmente obvio que algo está eliminando un elemento de lbAddress.ItemsSource
durante el clic del botón. La pregunta es, ¿qué ? Una mirada más de cerca a la imagen que publica revela la respuesta. Aquí está el error en tu código:
My.Context.DeleteObject(context)
My.Context.SaveChanges()
...
btn.GetVisualAncestor(...)
Las primeras dos líneas actualizan su modelo de datos. Esto elimina inmediatamente el ListBoxItem del árbol visual. Cuando al llamar a GetVisualAncestor más tarde para encontrar el ListBox falla porque el ListBoxItem ya no tiene un elemento primario de ningún tipo.
Estoy seguro de que la solución ahora es obvia para usted: simplemente busque el ancestro ListBox antes de eliminar los datos del modelo de datos y estará listo para continuar.
En cuanto a una solución que solo admite hacks XAML, puede usar un setter de Estilo para rellenar el ListBox
padre en la propiedad Tag
ListBoxItem''s
(si no lo está usando para ningún otro propósito):
<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>