【问题标题】:Listview Multiple Selection列表视图多选
【发布时间】:2008-09-17 14:40:16
【问题描述】:

有什么方法可以强制列表视图控件将所有点击视为通过 Control 键完成的?

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

提前谢谢你。

【问题讨论】:

    标签: c# winforms


    【解决方案1】:

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

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

    1. 从 ListView 派生控件
    2. 为“Selected”事件添加一个处理程序。
    3. 在“OnSelected”中,维护您自己的选定项目列表。
    4. 如果新选择的项目不在您的列表中,请将其添加。如果是,请将其删除。
    5. 在代码中,选择列表中的所有项目。

    应该足够简单,无需使用控制键即可实现并感觉像多选!

    【讨论】:

    • 我自己一直在按照这些思路工作,但想在继续修复之前检查一下是否有更简单的方法。不过感谢您的回答。
    • 确保您的选定项目列表存储“内容”而不是索引。使用索引更容易,但这意味着您需要保持 ListBox.ItemsCollection 和您的列表同步。希望您的对象或字符串足够独特以保持这种关系!
    【解决方案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();
        }
    }
    

    【讨论】:

      【解决方案3】:

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

      来自 MSDN 页面:

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

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

            【讨论】:

            • 这并没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker。 - From Review
            • 不确定它如何不能提供问题的答案。问题是“有什么方法可以强制列表视图控件将所有点击视为通过 Control 键完成的?”我给出的答案是“将多选属性设置为多个,而不是扩展”。这是对原始问题的有效、正确和经过测试的答案。哦,好吧,这是我最后一次尝试帮助那些用旧解决方案解决旧问题的人。
            【解决方案7】:

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

            【讨论】:

            • 跟浏览器没关系,他用的是C#和WinForms!
            • 还是谢谢,在winforms环境下不能使用Javascript。
            猜你喜欢
            • 2016-10-17
            • 1970-01-01
            • 2016-12-31
            • 1970-01-01
            • 1970-01-01
            • 2022-11-29
            • 1970-01-01
            相关资源
            最近更新 更多