c# - WPF CheckedListBox SelectionMode ="Multiple"不在 SelectionChanged 事件上更新 SelectedIndex

标签 c# wpf checkedlistbox

我正在测试 Kelly Elias 关于 Creating a WPF checkListBox 的文章.我需要获取 selectedIndex 和选中的框文本。一切都按需要工作,直到我将列表框的 SelectionMode 更改为我需要实现的“Multiple”。之后,SelectedIndex 和 SelectedItem 都不会使用 SelectionChanged 事件进行更改。这两个属性只显示第一个复选框的信息。但是,所有选中的复选框都会添加到 SelectedItems 集合中。有人可以协助解决这个问题吗?

先谢谢了!!!

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

namespace Jarloo
{
    public class Customer : INotifyPropertyChanged
    {
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                NotifyPropertyChanged("Name");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class CheckedListItem<T> : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private bool isChecked;
        private T item;

        public CheckedListItem()
        {
        }

        public CheckedListItem(T item, bool isChecked = false)
        {
            this.item = item;
            this.isChecked = isChecked;
        }

        public T Item
        {
            get { return item; }
            set 
            {
                item = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Item"));
            }
        }

        public bool IsChecked
        {
            get { return isChecked; }
            set
            {
                isChecked = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
            }
        }
    }

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public ObservableCollection<CheckedListItem<Customer>> Customers { get; set; }

        public MainWindow()
        {
            InitializeComponent();
            Customers = new ObservableCollection<CheckedListItem<Customer>>();

            Customers.Add(new CheckedListItem<Customer>(new Customer() { Name = "Kelly Smith" }));
            Customers.Add(new CheckedListItem<Customer>(new Customer() { Name = "Joe Brown" }));
            Customers.Add(new CheckedListItem<Customer>(new Customer() { Name = "Herb Dean" }));
            Customers.Add(new CheckedListItem<Customer>(new Customer() { Name = "John Paul" }));

            DataContext = this;
        }

        private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            int index = listbox1.SelectedIndex;
            string testName = ((CheckedListItem<Customer>)listbox1.SelectedValue).Item.Name;
        }
    }
}


<Window x:Class="Jarloo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" WindowStartupLocation="CenterScreen">
    <Grid>
        <ListBox Name="listbox1" ItemsSource="{Binding Customers}" SelectionChanged="ListBox_SelectionChanged"
                 SelectionMode="Multiple">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <CheckBox IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"
                              Content="{Binding Path=Item.Name}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

最佳答案

SelectionModeMultiple(或 Extended)时,控件的工作方式是设置 SelectedIndex 第一个项目被选中时(即添加到 SelectedItems)。其他项目将添加到 SelectedItems 列表的末尾,但 SelectedIndex 保持不变。

为了将此行为引入所需的结果,我们将把新添加的项目从 SelectedItems 的末尾取出,并将其(它们)重新插入到前面。但要实现这一点,我们要做的不仅仅是让最新的项目成为 SelectedItems 中的第一项。我们必须清空列表并重新添加每个条目,以便默认行为现在将所需的项目识别为 SelectedValue 并将更新 SelectedIndex。因此,我们将使用一个临时列表来提供帮助。

此外,我们将添加一个标志来指示我们当前是否正忙于更正 SelectedItems 顺序。这是必需的,因为我们将修改列表并且 ListBox.SelectionChanged 将被递归调用。

private bool busy;

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
  if (!this.busy)
  {
    this.busy = true;

    /*
     * If items were added to the end of the list,
     * reverse the list appropriately.
     * Removed items are self-handling.
     */
    if (e.AddedItems.Count > 0)
    {
      List<CheckedListItem<Customer>>  reversed = new List<CheckedListItem<Customer>>();

      // Reverse newly added items.
      foreach (var item in e.AddedItems)
      {
        listbox1.SelectedItems.Remove(item);
        reversed.Insert(0, (CheckedListItem<Customer>)item);
      }

      // Maintain previously reversed items' orders.
      foreach (var item in listbox1.SelectedItems)
      {
        reversed.Add((CheckedListItem<Customer>)item);
      }

      // Clear and reset selections to trigger SelectedIndex change.
      listbox1.UnselectAll();

      foreach (var item in reversed)
      {
        listbox1.SelectedItems.Add(item);
      }
    }

    int index = listbox1.SelectedIndex;
    string testName = listbox1.SelectedValue == null ? string.Empty : ((CheckedListItem<Customer>)listbox1.SelectedValue).Item.Name;
    System.Console.WriteLine("{0} {1}", index, testName);

    this.busy = false;
  }
}

旁注:除了 busy 标志和条件检查之外,您还可以执行以下操作:

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
  listbox1.SelectionChanged -= this.ListBox_SelectionChanged;

  // Same code within the condition...

  listbox1.SelectionChanged += this.ListBox_SelectionChanged;
}

它将达到防止递归调用和后续堆栈溢出的相同目的。我只是不知道取消订阅和订阅事件处理程序是否比 bool 检查更便宜或更昂贵(我的猜测是更多但研究,似乎没有偏好,如these所示three results )。

关于c# - WPF CheckedListBox SelectionMode ="Multiple"不在 SelectionChanged 事件上更新 SelectedIndex,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30820484/

相关文章:

c# - DataSource 中的更新重置了 CheckedListBox 复选框

c# - 带有 TextBox 的 CheckedListBox 过滤器

c# - C# 中的命名空间类冲突 - 寻找建议

c# - 在设计器中打开自定义用户控件时,Visual Studio Professional 15.9.2 崩溃

c# - 使用参数的 Prism 命令绑定(bind)?

c# - 如何将 Ctrl+,(控制加逗号)指定为 WPF 菜单项的键盘快捷键?

c# - 识别 CheckedListBox 项已被选中

c# - 通过代码检索内置的 UI Sprite

c# - 模拟接口(interface)转换回原始类

wpf - 如何在 WPF 中流式传输视频?