【问题标题】:Are there anyway to determine coordinates of Top-Left corner of Column in ListView?无论如何确定ListView中Column的左上角坐标吗?
【发布时间】:2013-07-14 18:24:09
【问题描述】:

我找不到任何可以帮助我解决这个问题的函数,我不想编写疯狂的函数来 HitTest ListView 区域的每个像素,以找出所需 Column 的坐标(如果有可能获得 Column来自 HitTest)。

感谢 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;
}

【问题讨论】:

  • 这个问题看起来没有答案。您可以通过调用 SendMessage() 来获取它。您需要 TVM_GETHEADER 和 HDM_GETITEMRECT。
  • 好吧,我至少知道如何去做,并根据 Yair Nevet 的评论编写了自己的函数。您可以发布您准备使用的答案,它将被接受。
  • @HansPassant 我很想看看你的解决方案。

标签: c# winforms listview coordinates


【解决方案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;
}

【讨论】:

    【解决方案2】:

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

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

    现在使用接收到的点的 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;
    }
    

    【讨论】:

    • 打扰一下,我至少从哪里得到该 ColumnHeader 的一些坐标?它不是控件,不包含 Location 字段,与 Top 和 Left 相同。
    • 你可以根据列表视图的位置和列宽来计算它。
    • 谢谢,这至少是一些想法。由于边界等原因,不是 100% 准确,但我仍然可以使用...
    • 嗯,我不能说我可以使用您编辑的答案,因为不明白它的作用。根据您的评论,我编写了包含在主要问题中的函数。
    • 是的,但你明白了我的意思,我很高兴看到你编写了一个函数来为你+1! :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-29
    • 1970-01-01
    相关资源
    最近更新 更多