Làm sao xác định được Top và Left của ô A1 so với Window? (1 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

Hoàng Trọng Nghĩa

Chuyên gia GPE
Thành viên BQT
Moderator
Tham gia
17/8/08
Bài viết
8,662
Được thích
16,725
Giới tính
Nam
Không biết đặt tiêu đề như vậy có sát nghĩa không, nhưng làm sao xác định được khoảng cách của 2 mũi tên (Xanh và Hồng) trong hình dưới đây:

Cám ơn rất nhiều.
 

File đính kèm

  • Picture2.jpg
    Picture2.jpg
    21.8 KB · Đọc: 135
Không biết đặt tiêu đề như vậy có sát nghĩa không, nhưng làm sao xác định được khoảng cách của 2 mũi tên (Xanh và Hồng) trong hình dưới đây:

Cám ơn rất nhiều.

Vị trí của cell là cái này:
Mã:
Function CellPosition(Optional rCell As Range)
  Dim PointsPerPixelX As Double, PointsPerPixelY As Double
  Dim x As Long, y As Long
  Dim rng As Range, bFound As Boolean, Arr(1 To 2)
  Application.Volatile
  If rCell Is Nothing Then Set rCell = ActiveCell
  Set rCell = rCell(1, 1)
  PointsPerPixelX = 72 / GetDeviceCaps(GetDC(0), 88)
  PointsPerPixelY = 72 / GetDeviceCaps(GetDC(0), 90)
  ReleaseDC 0, GetDC(0)
  For x = Int(Application.Left / PointsPerPixelX) To Int(Application.Left / PointsPerPixelX + Application.Width / PointsPerPixelX)
    For y = Int(Application.Top / PointsPerPixelY) To Int(Application.Top / PointsPerPixelY + Application.Height / PointsPerPixelY)
      Set rng = Application.Windows(1).RangeFromPoint(x, y)
      If Not (rng Is Nothing) Then
        bFound = True
        Exit For
      End If
    Next
    If bFound Then Exit For
  Next
  Arr(1) = x * PointsPerPixelX + (rCell.Left - rng.Left)
  Arr(2) = y * PointsPerPixelY + (rCell.Top - rng.Top)
  CellPosition = Arr
End Function
Nếu tôi đoán không lầm thì bạn hỏi vấn đề này nhằm mục đích định vị cho UserForm khi nó show trên bảng tính, đúng chứ?
 
Lần chỉnh sửa cuối:
Upvote 0
Theo tôi thì không thể viết tắt như sau được.

Mã:
  PointsPerPixelX = 72 / GetDeviceCaps(GetDC(0), 88)  <--- A
  PointsPerPixelY = 72 / GetDeviceCaps(GetDC(0), 90)  <--- B
  ReleaseDC 0, GetDC(0)  <--- C

Help:


View attachment 93281




Như đã thấy, với mỗi GetDC phải có 1 ReleaseDC tương ứng.
Ta xét dòng C code. Thực ra code được thực hiện theo 2 bước. Do hàm Release có 2 thông số nên:

1. bước 1 sẽ là xác định thông số thứ hai, tức gọi hàm GetDC
2. bước 2 là gọi hàm ReleaseDC khi đã xác định được 2 thông số.

Như vậy ReleaseDC được thực hiện cho GetDC ở dòng C. Với 2 lần gọi GetDC ở dòng A và B không có ReleaseDC. Mỗi GetDC phải có 1 ReleaseDC vì mỗi lần gọi GetDC thì ta nhận được một handle of a display device context KHÁC.
Dễ kiểm tra bằng cách không viết tắt mà viết rõ ra - ta có 3 lần gọi GetDC:

Mã:
Private Declare Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32.dll" (ByVal hwnd As Long, ByVal hDC As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32.dll" (ByVal hDC As Long, ByVal nIndex As Long) As Long

Function CellPosition(Optional rCell As Range)
  Dim PointsPerPixelX As Double, PointsPerPixelY As Double
  Dim X As Long, Y As Long
  Dim rng As Range, bFound As Boolean, Arr(1 To 2), DC1 As Long, DC2 As Long, DC3 As Long

  Application.Volatile
  If rCell Is Nothing Then Set rCell = ActiveCell
  Set rCell = rCell(1, 1)
  
  DC1 = GetDC(0)
  PointsPerPixelX = 72 / GetDeviceCaps(DC1, 88)
  DC2 = GetDC(0)
  PointsPerPixelY = 72 / GetDeviceCaps(DC2, 90)
  DC3 = GetDC(0)
  ReleaseDC 0, DC3
  
  ReleaseDC 0, DC1
  ReleaseDC 0, DC2
  
  MsgBox "DC1 = " & DC1 & ", DC2 = " & DC2 & ", DC3 = " & DC3
End Function

Tóm lại ta nên thay đoạn code trên bằng
Mã:
  DC = GetDC(0)
  PointsPerPixelX = 72 / GetDeviceCaps(DC, 88)
  PointsPerPixelY = 72 / GetDeviceCaps(DC, 90)
  ReleaseDC 0, DC

Mà giá trị tính được là tính từ góc trên bên trái của MÀN HÌNH (screen) (x, y trong RangeFromPoint là tính trong hệ tọa độ screen) chứ không phải tính từ góc trên bên trái của cửa sổ Excel như bạn Nghĩa yêu cầu.
Tất nhiên tôi nói ra để cho chính xác thôi chứ chuyển sang hệ toạ độ của cửa sổ Excel không có gì khó cả.
 
Lần chỉnh sửa cuối:
Upvote 0
Em hỏi ngoài lề chút: Không biết phần Help mà anh chụp hình đưa lên là anh lấy từ đâu vậy? Có thể hướng dẫn em tải về dùng được không?

À thì cài Delphi vào thì nó có đủ help thôi - Delphi.hlp và Win.hlp
Không biết tôi copy có đủ không, Tuấn thử xem
 

File đính kèm

Upvote 0
À thì cài Delphi vào thì nó có đủ help thôi - Delphi.hlp và Win.hlp
Không biết tôi copy có đủ không, Tuấn thử xem

Ôi... em tìm được rồi. Em không cài Delphi (mà cũng hổng biết nó là cái gì luôn) nên phải download "nguyên con" Win32 Developer's References tới 50MB mới xài được anh à
Nó ở đây:
http://radasm.cherrytree.at/download/?did=9
(Tài liệu này nghiên cứu mấy năm chắc cũng chưa hết được! Hay!)
 
Lần chỉnh sửa cuối:
Upvote 0
Ôi... em tìm được rồi. Em không cài Delphi (mà cũng hổng biết nó là cái gì luôn) nên phải download "nguyên con" Win32 Developer's References tới 50MB mới xài được anh à
Nó ở đây:
http://radasm.cherrytree.at/download/?did=9
(Tài liệu này nghiên cứu mấy năm chắc cũng chưa hết được! Hay!)


Cái này nó hơi nặng.
Delphi là môi trường để lập trình dùng ngôn ngữ Pascal
 
Lần chỉnh sửa cuối:
Upvote 0
[/CODE]

Tóm lại ta nên thay đoạn code trên bằng
Mã:
  DC = GetDC(0)
  PointsPerPixelX = 72 / GetDeviceCaps(DC, 88)
  PointsPerPixelY = 72 / GetDeviceCaps(DC, 90)
  ReleaseDC 0, DC

Mà giá trị tính được là tính từ góc trên bên trái của MÀN HÌNH (screen) (x, y trong RangeFromPoint là tính trong hệ tọa độ screen) chứ không phải tính từ góc trên bên trái của cửa sổ Excel như bạn Nghĩa yêu cầu.
Tất nhiên tôi nói ra để cho chính xác thôi chứ chuyển sang hệ toạ độ của cửa sổ Excel không có gì khó cả.

attachment.php


Xin vui lòng cho em biết 3 giá trị DC1, DC2, DC3 là gì? Tại sao nó có giá trị âm, và cách để nó chuyển các giá trị đó về dạng của kích thước mà Form nó hiểu như thế nào ạ.

Cám ơn rất nhiều!
 

File đính kèm

  • Picture1.jpg
    Picture1.jpg
    14.7 KB · Đọc: 110
Upvote 0


Xin vui lòng cho em biết 3 giá trị DC1, DC2, DC3 là gì? Tại sao nó có giá trị âm, và cách để nó chuyển các giá trị đó về dạng của kích thước mà Form nó hiểu như thế nào ạ.

Cám ơn rất nhiều!

Vậy mục đích cuối cùng của Nghĩa là gì? Có phải là muốn định vị form theo vị trí cell không? Nếu đúng là vậy thì ta tiếp tục
 
Upvote 0
Thì Thầy đã "đi guốc trong bụng" em rồi còn gì! Thầy hướng dẫn em đi.

Nhân tiện sửa lại hàm theo ý kiến của anh siwtom:
PHP:
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hDC As Long, ByVal nIndex As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hDC As Long) As Long
Function CellPosition(Optional rCell As Range)
  Dim PointsPerPixelX As Double, PointsPerPixelY As Double
  Dim x As Long, y As Long
  Dim hDC1 As Long, hDC2 As Long, hDC3 As Long
  Dim rng As Range, bFound As Boolean, Arr(1 To 2)
  
  If rCell Is Nothing Then Set rCell = ActiveCell
  Set rCell = rCell(1, 1)
  hDC1 = GetDC(0)
  PointsPerPixelX = 72 / GetDeviceCaps(hDC1, 88)
  hDC2 = GetDC(0)
  PointsPerPixelY = 72 / GetDeviceCaps(hDC2, 90)
  hDC3 = GetDC(0)
  ReleaseDC 0, hDC3
  With Application
    .Volatile
    For x = Int(.Left / PointsPerPixelX) To Int(.Left / PointsPerPixelX + .Width / PointsPerPixelX)
      For y = Int(.Top / PointsPerPixelY) To Int(.Top / PointsPerPixelY + .Height / PointsPerPixelY)
        Set rng = ActiveWindow.RangeFromPoint(x, y)
        If Not (rng Is Nothing) Then
          bFound = True
          Exit For
        End If
      Next
      If bFound Then Exit For
    Next
  End With
  Arr(1) = x * PointsPerPixelX + (rCell.Left - rng.Left)
  Arr(2) = y * PointsPerPixelY + (rCell.Top - rng.Top)
  CellPosition = Arr
