懸停在下拉列表上時觸發事件
介紹 你曾經想要從你的組合盒中得到實時反饋嗎?你是否曾經想讓它能夠讓你知道使用者在點選前在下拉列表中懸停的是哪個專案?在本文中,我將展示如何繼承一個自定義組合框控制元件,當滑鼠懸停在下拉列表中的項上時,該控制元件將觸發事件。 背景 我有一個組合框的請求,它允許你更新一些東西,取決於在它的下拉列表中懸停的專案。“簡單!”我想。然後,我坐下來做這件事,發現使用標準的。net控制元件,它比我預期的要困難一些。這是我的勞動成果……如果你知道一個更簡單的方法,我很想聽聽! 直接進入程式碼! 您自己測試這一點的最簡單方法是執行示例應用程式。您應該看到,此組合框與標準組合框之間的唯一區別是添加了一個新事件Hover。這是一個非常有用的事件,在使用者真正選擇任何新內容之前,它可以讓你知道使用者何時將滑鼠懸停在下拉列表中的一個新專案上。有點像組合框的預覽。 在程式碼本身中,您將注意到我們定義了自己的自定義事件引數類和一個用於處理事件的委託:
public class HoverEventArgs : EventArgs { private int _itemIndex = 0; public int itemIndex { get { return _itemIndex; } set { _itemIndex = value; } } } ... public delegate void HoverEventHandler(object sender, HoverEventArgs e);
這只是在標準EventArgs中添加了一個名為“itemIndex”的新屬性,事件使用該屬性來通知組合框中當前正在懸浮的項。委託接受傳送方物件和由我們的自定義HoverEventArgs類提供的事件引數。 第一個真正感興趣的地方是下面的程式碼部分,以及我們必須使用System.Runtime的原因。InteropServices assembly.
// Import the GetScrollInfo function from user32.dll [DllImport("user32.dll", SetLastError = true)] private staticextern int GetScrollInfo(IntPtr hWnd, int n, ref ScrollInfoStruct lpScrollInfo); // Win32 constants private const int SB_VERT = 1; private const int SIF_TRACKPOS = 0x10; private const int SIF_RANGE = 0x1; private const int SIF_POS = 0x4; private const int SIF_PAGE = 0x2; private const int SIF_ALL = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS; private const int SCROLLBAR_WIDTH = 17; private const int LISTBOX_YOFFSET = 21; // Return structure for the GetScrollInfo method [StructLayout(LayoutKind.Sequential)] private struct ScrollInfoStruct { public int cbSize; public int fMask; public int nMin; public int nMax; public int nPage; public int nPos; public int nTrackPos; }
我們需要在user32.dll中的GetScrollInfo函式來讓我們知道當滾動條被用來移動它時,組合框的下拉列表部分發生了什麼。要知道滑鼠在螢幕上突出顯示的是哪一項是相當簡單的,但是要知道我們在列表上向上或向下滾動了多遠則完全是另一回事,我們必須求助於這種互操作解決方案。 一旦我們完成了對所有這些的定義,我們就可以開始這個控制元件的主要部分:覆蓋protected override void WndProc(ref Message msg)方法。 檢查後看到我們實際上改變了列表中的位置(即味精)。Msg等於308),我們必須繼續並捕獲滑鼠位置,這樣我們可以確定被滑鼠懸停在下拉列表上的專案。您可以通過使用游標來實現這一點。直接定位功能,但給你螢幕座標。執行以下操作要容易得多,以便使要點相對於組合框控制元件:
Point LocalMousePosition = this.PointToClient(Cursor.Position); xPos = LocalMousePosition.X; yPos = LocalMousePosition.Y - this.Size.Height - 1;
一旦我們有了滑鼠位置,我們就會計算在列表中從基於零的角度突出顯示的專案,並將ComboBox考慮在內。專案高度和組合箱。尺寸。高度,這樣調整控制元件中元素的大小不會破壞程式碼。 好的,這裡是被覆蓋的WndProc方法的所有榮耀:
//Capture messages coming to our combobox protected override void WndProc(ref Message msg) { //This message code indicates the value in the list is changing //32 is for DropDownStyle == Simple if ((msg.Msg == 308) || (msg.Msg == 32)) { int onScreenIndex = 0; // Get the mouse position relative to this control Point LocalMousePosition = this.PointToClient(Cursor.Position); xPos = LocalMousePosition.X; if (this.DropDownStyle == ComboBoxStyle.Simple) { yPos = LocalMousePosition.Y - (this.ItemHeight + 10); } else { yPos = LocalMousePosition.Y - this.Size.Height - 1; } // save our y position which we need to ensure the cursor is // inside the drop down list for updating purposes int oldYPos = yPos; // get the 0-based index of where the cursor is on screen // as if it were inside the listbox while (yPos >= this.ItemHeight) { yPos -= this.ItemHeight; onScreenIndex++; } //if (yPos < 0) { onScreenIndex = -1; } ScrollInfoStruct si = new ScrollInfoStruct(); si.fMask = SIF_ALL; si.cbSize = Marshal.SizeOf(si); // msg.LParam holds the hWnd to the drop down list that appears int getScrollInfoResult = 0; getScrollInfoResult = GetScrollInfo(msg.LParam, SB_VERT, ref si); // k returns 0 on error, so if there is no error add the current // track position of the scrollbar to our index if (getScrollInfoResult > 0) { onScreenIndex += si.nTrackPos; if (this.DropDownStyle == ComboBoxStyle.Simple) { simpleOffset = si.nTrackPos; } } // Add our offset modifier if we're a simple combobox since we don't // continuously receive scrollbar information in this mode. // Then make sure the item we're previewing is actually on screen. if (this.DropDownStyle == ComboBoxStyle.Simple) { onScreenIndex += simpleOffset; if (onScreenIndex > ((this.DropDownHeight / this.ItemHeight) + simpleOffset)) { onScreenIndex = ((this.DropDownHeight / this.ItemHeight) + simpleOffset - 1); } } // Check we're actually inside the drop down window that appears and // not just over its scrollbar before we actually try to update anything // then if we are raise the Hover event for this comboBox if (!(xPos > this.Width - SCROLLBAR_WIDTH || xPos < 1 || oldYPos < 0 || ((oldYPos > this.ItemHeight * this.MaxDropDownItems) && this.DropDownStyle != ComboBoxStyle.Simple))) { HoverEventArgs e = new HoverEventArgs(); e.itemIndex = (onScreenIndex > this.Items.Count - 1) ? this.Items.Count - 1 : onScreenIndex; OnHover(e); // if scrollPos doesn't equal the nPos from our ScrollInfoStruct then // the mousewheel was most likely used to scroll the drop down list // while the mouse was inside it - this means we have to manually // tell the drop down to repaint itself to update where it is hovering // still posible to "outscroll" this method but it works better than // without it present if (scrollPos != si.nPos) { Cursor.Position = new Point(Cursor.Position.X + xFactor, Cursor.Position.Y); xFactor = -xFactor; } } scrollPos = si.nPos; } // Pass on our message base.WndProc(ref msg); }
我們建立一個ScrollInfoStruct, si,來儲存來自互操作呼叫的資訊。執行這個函式可以獲取滾動條的狀態,我們使用滾動條來更改onScreenIndex變數,以表示在下拉列表中懸浮的實際專案。一旦我們確定哪些專案,這是我們檢查滑鼠的範圍實際上是在下降(因為您可以滾動滑鼠滾輪內當你不控制,移動它),然後如果我們,我們傳送事件說懸停在一個新項。剩下的就是在包含此組合框的表單上接收事件,然後問題就解決了!當用戶將滑鼠懸停在自定義組合框中的新項上時,您將確切地知道。 的興趣點 pointtoclient (Cursor.Position)對我來說是一個新東西,而且是一種獲得基於滑鼠位置的控制元件的有用方法。 非常有趣的是,下拉列表視窗的控制代碼在lParam引數中傳遞給了WndProc。很難在任何地方找到正式的文件!事實上,我從來沒有…… 用滑鼠滾輪移動下拉列表的內容並沒有突出顯示一個新專案——這似乎純粹是基於滑鼠實際移動的情況,所以我必須新增一些程式碼來在發生這種情況時將滑鼠移動一個畫素。 歷史 5/29/2006 -更新工作更好與簡單的下拉式風格。 5/26/2006 -第一次提交守則。 本文轉載於:http://www.diyabc.com/frontweb/news340.html