ListBox ItemTemplate 中的按钮未选择项目

Button within ListBox ItemTemplate not selecting item

我遇到了 ListBox 控件的情况。在项目模板内有一个按钮。当我单击按钮时,我想 selected 所述 ListBox 的项目更改为按钮所在的项目。目前,我可以通过单击项目模板中的其他位置来更改 selected 项目,但如果我 select 按钮,那将不起作用。为了向评论者澄清最后一句话,如果在不是按钮的项目模板中单击,则 SelectedItem 将按预期更改。如果您单击项目模板中的按钮,SelectedItem 将不会更改。

更多信息:我正在使用 MVVM,并且按钮在视图模型中附加了一个命令。任何解决方案都需要允许它继续工作。

ListBox 链接到 ItemSource,ListBox 的 SelectedItem 绑定到视图模型中的一个 属性。

如果有固定的方法可以做到这一点,我目前还没有找到。

我正在使用 C# 和 Visual Studio 2010。

谢谢。

如果你可以使用 ToggleButton 并将 IsChecked 绑定到 ListBoxItem IsSelected 属性 就可以了。
您的 ListBoxItem 未被选中的原因是按钮正在处理 MouseDown 事件,从而使 ListBoxItem 不知道点击。在您的按钮中为 Click 创建一个事件处理程序并设置 e.Handled = false;.

您的一些代码会有所帮助,但这里有一个虚拟示例,说明如何通过 MVVM 模式通过单击按钮来执行 select ListBoxItem

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 部分已被删除,以保持简单。不要简单地 copy/paste 此代码段,使其适应您的代码。

<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);
    }
}

您可以使用此函数获取作为按钮父级的列表框:

    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

C# 版本:

  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);
    }