End Function
Viết thêm 1 Sub để gọi UserForm
PHP:
Sub ShowForm(ByRef frm As Object, rCell As Range)
  Dim Arr
  On Error Resume Next
  Arr = CellPosition(rCell)
  With frm
    .Hide
    .StartUpPosition = 0
    .Left = Arr(1): .Top = Arr(2)
    .Show False
  End With
End Sub
Gọi form bằng sự kiện SelectionChange nhé:
PHP:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
 If Not Intersect(Target, Range("A1:A20")) Is Nothing Then
   If Target.Count = 1 Then ShowForm UserForm1, Target(, 2)
 Else
   Unload UserForm1
 End If
End Sub
Vậy thôi
(hàm chính đã có, sao Nghĩa không tự ráp vào mà thí nghiệm nhỉ?)
 
Upvote 0
attachment.php


Xin vui lòng cho em biết 3 giá trị DC1, DC2, DC3 là gì?

Thì bạn nhìn hình đính kèm trong bài viết của tôi thì biết nó là handle of a display device context.

Tại sao nó có giá trị âm

Thế bạn đọc ở đâu đó là handle of a display device context bắt buộc phải là số dương à?

và cách để nó chuyển các giá trị đó về dạng của kích thước mà Form nó hiểu như thế nào ạ

Câu cú thế này thì tôi chịu không hiểu nổi.
Nếu tôi chưa quên ngữ pháp thì trong câu:
"Tại sao có giá trị âm, và cách để chuyển các giá trị đó về dạng của kích thước mà Form nó hiểu như thế nào ạ."
nó xanh và nó đỏ là cùng một đối tượng, mà nó xanh - và do vậy cả nó đỏ - chắc chắn ám chỉ handle of a display device context, tức DC (DC1, DC2, DC3).
Vậy thì câu của bạn phải hiểu là:
Tại sao handle of a display device context có giá trị âm, và cách để handle of a display device context chuyển các giá trị CỦA MÌNH về dạng của kích thước mà Form nó hiểu như thế nào ạ"???

handle of a display device context là một con số, giá trị, nó có biết thao tác gì đâu mà "nó chuyển"?

Còn nếu bạn muốn nói:
"... và cách chuyển handle of a display device context về dạng của kích thước ..."

Thì tôi cũng chả hiểu. Tại sao lại phải chuyển handle of a display device context về cái gì đấy? Mà: "về dạng của kích thước mà Form nó hiểu" có nghĩa là gì vậy?
 
Lần chỉnh sửa cuối:
Upvote 0
Nhân tiện sửa lại hàm theo ý kiến của anh siwtom:
PHP:
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hDC As Long, ByVal nIndex As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hDC As Long) As Long
Function CellPosition(Optional rCell As Range)
  Dim PointsPerPixelX As Double, PointsPerPixelY As Double
  Dim x As Long, y As Long
  Dim hDC1 As Long, hDC2 As Long, hDC3 As Long
  Dim rng As Range, bFound As Boolean, Arr(1 To 2)
  
  If rCell Is Nothing Then Set rCell = ActiveCell
  Set rCell = rCell(1, 1)
  hDC1 = GetDC(0)
  PointsPerPixelX = 72 / GetDeviceCaps(hDC1, 88)
  hDC2 = GetDC(0)
  PointsPerPixelY = 72 / GetDeviceCaps(hDC2, 90)
  hDC3 = GetDC(0)
  ReleaseDC 0, hDC3
  With Application
    .Volatile
    For x = Int(.Left / PointsPerPixelX) To Int(.Left / PointsPerPixelX + .Width / PointsPerPixelX)
      For y = Int(.Top / PointsPerPixelY) To Int(.Top / PointsPerPixelY + .Height / PointsPerPixelY)
        Set rng = ActiveWindow.RangeFromPoint(x, y)
        If Not (rng Is Nothing) Then
          bFound = True
          Exit For
        End If
      Next
      If bFound Then Exit For
    Next
  End With
  Arr(1) = x * PointsPerPixelX + (rCell.Left - rng.Left)
  Arr(2) = y * PointsPerPixelY + (rCell.Top - rng.Top)
  CellPosition = Arr
