[original] [open source] SunnyUI.Net Development log: ListBox add follow mouse over highlight

Keywords: C# github

SunnyUI.Net Based on C ා net WinForm open source control library, tool class library, extended class library and multi page development framework

 

  SunnyUI.Net Development log: ListBox add follow mouse over highlight

 

In QQ group, cunmu said that when ListBox mouse moves, the current line needs focus. I think about it. It's not hard to achieve it

Just redraw the Item when the mouse moves. Besides, the selected Item has changed its color.

See UIListBox Code:

protected override void OnDrawItem(DrawItemEventArgs e)
        {
            base.OnDrawItem(e);
            BeforeDrawItem?.Invoke(this, Items, e);
            if (Items.Count == 0)
            {
                return;
            }

            e.DrawBackground();

            if (e.Index < 0 || e.Index >= Items.Count)
            {
                return;
            }

            StringFormat sStringFormat = new StringFormat();
            sStringFormat.LineAlignment = StringAlignment.Center;

            Color backColor = (e.State & DrawItemState.Selected) == DrawItemState.Selected ? ItemSelectBackColor : BackColor;
            Color foreColor = (e.State & DrawItemState.Selected) == DrawItemState.Selected ? ItemSelectForeColor : ForeColor;

            Rectangle rect = new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width - 1, e.Bounds.Height - 1);
            e.Graphics.FillRectangle(BackColor, e.Bounds);
            e.Graphics.FillRoundRectangle(backColor, rect, 5);
            e.Graphics.DrawString(Items[e.Index].ToString(), e.Font, foreColor, e.Bounds, sStringFormat);
        }

Look: (e.state& DrawItemState.Selected ) == DrawItemState.Selected Select row status

See e.State has e.State== DrawItemState.HotLight , is not it highlighted? Then start to roll up the code and add the status judgment

Run

Dizziness... It hasn't changed. This HotLight doesn't work. OK, ask Du Niang...

Climb over mountains and rivers...

Find this: https://www.jb51.cc/csharp/101121.html

It refers to:

I use OwnerDrawFixed as a DrawMode in my WinForms application to customize the ListBox control. When a user hovers over a ListBoxItem, I want to redraw the background of the ListBoxItem (or do something else), that is, in MouseMove DrawItemState.HotLight Never for listbox, so I want to know how to simulate it and how to solve this problem

DrawItemState.HotLight Never for ListBox, always... Why is it so far...

Keep looking down:

resolvent

I spent two years finding the answer for you, but here is:

DrawItemState.HotLight Applies only to menus drawn by the owner, not to ListBox. For listbox, you must track items yourself:

public partial class Form1 : Form
{
  private int _MouseIndex = -1;
 
  public Form1()
  { InitializeComponent(); }
 
  private void listBox1_DrawItem(object sender,DrawItemEventArgs e)
  {
    Brush textBrush = SystemBrushes.WindowText;
 
    if (e.Index > -1)
    {
      if (e.Index == _MouseIndex)
      {
        e.Graphics.FillRectangle(SystemBrushes.HotTrack,e.Bounds);
        textBrush = SystemBrushes.HighlightText;
      }
      else
      {
        if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
        {
          e.Graphics.FillRectangle(SystemBrushes.Highlight,e.Bounds);
          textBrush = SystemBrushes.HighlightText;
        }
        else
          e.Graphics.FillRectangle(SystemBrushes.Window,e.Bounds);
      }
      e.Graphics.DrawString(listBox1.Items[e.Index].ToString(),e.Font,textBrush,e.Bounds.Left + 2,e.Bounds.Top);
    }
  }
 
  private void listBox1_MouseMove(object sender,MouseEventArgs e)
  {
    int index = listBox1.IndexFromPoint(e.Location);
    if (index != _MouseIndex)
    {
      _MouseIndex = index;
      listBox1.Invalidate();
    }
  }
 
  private void listBox1_MouseLeave(object sender,EventArgs e)
  {
    if (_MouseIndex > -1)
    {
      _MouseIndex = -1;
      listBox1.Invalidate();
    }
  }
}

Brothers, what they have spent two years to solve should be useful. Continue to look for it and find another one in a foreign language:

https://stackoverflow.com/questions/1316027/listbox-drawitem-hotlight-state-in-the-ownerdraw-mode

It took me only two years to find the answer for you, but here it is:

