【问题标题】:OwnerDrawVariable ListBox selects last item when clicking on control below itemsOwnerDrawVariable ListBox 在单击项目下方的控件时选择最后一个项目
【发布时间】:2018-01-23 06:13:44
【问题描述】:

我在我的应用程序的 ListBox 中注意到了这个问题,然后确定 MSDN 上给出的 ListBox.MeasureItem 示例也遇到了同样的问题。

当您将 ListBox 的 DrawMode 设置为 OwnerDrawVariable 以处理 MeasureItem 事件(例如绘制增加高度的列表项)时,如果您单击最后一项下方的空白区域,控件将选择最后一项。

我希望它的行为方式与 DrawMode 设置为 OwnerDrawFixed 或 Normal 时的行为方式相同,并且在用户单击项目列表下方的控件时不更改项目选择。

我尝试通过处理 MouseDown 事件来实现此行为,发现控件在触发 MouseDown 事件之前选择了最底部的项。

我想知道我是否需要继承 ListBox,或者是否有更好的方法来做到这一点。

为了查看行为,来自 MSDN 的代码示例就足够了:

https://msdn.microsoft.com/en-us/library/system.windows.forms.listbox.measureitem(v=vs.110).aspx

【问题讨论】:

    标签: winforms


    【解决方案1】:

    您可以通过检查鼠标按下消息并在用户未单击项目时不让它处理该消息来做到这一点:

    public class ListBoxEx : ListBox {
      private const int WM_LBUTTONDOWN = 0x201;
    
      protected override void WndProc(ref Message m) {
        int lParam = m.LParam.ToInt32();
        int wParam = m.WParam.ToInt32();
        if (m.Msg == WM_LBUTTONDOWN) {
          Point clickedPt = new Point();
          clickedPt.X = lParam & 0x0000FFFF;
          clickedPt.Y = lParam >> 16;
          bool lineOK = false;
          for (int i = 0; i < Items.Count; i++) {
            if (GetItemRectangle(i).Contains(clickedPt)) {
              lineOK = true;
            }
          }
          if (!lineOK) {
            return;
          }        
        }
        base.WndProc(ref m);
      }
    }
    

    【讨论】:

    • 这将是在整个应用程序中使这种行为保持一致的更可靠的方法——创建一个这样的用户控件来代替标准的列表框。非常好!
    • 如果您想要一个ListBox,您可以使用这种“直接开箱即用”的行为拖放到表单上,这是首选方法。
    【解决方案2】:

    这是 Win32 控件中的内置行为。本质上,当您单击 ListBox 时,会调用一个名为 IndexFromPoint 的方法(您可以通过控件实例自己调用此方法)来确定所选项目的索引。

    我尝试完全控制该过程,但我找不到任何证据表明即使在使用DrawMode.OwnerDrawVariable 控件时,您也可以区分单击实际项目之间的区别。

    因此,我相信你无法控制它。但你可能会避开它:

    private void ListBox1_MouseDown(object sender, MouseEventArgs e)
    {
        for (Int32 i = 0; i < ListBox1.Items.Count; i++)
        {
            var rect = ListBox1.GetItemRectangle(i);
            if (rect.Contains(e.Location))
                return;
        }
        ListBox1.SelectedIndex = -1;
    }
    

    如您所见,在此事件触发时,该项目已被选中。这只是试图以足够快的速度取消选择它以至于不被注意到。如果 ListBox 中有很多项目,您可能会注意到它在闪烁。

    这可能是你能做的最好的了。

    【讨论】:

    • 谢谢!这对我有用。这是相当聪明的,只是试图撤消不良行为。我试图防止它,但由于您所说的原因,这并没有那么好 - 行为似乎已融入控件。我试过了,效果很好!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-02
    • 1970-01-01
    • 2022-01-09
    • 1970-01-01
    • 2019-08-18
    相关资源
    最近更新 更多