End Function
Viết thêm 1 Sub để gọi UserForm
PHP:
Sub ShowForm(ByRef frm As Object, rCell As Range)
  Dim Arr
  On Error Resume Next
  Arr = CellPosition(rCell)
  With frm
    .Hide
    .StartUpPosition = 0
    .Left = Arr(1): .Top = Arr(2)
    .Show False
  End With
End Sub
Gọi form bằng sự kiện SelectionChange nhé:
PHP:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
 If Not Intersect(Target, Range("A1:A20")) Is Nothing Then
   If Target.Count = 1 Then ShowForm UserForm1, Target(, 2)
 Else
   Unload UserForm1
 End If
End Sub
Vậy thôi
(hàm chính đã có, sao Nghĩa không tự ráp vào mà thí nghiệm nhỉ?)

Trời ơi, sao lại gọi GetDC 3 lần là sao? Gọi 1 lần thôi. Mà vẫn không chuẩn như cũ.

Mã:
hDC1 = GetDC(0)
PointsPerPixelX = 72 / GetDeviceCaps(hDC1, 88)
hDC2 = GetDC(0)
PointsPerPixelY = 72 / GetDeviceCaps(hDC2, 90)
hDC3 = GetDC(0)
ReleaseDC 0, hDC3

Sao không gọi ReleaseDC cho hDC1, hDC2?

Mà
Mã:
 hDC3 = GetDC(0)
  ReleaseDC 0, hDC3

hay code cũ, tương đương:

Mã:
ReleaseDC 0, GetDC

là những code vô bổ. Tự dưng lại gọi GetDC để rồi chả sử dụng cái handle of a display device context mà GetDC trả về để làm bất cứ việc gì mà mục đích chỉ là tạo "công ăn việc làm" cho ReleaseDC?

Code chuẩn tôi đã đưa ở bài #3 rồi mà. Nhắc lại: gọi GetDC 1 lần để lấy handle of a display device context rồi sử dụng handle of a display device context ấy trong 2 lần gọi GetDeviceCaps. Cuối cùng thì gọi ReleaseDC để giải phóng device context.
Mã:
  DC = GetDC(0)
  PointsPerPixelX = 72 / GetDeviceCaps(DC, 88)
  PointsPerPixelY = 72 / GetDeviceCaps(DC, 90)
  ReleaseDC 0, DC
--------------------

Tình hình hiện nay là ta có rất nhiều vòng lặp thừa. Giả dụ cửa sổ Excel là maximize và ta giả sử màn hình có chiều ngang là 1200 pixels và khoảng cách từ thanh tiêu đề (gờ trên) tới dòng cell đầu tiên khoảng 300 pixels thì sẽ có 1200 * 300 = 360.000 vòng lặp thừa cho tới khi bFound = TRUE.
Nếu là tôi thì tôi sẽ tối ưu chút code bằng việc giảm số vòng "thừa" này.
 
Lần chỉnh sửa cuối:
Upvote 0
attachment.php


Xin vui lòng cho em biết 3 giá trị DC1, DC2, DC3 là gì? Tại sao nó có giá trị âm, và cách để nó chuyển các giá trị đó về dạng của kích thước mà Form nó hiểu như thế nào ạ.

Cám ơn rất nhiều!

Lập trình API rất trừu tượng khi ban đầu chứng ta mày mò! Mình học được món này tương đối là do may mắn ban đầu đọc được sách "Kỹ thuật lập trình Windows... - Phần 1" của nước ngoài, các ví dụ toàn là ngôn ngữ C, hồi đó đọc sách thích không phải để làm được nó ngay nhưng thích ở chỗ hiểu được phần nào quy tắc làm việc của HĐH Windows. Nếu Nghĩa muốn khám phá thế giới API - Lập trình cho Windows thì tìm loại sách đó đọc.

Lập trình VBA tương đối trực quan, đặc biệt trên userform, vì nhìn thấy khung và đối tượng nay. Thực ra VBA để chúng ta làm ứng dụng nên ta không hiểu bản chất hay quy tắc làm việc của Windows. Windows làm việc với nhiều loại đối tượng: vẽ, cửa sổ, máy in, media,... trong quá trình làm việc thì HĐH lưu giữ các đối tượng đó ở một địa chỉ tronh bộ nhớ. HĐH Windows cung cấp các hàm để làm việc với các đối tượng mà nó đang quản lý - Application Programming Interface (API), tuy nhiên chúng ta không thể nhìn tận mắt hình thù của nó, HĐH chỉ cho ta địa chỉ vùng nhớ (kiểu giá trị là Long - VB/VBA), các hàm API của Windows làm việc với các thiết bị đều phải chỉ định thông qua địa chỉ của nó.
Đối tượng cửa sổ - "Handle to Window", đặt tên và khai báo biến thường là hWnd As Long. Trong VBA, để biết địa chỉ lưu điều khiển Userform chúng ta hay dùng hàm FindWindow(). Để nhận các thông số như độ rộng, cao của cửa sổ dùng =GetWindowRect(hWnd, Rect),....với các thuộc tính khác sẽ sử dụng các hàm khác.
Thiết bị vẽ - "device context", điều khiển thiết bị vẽ "handle to a device context". Đặt tên và khai báo biến thường là hDC As Long, để nhận địa chỉ thiết bị vẽ của cửa sổ dùng hDC = GetDC(hWnd), hDC lưu địa chỉ của thiết bị vẽ, địa chỉ này là số nguyên kiểu Long, như là số nhà của một công ty. Muốn vẽ chữ lên cửa sổ thì phải vẽ vào thiết bị vẽ của cửa sổ. Ví dụ hàm viết chữ TextOut(hDC, vị trí X, vị trí Y, "Nội dung", Số ký tự), việc vẽ là hĐH làm cho chúng ta.

Nói tóm lại là lập trình API là sử dụng các hàm mà HĐH cung cấp, đối số của các hàm này thường phải có địa chỉ của các đối tượng hay thiết bị mà HĐH đang quản lý, trên cơ sở đó HĐH mới chuyển yêu cầu từ người dùng (các tham số của hàm) đến đúng các thiết bị hay đối tượng cần can thiệp. Như việc ta yêu cầu sơn nhà ta chỉ cần ra lệnh =SonNha(địa chỉ nhà, màu sắc, loại sơn, chổi sơn,...) còn làm như thế nào thì HĐH làm.

