Как выбрать ListBoxItem при нажатии на кнопку в шаблоне? - PullRequest
5 голосов
/ 15 сентября 2010

У меня есть следующий шаблон данных, примененный к ListBox:

<DataTemplate x:Key="MyTemplate" DataType="{x:Type DAL:Person}">
    <StackPanel Orientation="Horizontal">
        <Button Content="X" Command="{x:Static cmd:MyCommands.Remove}"/>
        <TextBlock Text="{Binding Person.FullName}" />
    </StackPanel>
</DataTemplate>

Когда я нажимаю на кнопку, команда запускается, но ListBoxItem не выбирается.Как заставить его быть выбранным, чтобы я мог получить выбранный элемент в моем «выполненном» методе?

Спасибо

Ответы [ 3 ]

11 голосов
/ 16 сентября 2010

Лучший способ, поскольку вы не очень заинтересованы в выборе элемента (потому что он все равно будет быстро удален), состоит в том, чтобы передать сам элемент в Command как CommandParameter.

В качестве альтернативы выможет идти окольным путем либо с выделенным кодом, либо с помощью триггеров, но я не думаю, что это было бы так.Например:

вы можете обработать событие ButtonBase.Click в своем списке, например

<ListBox ButtonBase.Click="lb_Click"
...

, затем в своем коде выполните следующее:

private void lb_Click(object sender, RoutedEventArgs e)
{
    object clicked = (e.OriginalSource as FrameworkElement).DataContext;
    var lbi = lb.ItemContainerGenerator.ContainerFromItem(clicked) as ListBoxItem;
    lbi.IsSelected = true;
}

Этополучает связанный элемент, по которому щелкнули, потому что текст данных кнопки унаследован от ее шаблонного элемента, а затем фактического автоматически сгенерированного ListBoxItem из ItemContainerGenerator в ListBox и устанавливает для свойства IsSelected значение true.Я думаю, что это один из самых быстрых и простых способов.Также работает с несколькими производными от ButtonBase объектами в шаблоне.

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

public class SelectItemOnButtonClick : Behavior<ListBox>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(handler), true);
    }
    protected override void OnDetaching()
    {
        this.AssociatedObject.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(handler));
        base.OnDetaching();
    }
    private void handler(object s, RoutedEventArgs e)
    {
        object clicked = (e.OriginalSource as FrameworkElement).DataContext;
        var lbi = AssociatedObject.ItemContainerGenerator.ContainerFromItem(clicked) as ListBoxItem;
        lbi.IsSelected = true;
    }
}

Вы можете использовать его следующим образом:

<ListBox xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" ...>
    <i:Interaction.Behaviors>
        <local:SelectItemOnButtonClick />
    </i:Interaction.Behaviors>
</ListBox>

Добавить код обработки ошибок, например, по крайней мере, нулевые проверки - конечно, не хотелось бы, чтобы такая простая вещь, как эта бомбардировка вашего приложения.

Чтобы понять проблему, кнопка устанавливает для свойства Handled значение true для всех событий мыши, которые воздействуют на него (MouseDown / Click), чтобы они не рассматривались ListBoxItem.Вы также можете прикрепить событие MouseDown к ListBox и перемещаться по визуальному дереву вверх, пока не достигнете родительского ListBoxItem, но это гораздо сложнее ... а если вам интересно, вы можете прочитать эту статью , чтобызнаю, почему, в основном, вы также столкнетесь с FrameworkContentElements (который также отвечает на MouseDown), так что код станет более сложным, с тем преимуществом, что все, что щелкнуло внутри таблички данных, приведет к выбору ListBoxItem, независимо от того, помечено ли событие какобработано.

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

1 голос
/ 16 сентября 2010

Сделать базовый объект доступным для свойства RemoveCommand и привязать к нему свойство Command кнопки. Это упрощает шаблон данных; это также значительно упрощает случай, когда логика приложения может диктовать, что определенный элемент не может быть удален.

0 голосов
/ 19 мая 2017

Алекс, спасибо за ответ. Ваше решение с поведением великолепно. Первое решение не так хорошо, потому что это будет работать, только если вы нажмете на определенную кнопку. Вот еще одно решение, которое будет работать при нажатии на произвольную форму элемента управления ListBoxItem шаблона:

<ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem" 
           BasedOn="{StaticResource {x:Type ListBoxItem}}">
        <Style.Triggers>
            <Trigger Property="IsKeyboardFocusWithin" Value="True">
                <Setter Property="IsSelected" Value="True"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</ListBox.ItemContainerStyle>

Это только подход XAML. Я также установил свойство BasedOn только для того, чтобы не переопределять текущий стиль ListBoxItem.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...