У меня есть данные ListBox, привязанные к коллекции моего класса PersonCollection. Затем я определил шаблон данных для объектов типа Person, состоящий из DockPanel, который содержит TextBlock для имени человека и Button для удаления этого человека из список. Все вместе смотрится очень красиво.

Проблема, с которой я столкнулся, заключается в том, что я не могу добраться до выбранного элемента (и удалить его) в поле списка, когда я нажимаю кнопку, определенную в шаблоне данных. Вот обработчик кнопки:

private void RemovePersonButton_Click(object sender, RoutedEventArgs e)
{
    Button clickedButton = (Button)e.Source;
    DockPanel buttonPanel = (DockPanel)clickedButton.Parent;
    Control control = (Control)button.Parent;
}

Последний созданный объект control - это null, т.е. я не могу продвигаться дальше по дереву элементов, поэтому я не могу добраться до списка и его SelectedItem. Здесь важно отметить, что нельзя просто получить выбранный элемент из списка, вызвав его, потому что у меня есть более одного списка в окне, и все эти списки реализуют один и тот же шаблон данных, то есть используют один и тот же обработчик событий для кнопку удаления.

Буду признателен за любую помощь, которую я могу получить. Спасибо.

1
Boris 1 Дек 2010 в 01:59

2 ответа

Лучший ответ

Если я правильно понимаю вопрос, я думаю, вы сможете получить человека из DataContext кнопки

private void RemovePersonButton_Click(object sender, RoutedEventArgs e) 
{
    Button clickedButton = (Button)e.Source; 
    Person selectedItem = clickedButton.DataContext as Person;
    if (selectedItem != null)
    {
        PersonCollection.Remove(selectedItem);
    }
}

Другой способ - найти ListBox в VisualTree.

private void RemovePersonButton_Click(object sender, RoutedEventArgs e) 
{
    Button clickedButton = (Button)e.Source; 
    ListBox listBoxParent = GetVisualParent<ListBox>(clickedButton );
    Person selectedItem = listBoxParent.SelectedItem as Person;
    //...
}

public T GetVisualParent<T>(object childObject) where T : Visual
{
    DependencyObject child = childObject as DependencyObject;
    while ((child != null) && !(child is T))
    {
        child = VisualTreeHelper.GetParent(child);
    }
    return child as T;
}
3
Fredrik Hedblad 1 Дек 2010 в 02:09
Верный! Очень хороший. Я не знал о свойстве DataContext. Как бы то ни было, я смог разобраться в дереве элементов, вызвав метод VisualTreeHelper.GetParent(DependencyObject reference), но это гораздо лучшее решение! Спасибо.
 – 
Boris
1 Дек 2010 в 02:18
Я предпочитаю первое решение. Спасибо за публикацию!
 – 
Boris
1 Дек 2010 в 02:20
@Boris: Конечно! Да, первая версия точно лучше :) Добавил вторую, потому что не был уверен, нужен ли вам соответствующий ListBox для чего-то
 – 
Fredrik Hedblad
1 Дек 2010 в 02:29

Вы можете попробовать использовать VisualTreeHelper.GetParent обходить визуальное дерево, а не полагаться на логического родителя.

Тем не менее, вы можете подумать, можете ли вы обернуть свой Person в класс PersonItem с дополнительной контекстной информацией, чтобы PersonItem знал, как удалить Person из списка. Иногда я использую этот шаблон, и я написал класс EncapsulatingCollection, который автоматически создает экземпляры объектов-оболочек на основе изменений в отслеживаемой ObservableCollection.

0
Dan Bryant 1 Дек 2010 в 02:14
Спасибо за ваш ответ. Обернуть человека - это отличная идея, однако решение, предоставленное Meleak, - это практически все, что мне нужно на данный момент. Ваше здоровье.
 – 
Boris
1 Дек 2010 в 02:22