2013-08-29 144 views
2

我有一個WinForms組合框,記得以前輸入的項目。我想要一種方法來刪除以前的條目。我重寫ComboBox的DrawItem事件以將文本與X圖標一起渲染。 X圖標只是一個正方形的圖像,我可以縮放到該項目的高度。代碼非常簡單。捕獲ComboBox項目單擊

// Enable the owner draw on the ComboBox. 
ServerComboBox.DrawMode = DrawMode.OwnerDrawFixed; 
// Handle the DrawItem event to draw the items. 
ServerComboBox.DrawItem += delegate(object cmb, DrawItemEventArgs args) 
{ 
    // Draw the default background 
    args.DrawBackground(); 

    String url = (String)ServerComboBox.Items[args.Index]; 

    // Get the bounds for the first column 
    Rectangle r1 = args.Bounds; 
    r1.Width -= r1.Height; 

    // Draw the text 
    using (SolidBrush sb = new SolidBrush(args.ForeColor)) 
    { 
     args.Graphics.DrawString(url, args.Font, sb, r1); 
    } 

    // Draw the X icon 
    Rectangle r2 = new Rectangle(r1.Width+1, r1.Y + 1, r1.Height - 2, r1.Height - 2); 
    args.Graphics.DrawImage(Project.Test.Properties.Resources.CloseIcon, r2); 
}; 

現在我的問題是如何捕獲如果X被點擊。我的第一個想法是捕獲ComboBox的MouseDown事件,並檢查DroppedDown屬性是否爲true,但只有當您單擊未展開的ComboBox時纔會觸發該事件。我如何從ComboBox的DropDown部分捕獲事件。一旦我明白了這一點,我認爲這不是一個很大的問題,搞清楚X是被點擊還是現在。

回答

0

一旦用戶點擊了組合框中的一個項目,它就成爲選定的項目。您可以從組合框對象中檢索此屬性。

+1

這肯定不是OP想要的。 –

+0

@KingKing這裏很好的挑戰。繼續,通過顯示你可以在winforms中做到這一點來證明我錯了。 –

+0

@HighCore幸運的是,我對'win32'中的'ComboBox'有一點經驗。看到我的答案,你可能想給我一個+1。 –

1

當用戶選擇或使用鼠標滾輪SelectionIndexChanged事件將觸發。如果您不想對鼠標滾輪做出反應,並且只需要點擊並選擇,則可以考慮使用SelectionChangeCommited事件。

然後您可以閱讀SelectedIndexSelectedItem屬性以獲取所選項目。

編輯:對不起,我似乎完全誤解了這個問題。我認爲你需要在組合框內捕獲ListBox鼠標,並使用Rectange.Contains手動檢查它。將回來更多的細節。

+0

什麼'ListBox'?如果你的意思是'ComboBox'的附加列表框,要獲得它的訪問權限,對''win32''很少了解並不簡單。 –

+0

@KingKing我同意你的觀點並不容易。但我也可以說不那麼難。我正在嘗試您提供的類似解決方案。似乎你的解決方案將工作。如果沒有,那就讓我試試看。 –

2

其實你的問題僅僅是一個微不足道的問題,這Win32可以解決:

