-->

Excel addin: Cell absolute position

2019-07-21 18:26发布

问题:

How can I find the absolute coordinates in pixels of a certain cell?

I am developing an Office 2010 addon (the Ribbon UI) and I add a new button to a new menu in the Ribbon and when the button is pressed, I want to get the screen position of that cell. The problem is that

Globals.ThisWorkbook.Application.ActiveCell . Top / Left

only give the position relative to the A1 corner of the spreadsheet, while I want the position relative to 0,0 of the screen.

I found this: How to get screen X and Y of an Excel 2003 cell in C# but it's for Office 2003 and I don't fully understand the answer.

I'm using C# for devel, but VB will also do.

Thanks!

回答1:

I found this post, which contains the API calls used below. I also was reminded that you can get the height of the ribbon with Application.Commandbars("Ribbon").Height. So, in VBA you'd do:

EDIT: In response the Formula Bar and Headings height issue I added a function that hides them, gets the ActiveWindow.Height, then shows them and gets the new ActiveWindow.Height and figures the difference. That function now gets called in the line below that adds together the heights before converting. I think it works but I didn't do a lot of testing.

Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hdc As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As Long, ByVal nIndex As Long) As Long
Private Const LOGPIXELSX As Long = 88
Private Const LOGPIXELSY As Long = 90

Sub CellTopLeftPixels(rng As Excel.Range)
Dim RibbonHeight As Long
Dim TotalTop As Long
Dim TotalLeft As Long

RibbonHeight = Application.CommandBars("Ribbon").Height
TotalTop = (RibbonHeight + GetFormulaBarAndHeadingsHeight + rng.Top) * PixelsPerPointY
TotalLeft = rng.Left * PixelsPerPointX
Debug.Print "Top: "; TotalTop; " Left: "; TotalLeft
End Sub

Function GetFormulaBarAndHeadingsHeight()
Dim ActiveWindowHeightWhenHidden As Long
Dim ActiveWindowHeightWhenShown As Long

Application.DisplayFormulaBar = False
ActiveWindow.DisplayHeadings = False
ActiveWindowHeightWhenHidden = ActiveWindow.Height
Application.DisplayFormulaBar = True
ActiveWindow.DisplayHeadings = True
ActiveWindowHeightWhenShown = ActiveWindow.Height
GetFormulaBarAndHeadingsHeight = ActiveWindowHeightWhenHidden - ActiveWindowHeightWhenShown
End Function

Function PixelsPerPointX() As Double
Dim hdc As Long
Dim PixPerInchX As Long

hdc = GetDC(0)
PixPerInchX = GetDeviceCaps(hdc, LOGPIXELSX)
PixelsPerPointX = PixPerInchX / 72
ReleaseDC 0, hdc
End Function

Function PixelsPerPointY() As Double
Dim hdc As Long
Dim PixPerInchY As Long

hdc = GetDC(0)
PixPerInchY = GetDeviceCaps(hdc, LOGPIXELSY)
PixelsPerPointY = PixPerInchY / 72
ReleaseDC 0, hdc
End Function

The 72 above is the points per inch.

Call it like:

Sub test()
CellTopLeftPixels ActiveCell
End Sub


回答2:

I found this to work without any kind of hackery:

    Point GetScreenPositionFromCell(Excel.Range cell, Excel.Application excel)
    {
        var wnd = excel.ActiveWindow;
        if (wnd != null)
        {
            var result = new Point
            {
                X = wnd.PointsToScreenPixelsX((int)cell.Left),
                Y = wnd.PointsToScreenPixelsY((int)cell.Top)
            };

            //cleanup
            Marshal.ReleaseComObject(wnd);
            wnd = null;

            return result;
        }

        throw new Exception("Error retrieving active Excel-window.");
    }