代码之家  ›  专栏  ›  技术社区  ›  Mike Spross Alex Martelli

在WinForms列表框的每个项目中绘制“子”控件(如按钮)的最佳方法是什么?

  •  1
  • Mike Spross Alex Martelli  · 技术社区  · 17 年前

    ListBox 列表框 列表框 .

    列表框 有些封装,我试图通过覆盖来显示按钮 ListBox.OnDrawItem 方法(即按钮在概念上是列表框项的一部分),但我无法使其正常工作。

    列表框 当用户选择列表中的项目时 OnDrawItem 列表框 当滚动时,所选项目会离开屏幕,按钮仍会被绘制到其先前的位置,因此它会绘制在错误项目的顶部。我猜这是因为如果所选项目在屏幕外,Windows不会尝试重新绘制它,因此不会调用重新定位代码。

    以下是我现在拥有的精简版本:

    public partial class EventListBox : ListBox
    {
        private Button _importButton;
    
        public EventListBox()
        {
            InitializeComponent();
            this.DrawMode = DrawMode.OwnerDrawVariable;
    
        // set up the button that will appear next to the currently-selected item
            _importButton = new Button();
            _importButton.Text = "Import...";
            _importButton.AutoSize = true;
            _importButton.Visible = false;
    
        // Add the button as a child control of this ListBox
            Controls.Add(_importButton);
        }
    
    
        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            if (this.Items.Count > 0)
            {
                e.DrawBackground();
    
        // draw item here (omitted)
    
        // if drawing the selected item, re-position the "Import" button so that appears
        // inside the current item, and make it visible if it is hidden.
        // These checks prevent the resulting repaint that will occur from causing an infinite loop
    
        // The problem seems to be that if the ListBox is scrolled such that the selected item
        // moves off-screen , this code won't run, because it won't repaint the selected item anymore...
        // This means the button will be painted in its previous position.
    
        // The real question is: Is there a better way to approach the whole notion of
        // rendering buttons within ListBox items?
    
                if (e.State & DrawItemState.Selected == DrawItemState.Selected)
                {
                    _importButton.Top = e.Bounds.Bottom - _importButton.Height - 20;
                    _importButton.Left = e.Bounds.Left;
                    if(!_importButton.Visible) _importButton.Visible = true;
                }
            }
    
            base.OnDrawItem(e);
        }
    
        protected override void OnMeasureItem(MeasureItemEventArgs e)
        {
            base.OnMeasureItem(e);
            e.ItemHeight = 100; //hard-coded for now...
        }
    
    }
    

    我宁愿为中的每个项目创建一个单独的按钮 列表框 列表框 我可以覆盖的方法,并且不能重新分配 ListBox.Items 属性到自定义集合对象,因为 列表框。物品

    我目前的想法是,在添加新项目时创建一个新按钮是最有意义的 列表框 ,并在删除项目时删除按钮。

    以下是我提出的一些可能的解决方案,但是否有更好的实现方式?

    • 我可以创建自己的 AddItem RemoveItem 方法直接在我的派生 列表框 删除项目 然而,对我来说,这是一个丑陋的黑客行为,因为它迫使我调用这些特殊的添加/删除方法,而不仅仅是使用 列表框。物品 .
    • 我可以在中手动绘制一个新按钮 OnDrawItem System.Windows.Forms.ButtonRenderer ButtonRenderer
    • OnDrawItem 如果正在绘制的项目尚未关联按钮,我可以创建一个新按钮(我可以用 Dictionary<Item, Button> 列表框 列表框 重新绘制(后退!)。

    那么,是否有更好的方法将可点击按钮包含在 列表框 ?这显然是以前做过的,但我在谷歌上找不到任何有用的东西。我见过的唯一一个在 列表框 是WPF示例,但我正在寻找如何使用WinForms实现这一点。

    1 回复  |  直到 12 年前
        1
  •  0
  •   Mike Spross Alex Martelli    17 年前

    我找到了一个可行的解决方案,并最终暂时保留了单键方法。我会在这里发布我的解决方法,但如果有人对我最初的问题有更优雅的答案,不要犹豫发布。

    我的小顿悟

    经过更多的实验,似乎只有在使用鼠标滚轮滚动时才会出现按钮在滚动时无法正确重新定位的问题 ListBox 。以“正常”方式滚动似乎并不能再现这种行为,但由于我不能100%确定,我在我的解决方法中也包含了一个正常滚动的修复程序。

    我丑陋的变通方法

    由于我知道鼠标滚轮(以及一般的滚动)似乎是问题的核心,我决定让我的 列表框 每当 列表框 滚动或每当移动鼠标滚轮时。这确实会产生一些难看的闪烁,我想消除这种闪烁,但我现在可以忍受这种结果。

    我在推导中添加了以下方法 EventListBox 类别:

        protected override void WndProc(ref Message m)
        {
            const int WM_VSCROLL = 277;
    
            if (m.Msg == WM_VSCROLL)
            {
                this.Invalidate();
            }
    
            base.WndProc(ref m);
        }
    
        protected override void OnMouseWheel(MouseEventArgs e)
        {
            this.Invalidate();
            base.OnMouseWheel(e);
        }
    

    我有点惊讶 列表框 ScrollableControl 没有什么像 OnScroll 我可以重写的方法,所以我通过重写来进行滚动检查 WndProc 方法。鼠标滚轮检测更简单,因为已经有一种方法可以覆盖。

    就像我说的,这并不理想,因为每次滚动列表时都会使列表框无效,从而导致闪烁,我怀疑当列表框包含大量项目时,性能会显著下降。

    我还不会接受这个答案,因为我很好奇是否有人有更好的解决方案。

    推荐文章