Bài viết này hơi lan man chỉ để các bạn thích lập trình nâng cao có hướng tiếp cận với cách lập trình API. Để hiểu thấu và chính xác hơn cần đọc tài liệu lập trình API, làm với hàm nào thì phải đọc help hàm đó, đặc biệt nguồn tài liệu nên từ Microsoft. Nếu các bạn học có cách tiếp cận bài bản thì sẽ nắm bắt nhanh, hiểu vấn đề có hệ thống và chủ động vận dụng linh hoạt hơn.
 
Lần chỉnh sửa cuối:
Upvote 0
Trời ơi, sao lại gọi GetDC 3 lần là sao? Gọi 1 lần thôi. Mà vẫn không chuẩn như cũ.

Mã:
hDC1 = GetDC(0)
PointsPerPixelX = 72 / GetDeviceCaps(hDC1, 88)
hDC2 = GetDC(0)
PointsPerPixelY = 72 / GetDeviceCaps(hDC2, 90)
hDC3 = GetDC(0)
ReleaseDC 0, hDC3

Sao không gọi ReleaseDC cho hDC1, hDC2?

Mà
Mã:
 hDC3 = GetDC(0)
  ReleaseDC 0, hDC3

Đã nói là em không rành mà, nhất là code liên quan đến API
(Anh có nói em mới biết chứ)
Tức là sửa thành vầy:
PHP:
Function CellPosition(Optional rCell As Range)
......................
  hDC1 = GetDC(0)
  PointsPerPixelX = 72 / GetDeviceCaps(hDC1, 88)
  ReleaseDC 0, hDC1
  hDC2 = GetDC(0)
  PointsPerPixelY = 72 / GetDeviceCaps(hDC2, 90)
  ReleaseDC 0, hDC2
......................
End Function
Đúng không anh?
Ẹc... Ẹc...
----------------
Anh có nói rằng:
Nếu là tôi thì tôi sẽ tối ưu chút code bằng việc giảm số vòng "thừa" này.
Vậy anh làm luôn đi cho em tham khảo với
 
Lần chỉnh sửa cuối:
Upvote 0
Đã nói là em không rành mà, nhất là code liên quan đến API
(Anh có nói em mới biết chứ)
Tức là sửa thành vầy:
PHP:
Function CellPosition(Optional rCell As Range)
......................
  hDC1 = GetDC(0)
  PointsPerPixelX = 72 / GetDeviceCaps(hDC1, 88)
  ReleaseDC 0, hDC1
  hDC2 = GetDC(0)
  PointsPerPixelY = 72 / GetDeviceCaps(hDC2, 90)
  ReleaseDC 0, hDC2
......................
End Function
Đúng không anh?
Ẹc... Ẹc...

Như thế thì đúng về kỹ thuật. Nhưng tại sao lại phải gọi GetDC 2 lần và ReleaseDC 2 lần?
Một lần thôi. Như tôi đã viết. Tóm lại là: Ta "lấy" handle của device context - hDC - 1 lần bằng GetDC, GetWindowDC (Create*DC)--> làm mọi việc cần tới hDC --> khi xong việc, tức không cần tới device context nữa thì gọi ReleaseDC (DeleteDC)


----------------
Anh có nói rằng:

Vậy anh làm luôn đi cho em tham khảo với

Xin mời.
Chỉ có điều tôi viết cho Excel 2007 thôi vì không có Excel 2010 nên tôi không biết các cửa sổ có class như thế nào. Trong Excel 2007 thì tôi dùng class: "XLMAIN", "XLDESK", "EXCEL7"

Mã:
Private Const LOGPIXELSX = 88
Private Const LOGPIXELSY As Long = 90
Private Const POINTS_PER_INCH As Long = 72

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Private Declare Function GetWindowRect Lib "user32.dll" (ByVal hwnd As Long, ByRef lpRect As RECT) As Long
Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function FindWindowEx Lib "user32.dll" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Private Declare Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32.dll" (ByVal hwnd As Long, ByVal hdc As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32.dll" (ByVal hdc As Long, ByVal nIndex As Long) As Long

Function CellPosition(Optional rCell As Range)
  Dim PointsPerPixelX As Double, PointsPerPixelY As Double, DC As Long
  Dim x As Long, y As Long, h As Long, rc As RECT
  Dim rng As Range, bFound As Boolean, Arr(1 To 2)
    If rCell Is Nothing Then Set rCell = ActiveCell
    Set rCell = rCell(1, 1)
    DC = GetDC(0)
    PointsPerPixelX = POINTS_PER_INCH / GetDeviceCaps(DC, LOGPIXELSX)
    PointsPerPixelY = POINTS_PER_INCH / GetDeviceCaps(DC, LOGPIXELSY)
    ReleaseDC 0, DC
      
    h = FindWindow("XLMAIN", Application.Caption)
    h = FindWindowEx(h, 0, "XLDESK", vbNullString)
    h = FindWindowEx(h, 0, "EXCEL7", vbNullString)
    GetWindowRect h, rc
    For x = rc.Left To rc.Right
        For y = rc.Top To rc.Bottom
            Set rng = Application.Windows(1).RangeFromPoint(x, y)
            If Not (rng Is Nothing) Then
                bFound = True
                Exit For
            End If
        Next
        If bFound Then Exit For
    Next
    Arr(1) = x * PointsPerPixelX + (rCell.Left - rng.Left)
    Arr(2) = y * PointsPerPixelY + (rCell.Top - rng.Top)
    CellPosition = Arr
End Function
 
Upvote 0
Xin mời.
Chỉ có điều tôi viết cho Excel 2007 thôi vì không có Excel 2010 nên tôi không biết các cửa sổ có class như thế nào. Trong Excel 2007 thì tôi dùng class: "XLMAIN", "XLDESK", "EXCEL7"

