无论如何确定ListView中列的左上角坐标?

时间:2013-07-14 18:24:09

标签: c# winforms listview coordinates

我找不到任何可以帮助我的功能,我不想编写疯狂的功能,将HitTest的ListView区域的每个像素,找出所需的列的坐标(如果有可能得到列来自HitTest)。

enter image description here

感谢Yair Nevet评论,我写了下一个函数来确定所需列的左侧位置:

private int GetLeftOfColumn(ColumnHeader column, ListView lv)
{
    if (!lv.Columns.Contains(column))
        return -1;

    int calculated_left = 0;

    for (int i = 0; i < lv.Columns.Count; i++)
        if (lv.Columns[i] == column)
            return calculated_left;
        else
            calculated_left += lv.Columns[i].Width + 1;

    return calculated_left;
}

2 个答案:

答案 0 :(得分:1)

您可以使用PointToScreenPointToClient类来实现它,看看:

Point locationOnForm = listView1.FindForm()
              .PointToClient(listView1.Parent.PointToScreen(listView1.Location));

现在使用收到的Point的X和Y坐标以及列的宽度和高度来计算它在表单上的位置:

private int GetLeftOfColumn(ColumnHeader column, ListView lv)
{
    if (!lv.Columns.Contains(column))
        return -1;

    int calculated_left = 0;

    for (int i = 0; i < lv.Columns.Count; i++)
        if (lv.Columns[i] == column)
            return calculated_left;
        else
            calculated_left += lv.Columns[i].Width + 1;

    return calculated_left;
}

答案 1 :(得分:1)

因为上面提到了USER32消息,但没有给出实现,而我刚刚解决它时遇到了这个消息,这里是使用消息传递到本机代码的工作版本。

以下是私有NativeMethods类中包含的一些必需项:

private static class NativeMethods {
    private const int LVM_FIRST = 0x1000;
    private const int LVM_GETHEADER = LVM_FIRST + 31;

    private const int HDM_FIRST = 0x1200;
    private const int HDM_GETITEMRECT = HDM_FIRST + 7;

    private const int SB_HORZ = 0;
    private const int SB_VERT = 1;

    private const int SIF_POS = 0x0004;

    [StructLayout(LayoutKind.Sequential)]
    public class SCROLLINFO {
        public int cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO));
        public int fMask;
        public int nMin;
        public int nMax;
        public int nPage;
        public int nPos;
        public int nTrackPos;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);

    [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessageGETRECT(IntPtr hWnd, int Msg, int wParam, ref RECT lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern bool GetScrollInfo(IntPtr hWnd, int fnBar, SCROLLINFO scrollInfo);

    /// <summary>
    /// Return the handle to the header control on the given list
    /// </summary>
    /// <param name="list">The listview whose header control is to be returned</param>
    /// <returns>The handle to the header control</returns>
    public static IntPtr GetHeaderControl(ListView list) {
        return SendMessage(list.Handle, LVM_GETHEADER, 0, 0);
    }

    /// <summary>
    /// Get the scroll position of the given scroll bar
    /// </summary>
    /// <param name="lv"></param>
    /// <param name="horizontalBar"></param>
    /// <returns></returns>
    public static int GetScrollPosition(ListView lv, bool horizontalBar) {
        int fnBar = (horizontalBar ? SB_HORZ : SB_VERT);

        SCROLLINFO scrollInfo = new SCROLLINFO();
        scrollInfo.fMask = SIF_POS;
        if (GetScrollInfo(lv.Handle, fnBar, scrollInfo)) {
            return scrollInfo.nPos;
        }
        else {
            return -1;
        }
    }

    /// <summary>
    /// Return the screen rectangle of the column header item specified
    /// </summary>
    /// <param name="handle">Handle to the header control to check</param>
    /// <param name="index">Index of the column to get</param>
    /// <returns></returns>
    public static Rectangle GetHeaderItemRect(IntPtr handle, int index) {
        RECT rc = new RECT();
        IntPtr result = NativeMethods.SendMessageGETRECT(handle, HDM_GETITEMRECT, index, ref rc);
        if (result != IntPtr.Zero) {
            return new Rectangle(rc.Left, rc.Top, rc.Right - rc.Left, rc.Bottom - rc.Top);
        }
        return Rectangle.Empty;
    }
}

以下是获取您所在位置的代码:

/// <summary>
/// Get's the location coordinate of the specified column header's top/left corner
/// </summary>
/// <param name="oListView">ListView control to get the column header location from</param>
/// <param name="iColumn">Column to find the location for</param>
/// <param name="bAsScreenCoordinate">Whether the location returned is for the screen or the local ListView control</param>
/// <returns>Location of column header or <see cref="Point.Empty"/> if it could not be retrieved</returns>
public static Point GetColumnHeaderTopLeft(this ListView oListView, int iColumn, bool bAsScreenCoordinate) {
    if (oListView == null) {
        throw new ArgumentNullException();
    }
    if ((iColumn < 0) || (iColumn >= oListView.Columns.Count)) {
        throw new ArgumentOutOfRangeException();
    }

    // Get the header control's rectangle
    IntPtr hndHeader = NativeMethods.GetHeaderControl(oListView);
    Rectangle oHeaderRect = NativeMethods.GetHeaderItemRect(hndHeader, iColumn);
    if (oHeaderRect.IsEmpty) {
        return Point.Empty;
    }

    // Get the scroll bar position to adjust the left
    int iScroll = NativeMethods.GetScrollPosition(oListView, true);

    // Create the local coordinate
    Point oLocation = new Point(oHeaderRect.Left - iScroll, oHeaderRect.Top);

    // Return the local or screen coordinate
    return bAsScreenCoordinate ? oListView.PointToScreen(oLocation) : oLocation;
}