我在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的任何指示?
答案 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
请注意,我谈到了发送消息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。