The DrawItemState.HotLight only applies to owner drawn menus, not the listbox.

For the ListBox, you have to keep track of the item yourself:

Look, it's also two years. It's estimated that the above Chinese translation came from this.

Besides these two, I haven't found them.

Continue to roll up the code, it really works. However, there is still a problem. The mouse slides fast and the ListBox blinks badly.

Parse code listBox1.Invalidate(); this is refreshing all. Mouse over is also related to this selection and the last selection.

Just swipe these two items. If you have an idea, you can send them three times in a row:

        private int lastIndex = -1;
        private int mouseIndex = -1;

        [Browsable(false)]
        public int MouseIndex
        {
            get => mouseIndex;
            set
            {
                if (mouseIndex != value)
                {
                    if (lastIndex >= 0 && lastIndex != SelectedIndex)
                    {
                        OnDrawItem(new DrawItemEventArgs(this.CreateGraphics(), Font, GetItemRectangle(lastIndex), lastIndex, DrawItemState.Grayed));
                    }

                    mouseIndex = value;
                    if (mouseIndex >= 0 && mouseIndex != SelectedIndex)
                    {
                        OnDrawItem(new DrawItemEventArgs(this.CreateGraphics(), Font, GetItemRectangle(value), value, DrawItemState.HotLight));
                    }

                    lastIndex = mouseIndex;
                }
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            MouseIndex = IndexFromPoint(e.Location);
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            MouseIndex = -1;
        }

Where new drawitemeventargs( this.CreateGraphics (), Font, GetItemRectangle(value), value, DrawItemState.HotLight )

The first parameter, Graphics, was also found for a while. Later, see this:

https://www.cnblogs.com/yuanyeguhong/archive/2013/09/20/3330606.html

 

In fact, it is the DrawString method of the Graphics object, and how does the Graphics in the parameter e come from.
We then analyze the DrawItemEventArgs class. Since it is a package of listBox1 properties,
So I guess the Graphics object is from listbox1.creategraphics.
OK, now we can customize the function of redrawing listbox, which can be called at will!

OK, so far, we have found the answers to all the questions, and then we need to straighten out our thinking and put the logic in order. Roll code, debug, OK!!!

The main codes are as follows:

protected override void OnDrawItem(DrawItemEventArgs e)
        {
            base.OnDrawItem(e);

            BeforeDrawItem?.Invoke(this, Items, e);
            if (Items.Count == 0)
            {
                return;
            }

            bool otherState = e.State == DrawItemState.Grayed || e.State == DrawItemState.HotLight;
            if (!otherState)
            {
                e.DrawBackground();
            }

            if (e.Index < 0 || e.Index >= Items.Count)
            {
                return;
            }

            StringFormat sStringFormat = new StringFormat();
            sStringFormat.LineAlignment = StringAlignment.Center;

            bool isSelected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
            Color backColor = isSelected ? ItemSelectBackColor : BackColor;
            Color foreColor = isSelected ? ItemSelectForeColor : ForeColor;

            Rectangle rect = new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width - 1, e.Bounds.Height - 1);
            if (!otherState)
            {
                e.Graphics.FillRectangle(BackColor, e.Bounds);
                e.Graphics.FillRoundRectangle(backColor, rect, 5);
                e.Graphics.DrawString(Items[e.Index].ToString(), e.Font, foreColor, e.Bounds, sStringFormat);
            }
            else
            {
                if (e.State == DrawItemState.Grayed)
                {
                    backColor = BackColor;
                    foreColor = ForeColor;
                }

                if (e.State == DrawItemState.HotLight)
                {
                    backColor = HoverColor;
                    foreColor = ForeColor;
                }

                e.Graphics.FillRectangle(BackColor, e.Bounds);
                e.Graphics.FillRoundRectangle(backColor, rect, 5);
                e.Graphics.DrawString(Items[e.Index].ToString(), e.Font, foreColor, e.Bounds, sStringFormat);
            }

            AfterDrawItem?.Invoke(this, Items, e);
        }

Look, DrawItemState.HotLight We have achieved it, DrawItemState.Grayed I am in a state of random choice, different from others.

Want to see all the code, see my open source project, https://gitee.com/yhuse/SunnyUI Hey, sir, don't go away. Order a Star first.

 

Original article, reprint please keep the link Sunny's blog

Posted by explore on Thu, 21 May 2020 08:57:59 -0700