WPF CheckedListBox SelectionMode =“Multiple”未在SelectionChanged事件

时间:2015-06-13 15:39:35

标签: c# wpf checkedlistbox

我正在测试Kelly Elias关于Creating a WPF checkListBox的文章。我需要获取selectedIndex和checkedbox文本。一切都按需要工作,直到我将列表框的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>

1 个答案:

答案 0 :(得分:0)

SelectionMode Multiple(或Extended)时控件的工作方式是在第一项时设置SelectedIndex 被选中(IE添加到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;
}

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