我試圖通過調用「ToolTip.Show(String,IWin32Window,Point)」來顯示工具提示,但我想像做Windows資源管理器那樣做 - 在光標的左下角顯示工具提示。獲取當前光標左下角的位置,以便工具提示可以正確顯示
我可以通過「MousePosition」獲得鼠標位置,但我怎麼能得到它的左下角位置?
感謝,
我試圖通過調用「ToolTip.Show(String,IWin32Window,Point)」來顯示工具提示,但我想像做Windows資源管理器那樣做 - 在光標的左下角顯示工具提示。獲取當前光標左下角的位置,以便工具提示可以正確顯示
我可以通過「MousePosition」獲得鼠標位置,但我怎麼能得到它的左下角位置?
感謝,
如果沒有人想出了一個更好的答案,你可以試試這個:
toolTip1.Show("Am I where you want me to be?", this, this.PointToClient(MousePosition).X,
this.PointToClient(MousePosition).Y + Cursor.Size.Height * 2);
通過與X/Y參數打調整文字的定位。它適用於我的機器,但我不確定它在不同設置下的外觀。
工具提示有趣的提示:把這一行放在你的Form的MouseMove事件中。
我認爲資源管理器將工具提示放在光標的熱點下,因此您不必更正X位置。這很好看:
private void panel1_MouseClick(object sender, MouseEventArgs e) {
int x = e.X;
int y = e.Y + Cursor.Current.Size.Height - Cursor.Current.HotSpot.Y;
toolTip1.Show("test", panel1, x, y);
}
做到這一點的唯一方法是掃描光標MASK並找到光標面具的最後一組像素和光標ÿ熱點之間的距離,我今天不得不做的這件事,因此繼承人代碼:
#define useUnsafe
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System;
using System.Windows.Forms;
namespace Utils
{
/// <summary>
/// Provides extension methods for the Cursor class
/// </summary>
/// <remarks>By Aaron Murgatroyd</remarks>
public static class CursorExtensionMethods
{
#region API Functions
/// <summary>
/// Contains the icon information for a Windows API icon
/// </summary>
private struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
/// <summary>
/// Gets the icon information for a Windows API icon
/// </summary>
/// <param name="hIcon">The icon to get the info for</param>
/// <param name="pIconInfo">The object to receive the info</param>
/// <returns>True on success, false on failure</returns>
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
[DllImport("gdi32.dll")]
static extern bool DeleteObject(IntPtr hObject);
#endregion
#region Private Static Methods
/// <summary>
/// Scans bits in bitmap data for a set or unset bit
/// </summary>
/// <param name="byteData">The pointer to the first byte of the first scanline</param>
/// <param name="start">The vertical position to start the scan</param>
/// <param name="lineInc">The number of bytes to move per line</param>
/// <param name="maxLines">The number of lines to scan</param>
/// <param name="set">True to scan for set bits, false to scan for unset bits</param>
/// <param name="fromBottom">True to scan from the bottom of the bitmap, false to scan from the top</param>
/// <returns>The number of lines scanned before a bit was found, or -1 if none found before reaching max lines</returns>
#if useUnsafe
private static unsafe int ScanBits(IntPtr byteData, int start, int lineInc, int maxLines, bool set, bool fromBottom)
#else
private static int ScanBits(IntPtr byteData, int start, int lineInc, int maxLines, bool set, bool fromBottom)
#endif
{
// Calculate the starting byte of the first scanline
#if useUnsafe
byte* lbLine = ((byte*)byteData) + (start * lineInc);
#else
int lbLine = ((int)(byteData) + (start * lineInc));
#endif
int liLine = 0;
// Use lineInc to determines bytes per line
int liBytesPerLine = (lineInc < 0 ? -lineInc : lineInc);
// If we want to search in reverse order
if (fromBottom)
{
// Move to the START of the line
lbLine += lineInc * (maxLines - 1);
// Negate the line increment
lineInc = -lineInc;
}
while (maxLines > 0)
{
// Setup the line scan
#if useUnsafe
byte* lbData = lbLine;
#else
int lbData = lbLine;
#endif
int liByte = liBytesPerLine;
// For each byte in the line
while (liByte > 0)
{
#if !useUnsafe
byte lbByte = Marshal.ReadByte((IntPtr)lbData);
#endif
// If we want set bits, and a bit is set
#if useUnsafe
if (set && *lbData != 0)
#else
if (set && lbByte != 0)
#endif
// Return the line number
return liLine;
else
// If we want unset bits and any bits arent set
#if useUnsafe
if (!set && *lbData != byte.MaxValue)
#else
if (!set && lbByte != byte.MaxValue)
#endif
// Return the line number
return liLine;
// Next byte for scan line
liByte--;
lbData++;
}
// Next scan line
liLine++;
maxLines--;
lbLine += lineInc;
}
// If all lines were scanned, return -1
if (maxLines == 0)
return -1;
else
// Return number of lines scanned
return liLine;
}
#endregion
#region Public Static Methods
/// <summary>
/// Gets the number of pixels between the Y hotspot
/// and the last physical line of a cursor
/// </summary>
/// <param name="cursor">The cursor to scan</param>
/// <returns>
/// The number of lines between the Y hotspot
/// and the last physical line of the cursor
/// </returns>
public static int GetBaseLineHeight(this Cursor cursor)
{
return GetBaseLine(cursor) - cursor.HotSpot.Y;
}
/// <summary>
/// Gets the physical base line of the cursor, that is,
/// the distance between the top of the virtual cursor
/// and the physical base line of the cursor
/// </summary>
/// <param name="cursor">The cursor to scan</param>
/// <returns>The number of lines between the top of the virtual cursor
/// and the physical base line of the curosr</returns>
public static int GetBaseLine(this Cursor cursor)
{
IconInfo liiInfo = new IconInfo();
if (!GetIconInfo(cursor.Handle, ref liiInfo))
return cursor.Size.Height;
Bitmap lbmpBitmap = Bitmap.FromHbitmap(liiInfo.hbmMask);
try
{
BitmapData lbdData = lbmpBitmap.LockBits(
new Rectangle(0, 0, lbmpBitmap.Width, lbmpBitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
try
{
// Calculate number of lines in AND scan before any found
int liLine = ScanBits(lbdData.Scan0, 0, lbdData.Stride, cursor.Size.Height, false, true);
// If no AND scan bits found then scan for XOR bits
if (liLine == -1 && lbdData.Height == cursor.Size.Height * 2)
liLine = ScanBits(lbdData.Scan0, cursor.Size.Height, lbdData.Stride, cursor.Size.Height, true, true);
return cursor.Size.Height-liLine;
}
finally
{
lbmpBitmap.UnlockBits(lbdData);
}
}
finally
{
DeleteObject(liiInfo.hbmMask);
DeleteObject(liiInfo.hbmColor);
lbmpBitmap.Dispose();
}
}
#endregion
}
}
可以取消定義條件在上面定義「useUnsafe」所以你沒有啓用不安全的代碼,如果你喜歡,但被警告它將運行在此模式下慢。
所以這使用擴展方法,所以你所要做的就是將Cursor.Current.GetBaseLineHeight()添加到你的Cursor.Position.Y中,這將是光標下的第一個空行。
即。
Point lptBlankLineUnderCursor = new Point(Cursor.Position.X, Cursor.Position.Y + Cursor.Current.GetBaseLineHeight())
感謝, 其實我剛剛發現Cursor.Size.Height都可以使用。但是爲什麼多加2呢? 它會隨自定義光標縮放嗎? – user124858 2009-11-17 02:20:14
我不知道爲什麼要兩個作品多重。我檢查了Cursor.Size屬性到底是什麼(以及它如何與用戶的本地設置相關),並且當我的第一次嘗試沒有出現答案時就放棄了。這就是爲什麼我加了我的警告,說明代碼可能不適用於所有機器! – 2009-11-17 02:25:00