Множественный выбор списка

Есть ли способ заставить элемент управления listview обрабатывать все клики, как если бы они были сделаны с помощью клавиши Control?

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

Заранее спасибо.


person Evan    schedule 17.09.2008    source источник


Ответы (7)


Это не стандартное поведение элемента управления ListView, даже если для параметра MultiSelect установлено значение true.

Если вы хотите создать свой собственный пользовательский элемент управления, вам необходимо сделать следующее:

  1. Получить элемент управления из ListView
  2. добавить обработчик события "Выбрано".
  3. В «OnSelected» сохраните свой собственный список выбранных элементов.
  4. Если нового выбранного элемента нет в вашем списке, добавьте его. Если это так, удалите его.
  5. В коде выберите все элементы в вашем списке.

Должно быть достаточно просто для реализации и ощущения множественного выбора без использования клавиши управления!

person Ray Hayes    schedule 17.09.2008
comment
Я сам работал в этом направлении, но хотел проверить и посмотреть, есть ли что-то более простое, прежде чем приступить к исправлению. Спасибо за ваш ответ. - person Evan; 17.09.2008
comment
Убедитесь, что в списке выбранных элементов хранится содержимое, а не индекс. Использование индекса проще, но это означает, что вам нужно синхронизировать ListBox.ItemsCollection и ваш список. Надеюсь, ваш объект или строка достаточно уникальны, чтобы сохранить эту связь! - person Ray Hayes; 17.09.2008

Вот полное решение, которое является модификацией решения, предоставленного Мэтью М. выше.

Он предлагает улучшение, а также немного добавленной функциональности.

Улучшение:

  • Щелчок левой кнопкой мыши по элементу управления переводит фокус на элемент управления.
  • поведение щелчка правой кнопкой мыши согласовано (одиночный выбор)

Добавлен функционал:

  • элемент управления имеет свойство (MultiSelectionLimit), позволяющее установить ограничение на количество элементов, которые можно выбрать одновременно.

После моего первого сообщения я понял небольшую проблему с кодом. Очистка множественного выбора привела бы к тому, что событие ItemSelectionChanged вызывалось несколько раз.
Я не мог найти способа избежать этого с текущим наследованием, поэтому вместо этого я принял решение, в котором логическое свойство SelectionsBeingCleared будет иметь значение true, пока все выбранные элементы не будут был отменен.

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

public class ListViewMultiSelect : ListView
{
    public const int WM_LBUTTONDOWN = 0x0201;
    public const int WM_RBUTTONDOWN = 0x0204;

    private bool _selectionsBeingCleared;
    /// <summary>
    /// Returns a boolean indicating if multiple items are being deselected.
    /// </summary>
    /// <remarks> This value can be used to avoid updating through events before all deselections have been carried out.</remarks>
    public bool SelectionsBeingCleared
    {
        get
        {
            return this._selectionsBeingCleared;
        }
        private set
        {
            this._selectionsBeingCleared = value;
        }
    }
    private int _multiSelectionLimit;
    /// <summary>
    /// The limit to how many items that can be selected simultaneously. Set value to zero for unlimited selections.
    /// </summary>
    public int MultiSelectionLimit
    {
        get
        {
            return this._multiSelectionLimit;
        }
        set
        {
            this._multiSelectionLimit = Math.Max(value, 0);
        }
    }

    public ListViewMultiSelect()
    {
        this.ItemSelectionChanged += this.multiSelectionListView_ItemSelectionChanged;
    }

    public ListViewMultiSelect(int selectionsLimit)
        : this()
    {
        this.MultiSelectionLimit = selectionsLimit;
    }

    private void multiSelectionListView_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
    {
        if (e.IsSelected)
        {
            if (this.MultiSelectionLimit > 0 && this.SelectedItems.Count > this.MultiSelectionLimit)
            {
                this._selectionsBeingCleared = true;
                List<ListViewItem> itemsToDeselect = this.SelectedItems.Cast<ListViewItem>().Except(new ListViewItem[] { e.Item }).ToList();

                foreach (ListViewItem item in itemsToDeselect.Skip(1)) { 
                    item.Selected = false; 
                }

                this._selectionsBeingCleared = false;
                itemsToDeselect[0].Selected = false;
            }
        }
    }

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_LBUTTONDOWN:
                if (this.SelectedItems.Count == 0 || !this.MultiSelect) { break; }
                if (this.MultiSelectionLimit > 0 && this.SelectedItems.Count > this.MultiSelectionLimit) { this.ClearSelections(); }

                int x = (m.LParam.ToInt32() & 0xffff);
                int y = (m.LParam.ToInt32() >> 16) & 0xffff;
                ListViewHitTestInfo hitTest = this.HitTest(x, y);