public class Form1 : Form { 
    [DllImport("user32")] 
    private static extern int GetComboBoxInfo(IntPtr hwnd, out COMBOBOXINFO comboInfo); 
    struct RECT { 
    public int left, top, right, bottom; 
    } 
    struct COMBOBOXINFO { 
     public int cbSize; 
     public RECT rcItem; 
     public RECT rcButton; 
     public int stateButton; 
     public IntPtr hwndCombo; 
     public IntPtr hwndItem; 
     public IntPtr hwndList; 
    } 
    public Form1(){ 
    InitializeComponent(); 
    comboBox1.HandleCreated += (s, e) => { 
     COMBOBOXINFO combo = new COMBOBOXINFO(); 
     combo.cbSize = Marshal.SizeOf(combo); 
     GetComboBoxInfo(comboBox1.Handle, out combo); 
     hwnd = combo.hwndList; 
     init = false; 
    }; 
    } 
    bool init; 
    IntPtr hwnd; 
    NativeCombo nativeCombo = new NativeCombo(); 
    //This is to store the Rectangle info of your Icons 
    //Key: the Item index 
    //Value: the Rectangle of the Icon of the item (not the Rectangle of the item) 
    Dictionary<int, Rectangle> dict = new Dictionary<int, Rectangle>(); 
    public class NativeCombo : NativeWindow { 
     //this is custom MouseDown event to hook into later 
     public event MouseEventHandler MouseDown; 
     protected override void WndProc(ref Message m) 
     {     
      if (m.Msg == 0x201)//WM_LBUTTONDOWN = 0x201 
      {      
       int x = m.LParam.ToInt32() & 0x00ff; 
       int y = m.LParam.ToInt32() >> 16; 
       if (MouseDown != null) MouseDown(null, new MouseEventArgs(MouseButtons.Left, 1, x, y, 0));                 
      } 
      base.WndProc(ref m); 
     } 
    } 
    //DropDown event handler of your comboBox1 
    private void comboBox1_DropDown(object sender, EventArgs e) { 
     if (!init) { 
      //Register the MouseDown event handler <--- THIS is WHAT you want. 
      nativeCombo.MouseDown += comboListMouseDown; 
      nativeCombo.AssignHandle(hwnd);     
      init = true; 
     } 
    } 
    //This is the MouseDown event handler to handle the clicked icon 
    private void comboListMouseDown(object sender, MouseEventArgs e){ 
    foreach (var kv in dict) { 
     if (kv.Value.Contains(e.Location)) { 
     //Show the item index whose the corresponding icon was held down 
     MessageBox.Show(kv.Key.ToString()); 
     return; 
     } 
    } 
    } 
    //DrawItem event handler 
    private void comboBox1_DrawItem(object sender, DrawItemEventArgs e) { 
    //We have to save the Rectangle info of the item icons 
    Rectangle rect = new Rectangle(0, e.Bounds.Top, e.Bounds.Height, e.Bounds.Height); 
    dict[e.Index] = rect; 
    //Draw the icon 
    //e.Graphics.DrawImage(yourImage, rect); 
    } 
} 

一些關於會發生什麼

一個ComboBox有一個附加下拉列表,這可能是通過其Handle訪問。我們使用GetComboBoxInfo win32 api函數檢索ComboBox的某些信息,該信息保存在名爲COMBOBOXINFO的結構中。我們可以通過hwndList這個結構獲得下拉列表

在獲得hwndList後,我們可以使用自定義NativeWindow類(示例中的NativeCombo)掛鉤到其消息循環中。這允許我們容易地干涉下拉列表的消息循環。然後我們可以捕獲WM_LBUTTONDOWN消息來處理MouseDown事件。當然,這不是一個完整MouseDown事件,但它只是一個演示,在這種情況下,它足以解決問題。 WM_LBUTTONDOWN點擊點保存在LParam發送一些信息。點擊的點是在下拉列表(不在屏幕座標中)的客戶端區域的座標中計算的。我們應該注意到DrawItem事件處理程序也有e.Bounds參數,該參數也在客戶區座標(不是屏幕座標)中計算。因此它們在相同的座標系統中。我們使用Rectangle.Contains方法知道點擊的點是否包含在某個圖標的的邊界內。我們存儲所有圖標Bounds字典。因此,它包含了點擊點矩形相應重點(存儲項目指標)讓我們知道了相應項目指標,那麼我們就可以進一步處理。

+0

所有這些反應都會讓閱讀變得困難。請謹慎使用它們。當前上下文中不存在'hwnd'和'ev'。 – LarsTech

+0

@LarsTech謝謝你,當然在發佈之前我已經做了一個演示,有一些代碼我沒有複製和粘貼在這裏。現在更新。 –

+0

@KingKing除了1件事外,這個工作非常好。進入comboListMouseDown的位置似乎不正確。在我的原始文章中,該圖標被繪製到r2矩形中,該矩形應該位於客戶端座標中,以便我保存到字典中的矩形。電子定位點似乎具有正確的Y值,但X不到應有的一半。你有什麼想法我搞亂了嗎? – TFili