Получить текущий курсор в левом нижнем положении, чтобы подсказка отображалась правильно - PullRequest
0 голосов
/ 17 ноября 2009

Я пытаюсь отобразить всплывающую подсказку, вызывая «ToolTip.Show (String, IWin32Window, Point)», но я хотел сделать это так, как это делает проводник Windows - отображать подсказку в левом нижнем углу курсора.

Я могу получить положение мыши с помощью "MousePosition", но как я могу получить ее положение в левом нижнем углу?

Спасибо

Ответы [ 3 ]

2 голосов
/ 17 ноября 2009

Если никто не придумает лучшего ответа, вы можете попробовать это:

    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. Это работает на моей машине, но я не уверен, как это будет выглядеть при других настройках.

Подсказка: добавьте эту строку в событие MouseMove вашей формы.

1 голос
/ 15 марта 2011

Единственный способ сделать это - отсканировать MASK курсоров и найти расстояние между последним установленным пикселем в маске курсора и горячей точкой курсоров Y, мне пришлось сделать это сегодня, так что вот код:

#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())
1 голос
/ 17 ноября 2009

Я думаю, что Explorer помещает всплывающую подсказку под горячую точку курсора, поэтому вам не нужно корректировать 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);
}
...