Excel 2010 chạy được luôn anh à!
Em hỏi tí. Trong code có đoạn:
Mã:
h = FindWindow("XLMAIN", Application.Caption)
[COLOR=#ff0000]h = FindWindowEx(h, 0, "XLDESK", vbNullString)
h = FindWindowEx(h, 0, "EXCEL7", vbNullString)[/COLOR]
Không biết đoạn màu đỏ có thừa không? Vì em xóa luôn nó vẫn chạy chính xác
 
Upvote 0
Excel 2010 chạy được luôn anh à!
Em hỏi tí. Trong code có đoạn:
Mã:
h = FindWindow("XLMAIN", Application.Caption)
[COLOR=#ff0000]h = FindWindowEx(h, 0, "XLDESK", vbNullString)
h = FindWindowEx(h, 0, "EXCEL7", vbNullString)[/COLOR]
Không biết đoạn màu đỏ có thừa không? Vì em xóa luôn nó vẫn chạy chính xác

Trước tiên là xin lỗi vì nhìn nhầm.
Code của ndu không phải là duyệt từng dòng pixels như tôi nhìn nhầm. Do FOR ở trong duyệt theo y nên code duyệt từng cột pixels, tính từ gờ trái của cửa sổ Application.hwnd. Như thế thì vòng lặp phải duyệt dải chữ nhật có chiều cao bằng chiều cao của cửa sổ Excel và chiều rộng bằng chiều rộng của dải đánh số các dòng (1, 2, 3,...). Như vậy thì code tôi sửa không nhanh hơn là bao. Vậy thì có lẽ chả nhọc công sửa code làm gì Tuấn ạ.

Có thể đặt trong FOR bên trong, trước "Set rng" code

Mã:
loop = loop + 1

thì biết ngay là phải qua bao nhiêu vòng lặp thì mới có bFound = TRUE.

Không thừa. Bỏ 2 dòng cuối là ta chỉ xét toàn bộ cửa sổ chính - ta nhìn thế nào trên màn hình thì nó là thế ấy. Xóa dòng cuối có nghĩa là ta xét cửa sổ nhỏ hơn, nằm trong cửa sổ chính. Không xóa dòng nào có nghĩa là xét cửa sổ còn nhỏ hơn, nằm trong cửa sổ thứ hai. Do xét cửa sổ nhỏ hơn nên mỗi dòng pixels sẽ ngắn hơn, tức (cận trên - cận dưới) của x trong FOR sẽ nhỏ hơn. Tương tự cột pixels cũng ngắn hơn.

Tuấn cứ đặt "loop = loop + 1" trong FOR bên trong rồi đọc ra giá trị của loop khi ra khỏi 2 FOR thì sẽ biết cần phải qua bao nhiêu vòng lặp mới có bFound = TRUE.

Thử cho mọi code: của Tuấn, của tôi bỏ 2 dòng cuối, của tôi bỏ dòng cuối, và của tôi để nguyên.
 
Upvote 0
Nhân tiện sửa lại hàm theo ý kiến của anh siwtom:
........................................
(hàm chính đã có, sao Nghĩa không tự ráp vào mà thí nghiệm nhỉ?)

Thật sự là hàm trước mà Thầy làm em đã ứng dụng và chạy rất tốt rồi, em cám ơn Thầy nhiều

Còn việc em hỏi thì xin thưa với các Thầy là cho tới giờ em vẫn mù mờ về đơn vị tính Vị trí của cell hoặc form đấy, em chẳng biết nó tính bằng đơn vị gì nữa! Cái này em NGU nên em phải hỏi cho BỚT NGU ạ.

Chẳng hạn cột A các Thầy nhìn hình sẽ thấy cũng là Width nhưng nó có tới 3 đơn vị đo khoảng cách (8.38 - 72 pixel) trong immediate lại là 54

attachment.php


attachment.php


Và như vậy làm em rối tung lên, chẳng hiểu nó tính bằng đơn vị gì và convert nó lại như thế nào. Hơn thế, cái hàm của Thầy Siwtom lại cho ra số hàng triệu thì lại càng không biết đó lại là đơn vị gì nữa, đã NGU càng thêm NGU.
 

File đính kèm

  • Picture1.jpg
    Picture1.jpg
    23.2 KB · Đọc: 62
  • Picture2.jpg
    Picture2.jpg
    7.5 KB · Đọc: 61
Upvote 0
Thật sự là hàm trước mà Thầy làm em đã ứng dụng và chạy rất tốt rồi, em cám ơn Thầy nhiều

Còn việc em hỏi thì xin thưa với các Thầy là cho tới giờ em vẫn mù mờ về đơn vị tính Vị trí của cell hoặc form đấy, em chẳng biết nó tính bằng đơn vị gì nữa! Cái này em NGU nên em phải hỏi cho BỚT NGU ạ.

Chẳng hạn cột A các Thầy nhìn hình sẽ thấy cũng là Width nhưng nó có tới 3 đơn vị đo khoảng cách (8.38 - 72 pixel) trong immediate lại là 54

attachment.php


attachment.php

.

Chú ý là Range.With khác với Range.ColumnWidth nha
Ngoài ra vị trí của 1 cell mà ta thường dùng như rCel.Top, rCel.Left là so sánh tương đối với cửa sổ của Application (vì thế mà Range("A1").TopRange("A1").Left luôn = 0 bất kể cửa sổ Excel đang ở chế độ phóng to hay thu nhỏ)
Trong bài này, cái mà Nghĩa đang cần là xác định vị trí của cell so với Desktop, như thế mới "khớp" được với vị trí của UserForm, đúng không?
---------------------
Và như vậy làm em rối tung lên, chẳng hiểu nó tính bằng đơn vị gì và convert nó lại như thế nào. Hơn thế, cái hàm của Thầy Siwtom lại cho ra số hàng triệu thì lại càng không biết đó lại là đơn vị gì nữa, đã NGU càng thêm NGU.
Tôi cũng NGU vậy thôi, có điều tôi không quan tâm vấn đề này, miễn biết ứng dụng là được
Mai này nếu trình độ tôi cao hơn, tôi sẽ suy nghĩ đến cũng chưa muộn
 
Lần chỉnh sửa cuối:
Upvote 0
Còn việc em hỏi thì xin thưa với các Thầy là cho tới giờ em vẫn mù mờ về đơn vị tính Vị trí của cell hoặc form đấy, em chẳng biết nó tính bằng đơn vị gì nữa! Cái này em NGU nên em phải hỏi cho BỚT NGU ạ.

Và như vậy làm em rối tung lên, chẳng hiểu nó tính bằng đơn vị gì và convert nó lại như thế nào. Hơn thế, cái hàm của Thầy Siwtom lại cho ra số hàng triệu thì lại càng không biết đó lại là đơn vị gì nữa, đã NGU càng thêm NGU.

Bây giờ thì tôi hiểu "nó chuyển" của bạn là hàm ý gì.
Bạn hiểu sai rồi. Giá trị trả về bởi hàm API GetDC (chỗ đỏ đỏ) thì liên quan gì tới Application.Left, Top, Width, Height, UserForm.Left, Top, Cell.Left, Cell.Top???
Để cho bạn hiểu tôi sẽ phân tích và giải thích cặn kẽ code của ndu. Bạn hãy đọc kỹ.
----------------
1. Mục đích duy nhất là ta phải xác định được cell(A1).Left, cell(A1).Top tính trong hệ tọa độ screen (màn hình) và đơn vị tính là Points. Tại sao lại tính trong hệ tọa độ screen? Vì nếu bạn muốn định vị UserForm vào đúng cell(A1) mà UserForm.Left, UserForm.Top được tính trong hệ tọa độ screen nên bạn phải tính tọa độ của cell(A1) trong hệ tọa độ của screen, thế thôi.
Tọa độ của cell(A1) chính là rng.Left, rng.Top trong code của ndu.
Nếu bạn muốn tính tọa độ của rCell trong hệ tọa độ của screen thì chỉ việc cộng thêm (rCell.Left - rng.Left) và (rCell.Top - rng.Top).

Thế tại sao lại tính tọa độ bằng đơn vị Points? Vì UserForm.Left, UserForm.Top trong Excel được tính bằng đơn vị Points, thế thôi.

2. Cửa sổ Application chiếm một vùng chữ nhật nào đó trên screen, ta gọi là vùng A. Cell(A1) cũng chiếm một vùng nào đó trên screen, gọi là vùng B. Dễ thấy là vùng B nằm trọn trong vùng A. Vậy ta xét lần lượt từng điểm của vùng A - đi từ góc trên bên trái của hình chữ nhật A, tức đi từ x = Application.Left, y = Application.Top - và với mỗi điểm như thế trong vòng FOR bên trong ta dùng Application.Windows(1).RangeFromPoint(x, y) để xác định xem điểm đang xét - tính trong hệ tọa độ screen (Excel help: Window.RangeFromPoint Method
Returns the Shape or Range object that is positioned at the specified pair of screen coordinates
x Required Long The value (in pixels) that represents the horizontal distance from the left edge of the screen, starting at the top.
y Required Long The value (in pixels) that represents the vertical distance from the top of the screen, starting on the left.
)
có thuộc cell nào đó không. Do x, y trong phương thức RangeFromPoint được tính bằng Pixels nên ta không thể xét
Mã:
For x = Application to Application.Left + Application.Width
được vì Application.Left và Application.Width trong Excel được tính bằng Points. Vậy phải đổi sang Pixels để nhập vào phương thức RangeFromPoint. Tức ta phải viết:
Mã:
For x = Int(Application.Left / PointsPerPixelX) To Int(Application.Left / PointsPerPixelX + Application.Width / PointsPerPixelX)

Tất nhiên khi thực hiện 2 vòng FOR thì sẽ có lúc điểm đang xét sẽ thuộc 1 cell nào đó. Do ta "đi" từ góc trên bên trái của cửa sổ Application nên điểm đầu tiên thuộc cell cũng có nghĩa là điểm đầu tiên thuộc cell A1.

3. Như ta đã thấy ở điểm 2 thì nảy sinh ra nhu cầu convert Points sang Pixels. Từ Excel help ta biết rằng:
a. Độ dài 1 inch là 72 points (point equals 1/72 of an inch)

b. Nếu ta biết 1 inch tương đương với bao nhiêu pixels thì ta sẽ dễ dàng tịnh được 1 pixels tương đương với bao nhiêu points (bài toán tỉ lệ thuận của lớp 1).

c. Để xác định 1 inch tương đương với bao nhiêu pixels thì ta dùng hàm API GetDeviceCaps
Windows help:

View attachment 93501


Tức:
72 [points] ngang --> 1 [inch] --> GetDeviceCaps(hDC, LOGPIXELSX) [pixels]
x [points] -----------------------------> 1 [pixels]
=> x = 72 / GetDieviceCaps(hDC, LOGPIXELSX) - bài toán tỉ lệ thuận

Nói cách khác thì cứ 1 pixels ngang thì là 72 / GetDieviceCaps(hDC, LOGPIXELSX) points. Tương tự cứ 1 pixels dọc thì là 72 / GetDieviceCaps(hDC, LOGPIXELSY) points

Vậy ta đặt:
Mã:
Private Const LOGPIXELSX = 88
Private Const LOGPIXELSY As Long = 90
Private Const POINTS_PER_INCH As Long = 72

PointsPerPixelX = POINTS_PER_INCH / GetDeviceCaps(DC, LOGPIXELSX)
PointsPerPixelY = POINTS_PER_INCH / GetDeviceCaps(DC, LOGPIXELSY)
-----------------------------
Sau khi đã phân tích bài toán thì thứ tự thao tác phải là:

1. hDC = GetDC(0). Ta phải gọi GetDC để "lấy" device-context handle cần thiết cho việc gọi hàm GetDeviceCaps

2. Tính xem có bao nhiêu points theo chiều ngang và dọc ứng với 1 pixel
Mã:
PointsPerPixelX = POINTS_PER_INCH / GetDeviceCaps(DC, LOGPIXELSX)
PointsPerPixelY = POINTS_PER_INCH / GetDeviceCaps(DC, LOGPIXELSY)

Bạn để ý là để tính PointsPerPixelX và PointsPerPixelY thì ta phải gọi hàm GetDeviceCaps, mà nó "đòi hỏi" device-context handle nên trước tiên ta phải thực hiện điểm 1, tức gọi GetDC để "lấy" device-context handle.

3. Có được PointsPerPixelX và PointsPerPixelY thì ta dùng nó để convert Application.Left, Top, Width, Height từ Points sang Pixels dùng trong 2 vòng FOR.
Mã:
For x = Int(Application.Left / PointsPerPixelX) To Int(Application.Left / PointsPerPixelX + Application.Width / PointsPerPixelX)
    For y = Int(Application.Top / PointsPerPixelY) To Int(Application.Top / PointsPerPixelY + Application.Height / PointsPerPixelY)
-------------------------
Sau khi phân tích bài toán để biết cần phải tính những gì và xác định thứ tự tính toán thì ta có code của ndu như sau. Tôi cũng chú thích luôn.
Mã:
Private Const LOGPIXELSX = 88
Private Const LOGPIXELSY As Long = 90
Private Const POINTS_PER_INCH As Long = 72
Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type
Private Declare Function GetWindowRect Lib "user32.dll" (ByVal hwnd As Long, ByRef lpRect As RECT) As Long
Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function FindWindowEx Lib "user32.dll" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Private Declare Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32.dll" (ByVal hwnd As Long, ByVal hdc As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32.dll" (ByVal hdc As Long, ByVal nIndex As Long) As Long

Function CellPosition(Optional rCell As Range)
  Dim PointsPerPixelX As Double, PointsPerPixelY As Double
  Dim x As Long, y As Long, hDC as Long
  Dim rng As Range, bFound As Boolean, Arr(1 To 2)
  Application.Volatile
  If rCell Is Nothing Then Set rCell = ActiveCell
  Set rCell = rCell(1, 1)

[COLOR=#0000ff]' "lấy"  device-context handle cần thiết cho việc gọi hàm GetDeviceCaps ở phần tiếp theo.[/COLOR]
  hDC = GetDC(0)
[COLOR=#0000ff]'  tính PointsPerPixelX và PointsPerPixelY dùng để convert Application.Left, Top, Width, Height
'  từ points sang pixels.[/COLOR]
  PointsPerPixelX = POINTS_PER_INCH / GetDeviceCaps(hDC, LOGPIXELSX)
  PointsPerPixelY = POINTS_PER_INCH / GetDeviceCaps(hDC, LOGPIXELSY)
[COLOR=#0000ff]' ta đã làm xong mọi việc cần có  device-context handle, vậy phải giải phóng device context[/COLOR]
  ReleaseDC 0, hDC
[COLOR=#0000ff]'  duyệt từng CỘT pixels từ trái sang phải, bắt đầu từ gờ trái của cửa sổ Application.[/COLOR]
  For x = Int(Application.Left / PointsPerPixelX) To Int(Application.Left / PointsPerPixelX + Application.Width / PointsPerPixelX)
[COLOR=#0000ff]'  trong mỗi CỘT pixels ta duyệt từng pixels, "đi" từ trên xuống dưới - từ trên xuống dưới là sao? 
'  Vì trong Windows thì gốc tọa độ là ở góc trên bên trái và trục y hướng xuống dưới.
[/COLOR]    For y = Int(Application.Top / PointsPerPixelY) To Int(Application.Top / PointsPerPixelY + Application.Height / PointsPerPixelY)
      Set rng = Application.Windows(1).RangeFromPoint(x, y)
      If Not (rng Is Nothing) Then
        bFound = True
        Exit For
      End If
    Next
    If bFound Then Exit For
  Next
[COLOR=#0000ff]'  do x, y được tính ở trên bằng đơn vị PIXELS nên trở về Excel ta đổi lại sang Points[/COLOR]
  Arr(1) = x * PointsPerPixelX + (rCell.Left - rng.Left)
  Arr(2) = y * PointsPerPixelY + (rCell.Top - rng.Top)
  CellPosition = Arr
End Function

Nếu sau khi đọc kỹ mà bạn không hiểu tôi viết gì thì chịu.
Bạn chỉ cần nhớ là phải phân tích bài toán và xác định được là cần phải tính những gì, dùng gì để tính, Excel có phương pháp nào cho các việc tính ấy không, nếu không thì Windows có những hàm nào dùng để tính những cái đó không, trong những tính toán thì đơn vị dùng là gì, có cần thay đổi chuyển đơn vị không, nếu cần thì chuyển như thế nào. Nói chung cần tư duy lôgic, đi từng bước.

Và nhớ là phải đọc help. Không đọc help Excel và help Windows thì chả biết dùng hàm như thế nào. Mà đọc thì phải hiểu chứ không được: tôi viết như thế là "chạy". Viết như thế vì "người ta" cũng viết như thế. Không có "người ta" gì ở đây. Nếu "bắt trước" thì cũng phải hiểu để sau này tự mình biết trong th thế này thế này thì phải dùng cái này cái này. Và ý nghĩa của các tham số là thế này thế này
 
Upvote 0
..............................

Nếu sau khi đọc kỹ mà bạn không hiểu tôi viết gì thì chịu.
Bạn chỉ cần nhớ là phải phân tích bài toán và xác định được là cần phải tính những gì, dùng gì để tính, Excel có phương pháp nào cho các việc tính ấy không, nếu không thì Windows có những hàm nào dùng để tính những cái đó không, trong những tính toán thì đơn vị dùng là gì, có cần thay đổi chuyển đơn vị không, nếu cần thì chuyển như thế nào. Nói chung cần tư duy lôgic, đi từng bước.

Và nhớ là phải đọc help. Không đọc help Excel và help Windows thì chả biết dùng hàm như thế nào. Mà đọc thì phải hiểu chứ không được: tôi viết như thế là "chạy". Viết như thế vì "người ta" cũng viết như thế. Không có "người ta" gì ở đây. Nếu "bắt trước" thì cũng phải hiểu để sau này tự mình biết trong th thế này thế này thì phải dùng cái này cái này. Và ý nghĩa của các tham số là thế này thế này

Cám ơn Thầy siwtom rất rất nhiều vì những giải thích rất cặn kẻ! Tuy nhiên vì em là tay ngang, không được học trường lớp gì về lập trình nên trước mắt, ai viết sao thì xài vậy, rồi từ từ nghiệm ra tại sao nó như vậy. Em nghĩ rằng không ai có thể giải thích cặn kẻ như Thầy vậy, thế nhưng đọc tới đọc lui chỉ hiểu được vài phần trong đó (chắc tại ngu lâu dốt bền khó đào tạo) nên em phải từ từ gặm nhấm những gì Thầy đã giải thích thôi, rồi phải thí nghiệm, rồi phải thực hành nữa chứ!

Cám ơn Thầy rất nhiều!
 
Upvote 0
Em thấy hàm API là quá cao cấp nếu chỉ muốn dùng để xác định vị trí của form hiện chỗ nào trong cell thì ta cũng có thể dùng thuộc tính có sẵn của window. Ví dụ chèn 1 UserForm1 vào và chép code này vào em nghĩ nó cũng đáp ứng được nhu cầu của a. Nghĩa

Mã:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
 UserForm1.Show vbModeless
    With ActiveWindow      
        UserForm1.Left = .PointsToScreenPixelsX(Target.Left + Target.Width)
        UserForm1.Top = .PointsToScreenPixelsY(Target.Top - 50)
      'Thay đổi thông số 50 cho phù hợp
    End With
End Sub
 
Upvote 0
Em thấy hàm API là quá cao cấp nếu chỉ muốn dùng để xác định vị trí của form hiện chỗ nào trong cell thì ta cũng có thể dùng thuộc tính có sẵn của window. Ví dụ chèn 1 UserForm1 vào và chép code này vào em nghĩ nó cũng đáp ứng được nhu cầu của a. Nghĩa

Mã:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
 UserForm1.Show vbModeless
    With ActiveWindow      
        UserForm1.Left = .PointsToScreenPixelsX(Target.Left + Target.Width)
        UserForm1.Top = .PointsToScreenPixelsY(Target.Top - 50)
      'Thay đổi thông số 50 cho phù hợp
    End With
End Sub

1. Nếu tôi chọn A1 thì code trên hiển thị Form ở đâu? ở B2?
2. Bạn hãy thu cửa sổ Excel về normal - bằng nút ở THANH TIÊU ĐỀ. Tốt nhất là bạn cho Excel nhỏ và nằm giữa mà hình. Bây giờ bạn chọn A1 rồi xem Form nó ở đâu.
3. Tương tự như điểm 2 nhưng bạn thu nhỏ cửa sổ của Sheet1 - bằng nút ở CÙNG DÒNG VỚI "Home - Insert ...". Tốt nhất là thu nhỏ chút và dịch sang phải, xuống dưới một chút. Bây giờ bạn chọn A1 rồi xem Form nó ở đâu.
 
Upvote 0
1. Nếu tôi chọn A1 thì code trên hiển thị Form ở đâu? ở B2?
Trên máy em test thì nó hiện ở B2 khi cửa sổ excel dạng Normal khi ta thu nhỏ thì quả là có sự chênh lệch vị trí hiện form nhưng với code trên thì nếu cửa sổ excel dạng bình thường thì code hàm API với cái có sẵn trong Excel không khác là bao, nếu không muốn code phức tạp, quả là có sự lợi hại khi dùng hàm API, cảm ơn Anh đúng là khi kiểm tra lại nhiều trường hợp mới thấy là code mình nó dỡ nhưng nó cũng phù hợp với ai thích code đơn giản
 
Upvote 0
Một vấn đề cũng gây khó khăn khi chạy code đó là không xác định chính xác tọa độ khi ZOOM <> 100%

Đã làm thử khi chia tỷ lệ với ActiveWindow.Zoom nhưng không thành công.

Làm ơn hướng dẫn thêm để hoàn thiện hàm này.

Xin cám ơn.
 
Upvote 0
Một vấn đề cũng gây khó khăn khi chạy code đó là không xác định chính xác tọa độ khi ZOOM <> 100%

Đã làm thử khi chia tỷ lệ với ActiveWindow.Zoom nhưng không thành công.

Làm ơn hướng dẫn thêm để hoàn thiện hàm này.

Xin cám ơn.

Không biết trường hợp trên tôi đã hỏi, các Thầy các anh có thể xử lý được chuyện này không?
 
Upvote 0
Một vấn đề cũng gây khó khăn khi chạy code đó là không xác định chính xác tọa độ khi ZOOM <> 100%

Đã làm thử khi chia tỷ lệ với ActiveWindow.Zoom nhưng không thành công.

Làm ơn hướng dẫn thêm để hoàn thiện hàm này.

Xin cám ơn.

1. Tôi rất dị ứng với những bài mà không có ..............................................................
 
Lần chỉnh sửa cuối:
Upvote 0
@all: bài này có lâu rồi, "khai quật" lại cho những ai chưa biết.

tui thường dùng hàm LocateForm dưới đây để đặt 1 userform vào vị trí 1 cell nào đó, hàm này gọn hơn, tốc độ cao hơn vì tránh được 2 vòng for

(declare getdc, getdevicecaps, releasedc như các bài trên)

Sub LocateForm(MyForm As Object, MyRange As Range)
Dim hDC&, deviceCaps88&, deviceCaps90&
hDC = GetDC(0)
deviceCaps88 = GetDeviceCaps(hDC, 88)
deviceCaps90 = GetDeviceCaps(hDC, 90)
MyForm.Left = ActiveWindow.PointsToScreenPixelsX(MyRange.Left * deviceCaps88 / 72) / deviceCaps88 * 72
MyForm.Top = ActiveWindow.PointsToScreenPixelsY(MyRange.Top * deviceCaps90 / 72) / deviceCaps90 * 72
ReleaseDC 0, hDC
End Sub
 
Upvote 0
@all: bài này có lâu rồi, "khai quật" lại cho những ai chưa biết.

tui thường dùng hàm LocateForm dưới đây để đặt 1 userform vào vị trí 1 cell nào đó, hàm này gọn hơn, tốc độ cao hơn vì tránh được 2 vòng for

(declare getdc, getdevicecaps, releasedc như các bài trên)

Sub LocateForm(MyForm As Object, MyRange As Range)
Dim hDC&, deviceCaps88&, deviceCaps90&
hDC = GetDC(0)
deviceCaps88 = GetDeviceCaps(hDC, 88)
deviceCaps90 = GetDeviceCaps(hDC, 90)
MyForm.Left = ActiveWindow.PointsToScreenPixelsX(MyRange.Left * deviceCaps88 / 72) / deviceCaps88 * 72
MyForm.Top = ActiveWindow.PointsToScreenPixelsY(MyRange.Top * deviceCaps90 / 72) / deviceCaps90 * 72
ReleaseDC 0, hDC
End Sub
Vậy cho hỏi, bạn làm sao để cho Form nó Show được vậy bạn?
 
Upvote 0
Vậy cho hỏi, bạn làm sao để cho Form nó Show được vậy bạn?

gọi nó trong sự kiện UserForm_Activate.
lưu ý: form này phải khai báo ShowModal = False

@nghĩa: hàm này chắc chắn dùng được vì tui trích từ trong chương trình đang chạy tốt. hàm này liên quan đến việc định vị, còn để nó show thì phải gọi form.show ở đâu đó.
 
Lần chỉnh sửa cuối:
Upvote 0
@all: bài này có lâu rồi, "khai quật" lại cho những ai chưa biết.

tui thường dùng hàm LocateForm dưới đây để đặt 1 userform vào vị trí 1 cell nào đó, hàm này gọn hơn, tốc độ cao hơn vì tránh được 2 vòng for

(declare getdc, getdevicecaps, releasedc như các bài trên)

Sub LocateForm(MyForm As Object, MyRange As Range)
Dim hDC&, deviceCaps88&, deviceCaps90&
hDC = GetDC(0)
deviceCaps88 = GetDeviceCaps(hDC, 88)
deviceCaps90 = GetDeviceCaps(hDC, 90)
MyForm.Left = ActiveWindow.PointsToScreenPixelsX(MyRange.Left * deviceCaps88 / 72) / deviceCaps88 * 72
MyForm.Top = ActiveWindow.PointsToScreenPixelsY(MyRange.Top * deviceCaps90 / 72) / deviceCaps90 * 72
ReleaseDC 0, hDC
End Sub

Thật ra còn có 1 cách đơn giản hơn nhiều
Nếu ta quyết định dùng 1 UserForm nào đó và nó chỉ Show trong sự kiện SelectionChange (Chọn cell, gọi Form để hổ trợ nhập liệu.. vân... vân...)... Lúc này ta có thể dùng Frame thay cho UserForm
Xem file ví dụ! Bảo đảm code ngắn gọn và chạy mượt
(Đương nhiên, không phải lúc nào chúng ta cũng dùng Frame thay cho UserForm <--- Tùy trường hợp cụ thể)
 

File đính kèm

Upvote 0
Thật ra còn có 1 cách đơn giản hơn nhiều
Nếu ta quyết định dùng 1 UserForm nào đó và nó chỉ Show trong sự kiện SelectionChange (Chọn cell, gọi Form để hổ trợ nhập liệu.. vân... vân...)... Lúc này ta có thể dùng Frame thay cho UserForm
Xem file ví dụ! Bảo đảm code ngắn gọn và chạy mượt
(Đương nhiên, không phải lúc nào chúng ta cũng dùng Frame thay cho UserForm <--- Tùy trường hợp cụ thể)

@ndu: tui thường làm với addin, các form do addin tạo ra và làm việc với workbook khác (kể cả không chứa code) và vì không muốn add bất kỳ object hay đoạn mã nào (vô workbook của người khác) nên không xài frame thay cho form được.
@all: cái nào tiện thì dùng thôi.
 
Upvote 0
@ndu: tui thường làm với addin, các form do addin tạo ra và làm việc với workbook khác (kể cả không chứa code) và vì không muốn add bất kỳ object hay đoạn mã nào (vô workbook của người khác) nên không xài frame thay cho form được.
@all: cái nào tiện thì dùng thôi.

Vâng! Thì mình cũng đã nói ở trên: TÙY TRƯỜNG HỢP mà
Chỉ là thêm 1 lựa chọn thôi (món này rất ít người dùng, biết đâu cũng có ngày cần đến)
 
Upvote 0

Bài viết mới nhất

Back
Top Bottom