Listview多选

时间:2008-09-17 14:40:16

标签: c# winforms

有没有办法强制listview控件将所有点击视为通过Control键完成?

我需要复制使用控制键的功能(选择项目集并取消设置其选择状态),以便用户可以同时轻松选择多个项目。

提前谢谢。

7 个答案:

答案 0 :(得分:4)

即使MultiSelect设置为true,它也不是ListView控件的标准行为。

如果您想创建自己的自定义控件,则需要执行以下操作:

  1. 从ListView
  2. 派生控件
  3. 为“Selected”事件添加处理程序。
  4. 在“OnSelected”中,维护您自己的所选项目列表。
  5. 如果新选择的项目不在您的列表中,请添加它。如果是,请将其删除。
  6. 在代码中,选择列表中的所有项目。
  7. 应该足够简单,无需使用控制键即可实现多选!

答案 1 :(得分:2)

您可能还想考虑在列表视图中使用Checkboxes。这是将多选概念传达给可能不了解Ctrl + Click的普通用户的一种明显方式。

从MSDN页面:

  

CheckBoxes属性提供了一种在不使用CTRL键的情况下在ListView控件中选择多个项目的方法。根据您的应用程序,使用复选框选择项而不是标准多选方法对用户来说可能更容易。即使ListView控件的MultiSelect属性设置为false,您仍然可以显示复选框并为用户提供多种选择功能。如果您不希望选择多个项目但仍希望允许用户从列表中选择多个项目以在您的应用程序中执行操作,则此功能非常有用。

答案 2 :(得分:2)

以下是我使用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);
  }
}

答案 3 :(得分:2)

这是一个完整的解决方案,是上述Matthew M.提供的解决方案的修改。

它提供了改进以及一些附加功能。

改进:

  • 左键单击控件可将焦点对准控件。
  • 鼠标右键单击行为一致(单选)

添加了功能:

  • 该控件具有一个属性(MultiSelectionLimit),允许您限制一次可以选择的项目数。

在我第一次发帖后,我发现了代码的一个小问题。清除多个选择将导致多次调用ItemSelectionChanged事件 我找不到用当前继承来避免这种情况的方法,所以我采用了一个解决方案,其中bool属性SelectionsBeingCleared将为真,直到所有选定的项目都被取消选择。

这样,对该属性的简单调用可以避免更新效果,直到所有多个选择都被清除。

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

答案 4 :(得分:0)

向下钻取ListviewItemCollection,您可以将各个项目的Selected属性设置为true。我相信,这将模仿您尝试重现的“多选”功能。 (另外,正如上面提到的那样,请确保将lisetview的MultiSelect属性设置为true。)

答案 5 :(得分:0)

以防万一其他人搜索并找到此文章,已接受的解决方案不再有效。 (事实上​​我不确定它是不是)。为了做你想要的(选择不带修饰键的多个),只需将列表视图选择类型设置为多个,而不是扩展。单击时多个选择一个项目,并且扩展需要首先按下修改键。

答案 6 :(得分:-2)

Ctrl + Click行为与浏览器一样,与实际的.NET Control几乎没有关系。您尝试实现的结果可以通过大量额外的JavaScript获得 - 最简单的方法可能是从默认构建一个以这种方式工作的JavaScript控件,而不是试图破解listview。这会是可取的吗?在那种情况下,我可以调查一下,并通过解决方案回复您。