                if (hitTest != null && hitTest.Item != null) { hitTest.Item.Selected = !hitTest.Item.Selected; }
                this.Focus();
                return;
            case WM_RBUTTONDOWN:
                if (this.SelectedItems.Count > 0) { this.ClearSelections(); }
                break;
        }
        base.WndProc(ref m);
    }

    private void ClearSelections()
    {
        this._selectionsBeingCleared = true;
        SelectedListViewItemCollection itemsToDeselect = this.SelectedItems;
        foreach (ListViewItem item in itemsToDeselect.Cast<ListViewItem>().Skip(1)) { 
            item.Selected = false; 
        }
        this._selectionsBeingCleared = false;
        this.SelectedItems.Clear();
    }
}
person rdongart    schedule 03.05.2013

Вы также можете рассмотреть возможность использования флажков в виде списка. Это очевидный способ донести концепцию множественного выбора до среднего пользователя, который может не знать о Ctrl+Click.

Со страницы MSDN:

Свойство CheckBoxes предлагает способ выбора нескольких элементов в элементе управления ListView без использования клавиши CTRL. В зависимости от вашего приложения использование флажков для выбора элементов вместо стандартного метода множественного выбора может быть проще для пользователя. Даже если для свойства MultiSelect элемента управления ListView задано значение false, вы все равно можете отображать флажки и предоставлять пользователю возможность множественного выбора. Эта функция может быть полезна, если вы не хотите, чтобы было выбрано несколько элементов, но все же хотите разрешить пользователю выбирать несколько элементов из списка для выполнения операции в вашем приложении.

person Chris Karcher    schedule 17.09.2008

Вот полное решение, которое я использовал для решения этой проблемы с помощью WndProc. По сути, он выполняет проверку попадания при щелчке мыши. Затем, если MutliSelect включен, он автоматически включает/выключает элемент [.Selected] и не беспокоится о сохранении каких-либо других списков или возне с функциональностью ListView.

Я не проверял это во всех сценариях... у меня это сработало. YMMV.

public class MultiSelectNoCTRLKeyListView : ListView {
  public MultiSelectNoCTRLKeyListView() {

  }

  public const int WM_LBUTTONDOWN = 0x0201;
  protected override void WndProc(ref Message m) {
    switch (m.Msg) {
      case WM_LBUTTONDOWN:
        if (!this.MultiSelect)
          break;

        int x = (m.LParam.ToInt32() & 0xffff);
        int y = (m.LParam.ToInt32() >> 16) & 0xffff;

        var hitTest = this.HitTest(x, y);
        if (hitTest != null && hitTest.Item != null)
          hitTest.Item.Selected = !hitTest.Item.Selected;

        return;
    }

    base.WndProc(ref m);
  }
}
person Matthew M.    schedule 20.04.2010

Разверните ListviewItemCollection, и вы можете установить для свойства Selected для отдельных элементов значение true. Я полагаю, что это будет эмулировать функцию «множественный выбор», которую вы пытаетесь воспроизвести. (Кроме того, как упомянул выше комментатор, убедитесь, что для свойства MultiSelect lisetview установлено значение true.)

person BKimmel    schedule 17.09.2008

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

person kamikazi    schedule 07.10.2016
comment
Это не дает ответа на вопрос. Получив достаточную репутацию, вы сможете /comment">прокомментировать любой пост; вместо этого дайте ответы которые не требуют разъяснений от спрашивающего. – Из обзора - person Jason Yost; 08.10.2016
comment
Не уверен, как это не дает ответа на вопрос. Вопрос был в том, есть ли способ заставить элемент управления listview обрабатывать все клики, как если бы они были сделаны с помощью клавиши Control? и ответ, который я дал, заключался в том, что свойство множественного выбора было множественным, а не расширенным. Это действительный, правильный и проверенный ответ на оригинальный вопрос. Ну что ж, это последний раз, когда я пытаюсь помочь другим, которые сталкиваются со старым вопросом со старым решением. - person kamikazi; 10.10.2016

Поведение Ctrl+Click реализуется браузером и имеет мало общего с фактическим элементом управления .NET. Результат, которого вы пытаетесь достичь, может быть получен с большим количеством дополнительного JavaScript - самый простой способ, вероятно, - создать элемент управления JavaScript по умолчанию, который работает таким образом, а не пытаться взломать список. Было бы это желательно? В этом случае я мог бы изучить его и вернуться к вам с решением.

person user16009    schedule 17.09.2008
comment
Ничего общего с браузером, он использует C # и WinForms! - person Ray Hayes; 17.09.2008
comment
В любом случае спасибо, Javascript нельзя использовать в среде winforms. - person Evan; 17.09.2008