2013-12-17 67 views
-1

我在我的winforms應用程序中使用自定義列表框來存放一長串文本。我正在使用自定義列表框來使selectedItem的高度比其他項目大。列表框的代碼:WinForm列表框選定的項目垂直居中並展開高度

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); 
    } 
} 

代碼的確有用。我希望列表框中有另一個功能,當我按向下箭頭鍵瀏覽列表時,我希望列表框的selecteditem從頂部開始,移動到屏幕的中心並保持到列表的底部在屏幕上可見,然後轉到最後一項。即所選擇的項目保持居中在屏幕上而不是列表的最開始和結束。

我可以使默認列表框以這種方式更改listbox.TopIndex值。但是,使用customList OnSelectedIndexChanged()重寫,滾動感覺有彈性。

關於如何在同一時間在列表框中居中對齊和擴展selectedItem的任何說明?

+1

您可以將[TopIndex](http://msdn.microsoft.com/zh-cn/library/system.windows.forms.listbox.topindex(v = vs.110).aspx)設置爲計算值當你移動選定的索引。 Math.Min(0,SelectedIndex-VisibleItems/2)使topIndex始終爲零。 – Johnbot

回答

1

一般來說,如果你想改變的一些具體項目固定的高度,就可以處理該事件ListBox.MeasureItem,但是你的要求表明,要改變它在運行時非常有活力的所選項目的高度。我試過這段代碼,它的工作效果很好,尤其是,如果使用Invalidate()來更新列表框,有一個明顯的閃爍,但通過更仔細地計算需要更新的區域(並將其傳遞給Invalidate方法),我們可以將閃爍降低到接近零(平滑)。現在回到主要問題,重點是我們可以將消息LB_SETITEMHEIGHT發送到列表框來更改項目的高度。實際上,WinForms列表框不支持此功能,但它僅支持使用ItemHeight屬性更改所有項目的高度,並且此屬性僅在DrawMode不是Normal時纔有意義。在設置物品高度後,我們必須InvalidateListBox進行相應的更新,正如我前面所說的,我們應該計算需要更新的區域以防止閃爍,否則只需調用listBox.Invalidate()並且有一點閃爍。現在是你的代碼:我不知道,如果你的DrawItem事件處理我的代碼一起工作上面,如果不是,你可以試試下面的代碼,而不是(測試)

//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)); 
} 

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); 
    } 
} 

enter image description here

需要注意的是,我談到發送消息LB_SETITEMHEIGHT,但我用反射調用WndProc而不是使用P/Invoke來調用SendMessage的Win32 API。

1

您可以使用類似:

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

哪裏VisibleItems是在列表中的某個時間可見項目的數量。

+0

Math.Min(0,SelectedIndex-VisibleItems/2)使topIndex始終爲零。無論如何,我理解這個想法和方法在默認列表框中工作良好,但出於某種原因,不在我的自定義列表框中。至少,我把這個問題歸結爲:-)! – user1386173

+1

將其更改爲最大(doh) – Johnbot

+0

該答案適用於默認列表。不過,我想要一個帶有selectedItem的列表框,這個列表框的高度已經擴展了,爲此我使用了一個customListbox。任何想法如何我可以在一起的功能?我更新了我的問題。謝謝 – user1386173