ListBoxItem.Parent ничего не возвращает, не может получить его через VisualTreeHelper.GetParent. - PullRequest
4 голосов
/ 14 апреля 2010

Как мне извлечь родительский контейнер 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

Обновление

Вот что происходит (обратите внимание, что переменная 'parent' остается нулевой):

This is how it looks like

Ответы [ 4 ]

4 голосов
/ 15 апреля 2010

Совершенно очевидно, что что-то удаляет элемент из lbAddress.ItemsSource во время нажатия кнопки. Вопрос в том, что ? Более внимательный взгляд на изображение, которое вы разместили, показывает ответ. Вот ошибка в вашем коде:

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

   ...

btn.GetVisualAncestor(...)

Первые две строки обновляют вашу модель данных. Это немедленно удаляет ListBoxItem из визуального дерева. Когда при последующем вызове GetVisualAncestor для поиска ListBox происходит сбой, поскольку ListBoxItem больше не имеет какого-либо родителя.

Я уверен, что решение теперь для вас очевидно: просто найдите предка ListBox до того, как вы удалите данные из модели данных, и все будет хорошо.

2 голосов
/ 14 апреля 2010

Похоже, виновником является ваша функция GetParent, которая использует свойство Parent вместо VisualTreeHelper.GetParent. Свойство Parent возвращает логический родительский элемент, а не визуальный родительский элемент, и, следовательно, будет возвращать значение NULL при попытке перехода из DataTemplate. (Также непонятно, как реализован 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));
}

Когда я запускаю это, переменная ListBox (lb) в обработчике Click устанавливается на правильное ненулевое значение.

0 голосов
/ 10 января 2012

Что касается общеизвестного хаклинского решения только для XAML, вы можете использовать установщик стиля, чтобы вставить родительский элемент 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>
0 голосов
/ 14 апреля 2010

Свойство Parent возвращает логического логического . Вы должны использовать visual parent, потому что в некоторых случаях логический родитель будет нулевым. Например, в вашем случае свойство Parent кнопки возвращает ноль.

От MSDN :

Родитель может быть нулевым в тех случаях, когда элемент был создан, но не прикреплен к любому логическому дереву, которое в конечном итоге подключается к уровню страницы корневой элемент или приложение объект.

Поскольку свойство 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
...