WinForm列表框选择的项目垂直居中并在高度上扩展

时间:2013-12-17 13:29:01

标签: c# winforms

我在winforms应用程序中使用自定义列表框来保存一长串文本。我使用自定义ListBox使selectedItem的高度比其他项目更大。列表框的代码:

public class CustomListBox : System.Windows.Forms.ListBox
{
    int thisIndex = -1;

    public CustomListBox()
    {
        this.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
        this.SelectionMode = System.Windows.Forms.SelectionMode.One;

    }
    protected override void OnDrawItem(DrawItemEventArgs e)
    {
        if (this.Items.Count > 0)
        {
            if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
                e.Graphics.FillRectangle(SystemBrushes.Highlight, e.Bounds);
            else
                e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
            Font myFont = new Font("Microsoft Sans Serif", 12f, FontStyle.Bold);

            object item = this.Items[e.Index];
            e.DrawFocusRectangle();
            Brush brush = new SolidBrush(e.ForeColor);
            SizeF size = e.Graphics.MeasureString(item.ToString(), e.Font);
            e.Graphics.DrawString(this.Items[e.Index].ToString(), myFont, Brushes.Black, e.Bounds.Left + (e.Bounds.Width / 2 - size.Width / 2), e.Bounds.Top + (e.Bounds.Height / 2 - size.Height / 2));
            base.OnDrawItem(e);

        }
    }

    protected override void OnSelectedIndexChanged(EventArgs e)
    {
        base.OnSelectedIndexChanged(e);
        thisIndex = this.SelectedIndex;
        this.RecreateHandle();
    }

    protected override void OnMeasureItem(MeasureItemEventArgs e)
    {
        if (e.Index > -1)
        {
            if (e.Index == thisIndex)
            {
                System.Diagnostics.Trace.WriteLine("HELLOooooooo");
                e.ItemHeight = 70;
            }
            else
                e.ItemHeight = 45;
        }
        base.OnMeasureItem(e);
    }
}

代码可以解决问题。我想要列表框中的其他功能,当我按向下箭头键在列表中导航时,我希望列表框的选择项从顶部开始,移动到屏幕中心并保持在列表底部在屏幕上可见,然后转到最后一项。即,除了列表的开头和结尾之外,选定项目仍然在屏幕上居中。

我可以使默认列表框以这种方式更改listbox.TopIndex值。但是,使用customList OnSelectedIndexChanged()覆盖,滚动感觉有弹性。

关于如何在列表框中同时对齐alldtem和扩展selectedItem的任何指示?

2 个答案:

答案 0 :(得分:1)

您可以使用以下内容:

TopIndex = Math.Max(0, SelectedIndex-VisibleItems/2);

其中VisibleItems是列表中一次可见的项目数量。

答案 1 :(得分:1)

通常,如果您想要更改某些特定项目的高度固定,您可以处理事件ListBox.MeasureItem,但是您的要求显示您要更改所选项目的高度这在运行时非常动态。我已经尝试过这个代码而且效果很好,特别是如果使用Invalidate()更新listBox,会有一个明显的闪烁,但通过更仔细地计算需要更新的区域(并将其传递给Invalidate方法),我们可以将闪烁减少到接近零(平滑)。现在回到主要问题,关键是我们可以将消息LB_SETITEMHEIGHT发送到listBox来更改项目的高度。实际上winforms listBox不支持此功能,它只支持使用ItemHeight属性更改所有项的高度,并且此属性仅在DrawMode时才有意义不是Normal。设置项目高度后,我们必须Invalidate相应地更新ListBox,如前所述,我们应该计算需要更新的区域以防止闪烁,否则只需调用{{1}并且有一点闪烁。现在是你的代码:

listBox.Invalidate()

我不确定您的//your form constructor public Form1(){ InitializeComponent(); listBox1.DrawMode = DrawMode.OwnerDrawVariable; listBox1.ItemHeight = 18;//setting this to change the height of all items listBoxWndProc = typeof(Control).GetMethod("WndProc", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); //the initial selected index is 0 SetItemHeight(0, selectedItemHeight); listBox1.SelectedIndexChanged += listBox1_SelectedIndexChanged; } int selectedItemHeight = 25; int lastSelectedIndex; private System.Reflection.MethodInfo listBoxWndProc; private void SetItemHeight(int index, int height) { var h = Math.Min(255, height);//the maximum height is 255 //LB_SETITEMHEIGHT = 0x1a0 Message msg = Message.Create(listBox1.Handle, 0x1a0, (IntPtr)index, (IntPtr)h); listBoxWndProc.Invoke(listBox1, new object[] { msg }); } //handle the SelectedIndexChanged to update the selected item height private void listBox1_SelectedIndexChanged(object sender, EventArgs e){ //Reset the height of the last selected item SetItemHeight(lastSelectedIndex, listBox1.ItemHeight); int minIndex = Math.Min(lastSelectedIndex, listBox1.SelectedIndex); int maxIndex = Math.Max(lastSelectedIndex, listBox1.SelectedIndex); lastSelectedIndex = listBox1.SelectedIndex; SetItemHeight(lastSelectedIndex, selectedItemHeight); var rect1 = listBox1.GetItemRectangle(minIndex); var rect2 = listBox1.GetItemRectangle(maxIndex); listBox1.Invalidate(new Rectangle(rect1.X, rect1.Y, rect1.Width, rect2.Bottom - rect1.Top)); } 事件处理程序是否与上面的代码一起使用,如果没有,您可以尝试使用以下代码(已测试):

DrawItem

enter image description here

请注意,我谈到了发送消息private void listBox1_DrawItem(object sender, DrawItemEventArgs e){ e.DrawBackground(); if ((e.State & DrawItemState.Focus) == DrawItemState.Focus) e.DrawFocusRectangle(); //determine the font, if the item is selected, choose a large font size //I set it to 15, you can set it yourself accordingly to the selectedItemHeight bool selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected; var font = selected ? new Font(e.Font.FontFamily, 15, e.Font.Style) : e.Font; var color = selected ? SystemColors.HighlightText : e.ForeColor; //Draw the string, you can also provide some StringFormat to align text, ... using(var brush = new SolidBrush(color)){ e.Graphics.DrawString(listBox1.Items[e.Index].ToString(), font, brush, e.Bounds); } } ,但我使用反射来调用LB_SETITEMHEIGHT而不是使用P / Invoke来调用WndProc win32 api。