Кнопка внутри ListBox ItemTemplate не выбирает элемент

У меня ситуация с элементом управления ListBox. Внутри шаблона элемента есть кнопка. Когда я нажимаю кнопку, я хочу, чтобы выбранный элемент указанного ListBox изменился на элемент, в котором находится кнопка. В настоящий момент я могу изменить выбранный элемент, щелкнув в другом месте в шаблоне элемента, но это не сработает, если я выберу кнопка. Чтобы уточнить последнее предложение для комментатора, если один щелкнет шаблон элемента, который не является кнопкой, SelectedItem изменится, как и ожидалось. Если вы нажмете кнопку в шаблоне элемента, SelectedItem не изменится.

Дополнительная информация: я использую MVVM, и к кнопке прикреплена команда в модели представления. Любое решение должно позволить этому продолжать работать.

ListBox связан с ItemSource, а SelectedItem ListBox связан со свойством в модели представления.

Если есть определенный способ сделать это, я пока не смог его найти.

Я использую C# и Visual Studio 2010.

Благодарю.

4 ответа

Решение

Если вы можете использовать ToggleButton и связать IsChecked к ListBoxItemIsSelected собственность тогда будет хорошо.
Причина, потому что ваш ListBoxItem не выбран, потому что кнопка обрабатывает MouseDown событие, таким образом, делая ListBoxItem не знают о клике. В вашей кнопке создайте обработчик событий для Click и установить e.Handled = false;,

Немного вашего кода было бы полезно, но вот пример того, как сделать выбор ListBoxItem используя щелчок по кнопке через шаблон MVVM.

public class MyViewModel : BaseViewModel // implements INotifyPropertyChanged
{
    private ICommand _myCommand;

    public ICommand MyCommand { get {return _myCommand;} private set { _myCommand = value; OnPropertyChanged(); }}

    private ObservableCollection<int> _myObjects;

    public ObservableCollection<int> MyObjects { get {return _myObjects;} private set {_myObjects = value; OnPropertyChanged();}}

    private int _mySelectedObject;

    public int MySelectedObject { get {return _mySelectedObject;} set {_mySelectedObject = value; OnPropertyChanged(); }}

    public MyViewModel 
    {
        MyCommand = new RelayCommand(SetSelectedObject); // the source code for RelayCommand may be found online.
    }

    private void SetSelectedObject(object obj) 
    {
        int myInt = (int)obj;

        MySelectedObject = myInt;
    }
}

Некоторые части XAML были стерты для упрощения. Не просто скопируйте / вставьте этот фрагмент, адаптируйте его к своему коду.

<UserControl x:Name="root" DataContext="{Binding MyViewModel, Source={StaticResource Locator.MyViewModel}}">
    <ListBox ItemsSource="{Binding MyObjects}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{Binding }"/>
                    <Button Command="{Binding MyCommand, ElementName=root}" CommandParameter="{Binding }"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</UserControl>

Я не проверял этот код. Так что могут быть некоторые ошибки. Не стесняйтесь указывать на это, и я обновлю свой код.

РЕДАКТИРОВАТЬ: Вот исходный код для моей реализации RelayCommand (модифицированный от Telerik):

public class RelayCommand : ICommand
{
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    private Action<object> _methodToExecute;
    private Func<object, bool> _canExecuteEvaluator;

    public RelayCommand(Action<object> methodToExecute, Func<object, bool> canExecuteEvaluator)
    {
        _methodToExecute = methodToExecute;
        _canExecuteEvaluator = canExecuteEvaluator;
    }

    public RelayCommand(Action<object> methodToExecute)
        : this(methodToExecute, null)
    {
    }

    public bool CanExecute(object parameter)
    {
        if (_canExecuteEvaluator == null)
        {
            return true;
        }
        else
        {
            bool result = _canExecuteEvaluator.Invoke(parameter);
            return result;
        }
    }

    public void Execute(object parameter)
    {
        _umethodToExecute.Invoke(parameter);
    }
}

Вы можете получить Listbox, который является родительским для кнопки, используя эту функцию:

    Function GetParent(child As UIElement, parentType As Type) As UIElement
        If child Is Nothing Then Return Nothing
        Dim p = child
        Do
            p = TryCast(VisualTreeHelper.GetParent(p), UIElement)
            If p Is Nothing Then Return Nothing
            If p.GetType Is parentType Then Return p
        Loop
    End Function

Это код, который выбирает элемент. Добавьте его в событие Click кнопки:

Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
  Dim lst As ListBox = GetParent(sender, GetType(ListBox))
  lst.SelectedIndex = lst.Items.IndexOf(Me.DataContext)
End Sub

С# версия:

        private void Button_Click(object sender, RoutedEventArgs e)
    {
        Button b = sender as Button;
        TListBoxItems data = b.DataContext as TListBoxItems;
        ListBox aLB = new ListBox();
        aLB = (ListBox)GetParent(sender, aLB.GetType());
        aLB.SelectedIndex = aLB.Items.IndexOf(data);
    }

    public object GetParent(object child, Type parentType)
    {
        if (child == null)
            return null/* TODO Change to default(_) if this is not a reference type */;
        var p = child;
        do
        {
            p = VisualTreeHelper.GetParent((UIElement)p) as UIElement;
            if (p == null)
                return null/* TODO Change to default(_) if this is not a reference type */;
            if (p.GetType() == parentType)
                return p;
        }
        while (true);
    }
Другие вопросы по тегам