Tạo popup khi kích vào một ô (1 người xem)

Liên hệ QC

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

cauthungoaihang

Thành viên mới
Tham gia
18/4/09
Bài viết
6
Được thích
1
Em đang muốn làm một sheet tổng, ở sheet này khi kích vào mộ ô nào đó sẽ hiện lên một cái bảng, mà cái bảng này đang ở sheet bên kia. (Ý em là hiện lên như một cái popup, chứ không phải là Hyperlink sang sheet bên kia).

Chi tiết em ghi trong file đính kèm, bác nào có cao kiến thì hướng dẫn em với, em cảm ơn nhiều.+-+-+-+
 

File đính kèm

Em đang muốn làm một sheet tổng, ở sheet này khi kích vào mộ ô nào đó sẽ hiện lên một cái bảng, mà cái bảng này đang ở sheet bên kia. (Ý em là hiện lên như một cái popup, chứ không phải là Hyperlink sang sheet bên kia).

Chi tiết em ghi trong file đính kèm, bác nào có cao kiến thì hướng dẫn em với, em cảm ơn nhiều.+-+-+-+

Đòi hỏi nghe chừng rất đơn giản mà làm được chắc cũng.. sặc máu
 
Có thể làm một cái form trong đó có nhúng một cái SheetSpread vào trong đó, ẩn hiện khi Selection, hic hic, căng quá!

Ý mình nói là là GIỐNG Y CHANG như cái giao diện mà tác giả yêu cầu ấy chứ (còn nếu là UserForm thì đâu nói làm gì)
Mặc khác: Nếu dùng SpreadSheet lại thêm vấn đề máy có control này mà máy lại không có ---> Thêm cái rắc rối nữa
 
to ndu:
mình cứ làm đại đi a, càng giống càng tốt --=0

to HTN:
bác là chuyên gia về Form mà, sao đành bó tay mấy vụ này :-=

to cauthungoaihang:
bạn mở file, chạy Macro và chọn vào các ô D4, D5 để xem kết quả.

'- - -
đã chỉnh lại form cho thật giống với đề bài :-=
no bar.png

Link: https://www.mediafire.com/?hqahee5x92dkhwi
 
Lần chỉnh sửa cuối:
Ý mình nói là là GIỐNG Y CHANG như cái giao diện mà tác giả yêu cầu ấy chứ (còn nếu là UserForm thì đâu nói làm gì)
Mặc khác: Nếu dùng SpreadSheet lại thêm vấn đề máy có control này mà máy lại không có ---> Thêm cái rắc rối nữa
Thực tế là ngay cả ListView của bài này:

to ndu:
mình cứ làm đại đi a, càng giống càng tốt --=0

to HTN:
bác là chuyên gia về Form mà, sao đành bó tay mấy vụ này :-=

to cauthungoaihang:
bạn mở file, chạy Macro và chọn vào các ô D4, D5 để xem kết quả.

Thì em cũng không xem được! Chỉ có xài ListView "chính chủ" do máy em "sản xuất" ra mà thôi! Chán thiệt đó.

to HTN:
bác là chuyên gia về Form mà, sao đành bó tay mấy vụ này :-=

Làm cái na ná thì OK, còn làm giống như thế thì e rằng y như lời Thầy NDU nói.

Với cái này thì Access làm việc một cách dễ dàng (kiểu Main Form, Sub Form).
 
Có thể làm một cái form trong đó có nhúng một cái SheetSpread vào trong đó, ẩn hiện khi Selection, hic hic, căng quá!
Bỏ qua cái vụ thanh cuộn kia thì em nghĩ đến một phương án cũng khá đơn giản: CopyPicture kết hợp với SelectionChange.
Code cho việc này có lẽ cũng không đến nỗi nào (nhưng mà em lười quá, với lại cũng đang bận, nên...)
 
Bỏ qua cái vụ thanh cuộn kia thì em nghĩ đến một phương án cũng khá đơn giản: CopyPicture kết hợp với SelectionChange.
Code cho việc này có lẽ cũng không đến nỗi nào (nhưng mà em lười quá, với lại cũng đang bận, nên...)
Ngay từ đầu anh đã nghĩ đến phương án này, nhưng lại tự hỏi liệu tác giả có tác động gì trên cái "popup" này vào sheet hay không nên không tính tới nó.
 
cho e hỏi nguyên nhân a và bác HTN ko mở được file trên, và cách khắc phục ntn được ko ah?

Tôi không nghiên cứu nên không phán nguyên nhân do đâu. Nhưng nếu bị gí dao vào cổ bắt đoán mò thì tôi sẽ phán: do nguyên nhân nào đó, rất có thể là do thư viện mà bạn dùng (Microsoft Windows Common Controls MSCOMCTL.OCX) có phiên bản khác với phiên bản có trên máy tôi.

Bất luận nguyên nhân là gì nếu tôi đã nhận được của đối tác, ở đây là bạn, thì tôi xử lý như sau. Khi tôi hiển thị Form trong VBA thì không có ListView. Tôi chọn Tools --> References --> bỏ chọn chỗ MISSING --> OK --> lưu lại file (save) --> chọn ListView trên Toolbox và đặt lên Form --> thiết lập lại Font. Xong

Tất nhiên như thế thì rách việc quá. Đây chỉ là xử lý tình huống đã rồi mà thôi. Chỉ cùng lắm không xử lý được mới phải "mắng" đối tác thôi.

Tôi mở ra thì có lỗi (do MISSING) và tôi đẫ xử lý như trên. Gọi là chữa cháy, xử lý sự cố đã cháy rồi
 
Lần chỉnh sửa cuối:
Để làm được vậy tôi phải vận dụng nhiều thủ tục của các cao thủ cho bài viết này:

1) Module: mdlPictureExtract (Insert Picture):

Mã:
Option Explicit
''******************************************************************************************************************************
''[COLOR=#ff8c00][B]Written by ndu96081631 - GiaiPhapExcel.com[/B][/COLOR]
''******************************************************************************************************************************
Private Declare Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Integer) As Long
Private Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetClipboardData Lib "user32" (ByVal wFormat As Integer) As Long
Private Declare Function CloseClipboard Lib "user32" () As Long
Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (PicDesc As uPicDesc, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
Private Declare Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As Long, ByVal lpszFile As String) As Long
Private Declare Function CopyImage Lib "user32" (ByVal handle As Long, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As Long
''******************************************************************************************************************************
Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type
''******************************************************************************************************************************
Private Type uPicDesc
    Size As Long
    Type As Long
    hPic As Long
    hPal As Long
End Type
''******************************************************************************************************************************


Function PictureFromObject(ByVal Target As Object, Optional ByVal bType As Boolean = True) As IPictureDisp
    Dim hPtr As Long, hCopy As Long, PicType As Long
    Const CF_BITMAP = 2
    Const CF_PALETTE = 9
    Const CF_ENHMETAFILE = 14
    Const IMAGE_BITMAP = 0
    Const LR_COPYRETURNORG = &H4
    Const PicType_BITMAP = 1
    Const PicType_ENHMETAFILE = 4
    
    Target.CopyPicture , IIf(bType, xlBitmap, xlPicture)
    PicType = IIf(IsClipboardFormatAvailable(CF_BITMAP) <> 0, CF_BITMAP, CF_ENHMETAFILE)
    
    If IsClipboardFormatAvailable(PicType) <> 0 Then
        If OpenClipboard(0) > 0 Then
            hPtr = GetClipboardData(PicType)
            If PicType = CF_BITMAP Then
                hCopy = CopyImage(hPtr, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)
            Else
                hCopy = CopyEnhMetaFile(hPtr, vbNullString)
            End If
            CloseClipboard
            If hPtr <> 0 Then
                Dim uPicInfo As uPicDesc, IID_IDispatch As GUID, IPic As IPictureDisp
                With IID_IDispatch
                    .Data1 = &H7BF80980
                    .Data2 = &HBF32
                    .Data3 = &H101A
                    .Data4(0) = &H8B
                    .Data4(1) = &HBB
                    .Data4(2) = &H0
                    .Data4(3) = &HAA
                    .Data4(4) = &H0
                    .Data4(5) = &H30
                    .Data4(6) = &HC
                    .Data4(7) = &HAB
                End With
                With uPicInfo
                    .Size = Len(uPicInfo)
                    .Type = IIf(PicType = CF_BITMAP, PicType_BITMAP, PicType_ENHMETAFILE)
                    .hPic = hCopy
                End With
                OleCreatePictureIndirect uPicInfo, IID_IDispatch, True, IPic
                Set PictureFromObject = IPic
            End If
        End If
    End If
End Function


2) Module: mdlFormPosition (tính vị trí của cell):

Mã:
Option Explicit
''******************************************************************************************************************************
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
''******************************************************************************************************************************


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

3) Module: mdlMainCode (thủ tục chính):

Mã:
Option Explicit
''******************************************************************************************************************************
Public pub_Sum As Long
Public pub_Picture As String
Public pub_Height As Double, pub_Width As Double
''******************************************************************************************************************************
Public pub_SumGD As Long, pub_SumDT As Long
Public pub_Height_GD As Double, pub_Width_GD As Double
Public pub_Height_DT As Double, pub_Width_DT As Double
''******************************************************************************************************************************


Sub CreatePicture()
    Dim lRow As Long
    On Error Resume Next
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    Application.DisplayAlerts = False
    Sheets("PictInsert").Visible = xlSheetVisible
    Sheets("PictInsert").Delete
    Application.DisplayAlerts = True
    Sheets.Add
    ActiveSheet.Name = "PictInsert"
    Sheets("PictInsert").Visible = xlSheetVeryHidden
    lRow = GiaDung.Range("B" & Rows.Count).End(xlUp).Row
    pub_SumGD = GiaDung.Range("D" & lRow).Value
    With GiaDung.Range("B3:D" & lRow)
        pub_Height_GD = .Height
        pub_Width_GD = .Width
        .Copy
    End With
    Sheets("PictInsert").Pictures.Paste(Link:=True).Name = "GiaDung"
    
    lRow = DienTu.Range("B" & Rows.Count).End(xlUp).Row
    pub_SumDT = DienTu.Range("D" & lRow).Value
    With DienTu.Range("B3:D" & lRow)
        pub_Height_DT = .Height
        pub_Width_DT = .Width
        .Copy
    End With
    Sheets("PictInsert").Pictures.Paste(Link:=True).Name = "DienTu"
    Application.CutCopyMode = False
    Application.EnableEvents = True
    Application.ScreenUpdating = True
End Sub


Sub PictureSetting()
    Dim PictIns As IPictureDisp, PictExt As Shape
    Set PictExt = Sheets("PictInsert").Shapes(pub_Picture)
    If Not PictExt Is Nothing Then
        Set PictIns = PictureFromObject(PictExt)
        Dim FormHeight As Single, FormWidth As Single
        FormHeight = 200
        FormWidth = 240
        With usfPopup
            .Height = FormHeight
            .Width = FormWidth
            .ScrollLeft = 0
            .ScrollTop = 0
            .ScrollBars = fmScrollBarsNone
            .Picture = PictIns
            If pub_Width > FormWidth And pub_Height > FormHeight Then
                .ScrollBars = fmScrollBarsBoth
                .ScrollHeight = pub_Height
                .ScrollWidth = pub_Width
            ElseIf pub_Width > FormWidth And pub_Height < FormHeight Then
                .ScrollBars = fmScrollBarsHorizontal
                .ScrollWidth = pub_Width
                .Height = pub_Height
            ElseIf pub_Width < FormWidth And pub_Height > FormHeight Then
                .ScrollBars = fmScrollBarsVertical
                .ScrollHeight = pub_Height
                .Width = pub_Width
            Else
                .Height = pub_Height
                .Width = pub_Width
            End If
        End With
    End If
End Sub

4) Sheet Module: Summary (chạy các sự kiện trên sheet):

Mã:
[/B][/U]Option Explicit


Private Sub Worksheet_Activate()
    Call CreatePicture
End Sub


Private Sub Worksheet_Deactivate()
    On Error Resume Next
    Unload usfPopup
    Application.ScreenUpdating = False
    Application.DisplayAlerts = False
    Sheets("PictInsert").Visible = xlSheetVisible
    Sheets("PictInsert").Delete
    pub_Width_GD = 0
    Application.DisplayAlerts = True
    Application.ScreenUpdating = True
End Sub


Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Application.EnableEvents = False
    If pub_Width_GD = 0 Then Call CreatePicture
    usfPopup.Hide
    If Target.Address = "$D$4" Then
        pub_Picture = "GiaDung"
        pub_Height = pub_Height_GD + 12
        pub_Width = pub_Width_GD + 12
        pub_Sum = pub_SumGD
        Call PictureSetting
        usfPopup.Show
    ElseIf Target.Address = "$D$5" Then
        pub_Picture = "DienTu"
        pub_Height = pub_Height_DT + 12
        pub_Width = pub_Width_DT + 12
        pub_Sum = pub_SumDT
        Call PictureSetting
        usfPopup.Show
    End If
    Application.EnableEvents = True
End Sub
[U][B]


5) UserForm Module: usfPopup (chạy các sự kiện trên Form):

Mã:
[/B][/U]Option Explicit
''******************************************************************************************************************************
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" ( _
        ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" ( _
        ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
        ByVal lpClassName As String, ByVal lpWindowName As String) As Long
''******************************************************************************************************************************
Const WS_BORDER = &H800000
Const WS_CAPTION = &HC00000
Const WS_THICKFRAME = &H40000
Const WS_DLGFRAME = &H400000
Const WS_EX_DLGMODALFRAME = &H1&
Const GWL_STYLE = (-16)
Const GWL_EXSTYLE = (-20)
''******************************************************************************************************************************


Private Sub UserForm_Activate()
    LocateForm Me, Selection
End Sub


Private Sub UserForm_Click()
    If pub_Picture = "GiaDung" Then
        Summary.Range("D4") = pub_Sum
    Else
        Summary.Range("D5") = pub_Sum
    End If
    Unload Me
End Sub


Private Sub UserForm_Initialize()
    Dim TitleHeight As Double, hwnd&
    TitleHeight = Height - InsideHeight
    hwnd = FindWindow("ThunderDFrame", Caption)    'XL2000
    SetWindowLong hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) _
    And Not (WS_BORDER Or WS_CAPTION Or WS_THICKFRAME Or WS_DLGFRAME)
    SetWindowLong hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) _
    And Not WS_EX_DLGMODALFRAME
    Height = Height - TitleHeight
End Sub
''******************************************************************************************************************************
Private Sub UserForm_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    Me.Hide
End Sub
[U][B]

;;;;;;;;;;;Thiệt là cực khổ quá đi đó hả!!! +-+-+-+
 

File đính kèm

Để làm được vậy tôi phải vận dụng nhiều thủ tục của các cao thủ cho bài viết này:

4) Sheet Module: Summary (chạy các sự kiện trên sheet):

Mã:
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Application.EnableEvents = False
    If pub_Width_GD = 0 Then Call CreatePicture
    usfPopup.Hide
    If Target.Address = "$D$4" Then
        pub_Picture = "GiaDung"
        pub_Height = pub_Height_GD [COLOR=#0000cd][B]+ 12[/B][/COLOR]
        pub_Width = pub_Width_GD [B][COLOR=#0000cd]+ 12[/COLOR][/B]
        pub_Sum = pub_SumGD
        Call PictureSetting
        usfPopup.Show
    ElseIf Target.Address = "$D$5" Then
        pub_Picture = "DienTu"
        pub_Height = pub_Height_DT[COLOR=#0000cd] [B]+ 12[/B][/COLOR]
        pub_Width = pub_Width_DT [B][COLOR=#0000cd]+ 12[/COLOR][/B]
        pub_Sum = pub_SumDT
        Call PictureSetting
        usfPopup.Show
    End If
    Application.EnableEvents = True
End Sub
;;;;;;;;;;;Thiệt là cực khổ quá đi đó hả!!! +-+-+-+

Với số + 12 của sự kiện trên, bạn có thể sẽ không thể thấy hết toàn bộ hình ảnh trên một số máy, thôi thì tăng "số hiệu chỉnh" là 125% đi cho chắc ăn:

Mã:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Application.EnableEvents = False
    If pub_Width_GD = 0 Then Call CreatePicture
    usfPopup.Hide
    If Target.Address = "$D$4" Then
        pub_Picture = "GiaDung"
        pub_Height = pub_Height_GD[COLOR=#0000cd][B] * 1.25[/B][/COLOR]
        pub_Width = pub_Width_GD [COLOR=#0000cd][B]* 1.25[/B][/COLOR]
        pub_Sum = pub_SumGD
        Call PictureSetting
        usfPopup.Show
    ElseIf Target.Address = "$D$5" Then
        pub_Picture = "DienTu"
        pub_Height = pub_Height_DT [B][COLOR=#0000cd]* 1.25[/COLOR][/B]
        pub_Width = pub_Width_DT [COLOR=#0000cd][B]* 1.25[/B][/COLOR]
        pub_Sum = pub_SumDT
        Call PictureSetting
        usfPopup.Show
    End If
    Application.EnableEvents = True
End Sub
 
Để làm được vậy tôi phải vận dụng nhiều thủ tục của các cao thủ cho bài viết này:

1) Module: mdlPictureExtract (Insert Picture):

Mã:
Option Explicit
''******************************************************************************************************************************
''[COLOR=#ff8c00][B]Written by ndu96081631 - GiaiPhapExcel.com[/B][/COLOR]
''******************************************************************************************************************************
Private Declare Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Integer) As Long
Private Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetClipboardData Lib "user32" (ByVal wFormat As Integer) As Long
Private Declare Function CloseClipboard Lib "user32" () As Long
Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (PicDesc As uPicDesc, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
Private Declare Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As Long, ByVal lpszFile As String) As Long
Private Declare Function CopyImage Lib "user32" (ByVal handle As Long, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As Long
''******************************************************************************************************************************
Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type
''******************************************************************************************************************************
Private Type uPicDesc
    Size As Long
    Type As Long
    hPic As Long
    hPal As Long
End Type
''******************************************************************************************************************************


Function PictureFromObject(ByVal Target As Object, Optional ByVal bType As Boolean = True) As IPictureDisp
    Dim hPtr As Long, hCopy As Long, PicType As Long
    Const CF_BITMAP = 2
    Const CF_PALETTE = 9
    Const CF_ENHMETAFILE = 14
    Const IMAGE_BITMAP = 0
    Const LR_COPYRETURNORG = &H4
    Const PicType_BITMAP = 1
    Const PicType_ENHMETAFILE = 4
    
    Target.CopyPicture , IIf(bType, xlBitmap, xlPicture)
    PicType = IIf(IsClipboardFormatAvailable(CF_BITMAP) <> 0, CF_BITMAP, CF_ENHMETAFILE)
    
    If IsClipboardFormatAvailable(PicType) <> 0 Then
        If OpenClipboard(0) > 0 Then
            hPtr = GetClipboardData(PicType)
            If PicType = CF_BITMAP Then
                hCopy = CopyImage(hPtr, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)
            Else
                hCopy = CopyEnhMetaFile(hPtr, vbNullString)
            End If
            CloseClipboard
            If hPtr <> 0 Then
                Dim uPicInfo As uPicDesc, IID_IDispatch As GUID, IPic As IPictureDisp
                With IID_IDispatch
                    .Data1 = &H7BF80980
                    .Data2 = &HBF32
                    .Data3 = &H101A
                    .Data4(0) = &H8B
                    .Data4(1) = &HBB
                    .Data4(2) = &H0
                    .Data4(3) = &HAA
                    .Data4(4) = &H0
                    .Data4(5) = &H30
                    .Data4(6) = &HC
                    .Data4(7) = &HAB
                End With
                With uPicInfo
                    .Size = Len(uPicInfo)
                    .Type = IIf(PicType = CF_BITMAP, PicType_BITMAP, PicType_ENHMETAFILE)
                    .hPic = hCopy
                End With
                OleCreatePictureIndirect uPicInfo, IID_IDispatch, True, IPic
                Set PictureFromObject = IPic
            End If
        End If
    End If
End Function


2) Module: mdlFormPosition (tính vị trí của cell):

Mã:
Option Explicit
''******************************************************************************************************************************
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
''******************************************************************************************************************************


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

3) Module: mdlMainCode (thủ tục chính):

Mã:
Option Explicit
''******************************************************************************************************************************
Public pub_Sum As Long
Public pub_Picture As String
Public pub_Height As Double, pub_Width As Double
''******************************************************************************************************************************
Public pub_SumGD As Long, pub_SumDT As Long
Public pub_Height_GD As Double, pub_Width_GD As Double
Public pub_Height_DT As Double, pub_Width_DT As Double
''******************************************************************************************************************************


Sub CreatePicture()
    Dim lRow As Long
    On Error Resume Next
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    Application.DisplayAlerts = False
    Sheets("PictInsert").Visible = xlSheetVisible
    Sheets("PictInsert").Delete
    Application.DisplayAlerts = True
    Sheets.Add
    ActiveSheet.Name = "PictInsert"
    Sheets("PictInsert").Visible = xlSheetVeryHidden
    lRow = GiaDung.Range("B" & Rows.Count).End(xlUp).Row
    pub_SumGD = GiaDung.Range("D" & lRow).Value
    With GiaDung.Range("B3:D" & lRow)
        pub_Height_GD = .Height
        pub_Width_GD = .Width
        .Copy
    End With
    Sheets("PictInsert").Pictures.Paste(Link:=True).Name = "GiaDung"
    
    lRow = DienTu.Range("B" & Rows.Count).End(xlUp).Row
    pub_SumDT = DienTu.Range("D" & lRow).Value
    With DienTu.Range("B3:D" & lRow)
        pub_Height_DT = .Height
        pub_Width_DT = .Width
        .Copy
    End With
    Sheets("PictInsert").Pictures.Paste(Link:=True).Name = "DienTu"
    Application.CutCopyMode = False
    Application.EnableEvents = True
    Application.ScreenUpdating = True
End Sub


Sub PictureSetting()
    Dim PictIns As IPictureDisp, PictExt As Shape
    Set PictExt = Sheets("PictInsert").Shapes(pub_Picture)
    If Not PictExt Is Nothing Then
        Set PictIns = PictureFromObject(PictExt)
        Dim FormHeight As Single, FormWidth As Single
        FormHeight = 200
        FormWidth = 240
        With usfPopup
            .Height = FormHeight
            .Width = FormWidth
            .ScrollLeft = 0
            .ScrollTop = 0
            .ScrollBars = fmScrollBarsNone
            .Picture = PictIns
            If pub_Width > FormWidth And pub_Height > FormHeight Then
                .ScrollBars = fmScrollBarsBoth
                .ScrollHeight = pub_Height
                .ScrollWidth = pub_Width
            ElseIf pub_Width > FormWidth And pub_Height < FormHeight Then
                .ScrollBars = fmScrollBarsHorizontal
                .ScrollWidth = pub_Width
                .Height = pub_Height
            ElseIf pub_Width < FormWidth And pub_Height > FormHeight Then
                .ScrollBars = fmScrollBarsVertical
                .ScrollHeight = pub_Height
                .Width = pub_Width
            Else
                .Height = pub_Height
                .Width = pub_Width
            End If
        End With
    End If
End Sub

4) Sheet Module: Summary (chạy các sự kiện trên sheet):

Mã:
Mã:
Option Explicit


Private Sub Worksheet_Activate()
    Call CreatePicture
End Sub


Private Sub Worksheet_Deactivate()
    On Error Resume Next
    Unload usfPopup
    Application.ScreenUpdating = False
    Application.DisplayAlerts = False
    Sheets("PictInsert").Visible = xlSheetVisible
    Sheets("PictInsert").Delete
    pub_Width_GD = 0
    Application.DisplayAlerts = True
    Application.ScreenUpdating = True
End Sub


Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Application.EnableEvents = False
    If pub_Width_GD = 0 Then Call CreatePicture
    usfPopup.Hide
    If Target.Address = "$D$4" Then
        pub_Picture = "GiaDung"
        pub_Height = pub_Height_GD + 12
        pub_Width = pub_Width_GD + 12
        pub_Sum = pub_SumGD
        Call PictureSetting
        usfPopup.Show
    ElseIf Target.Address = "$D$5" Then
        pub_Picture = "DienTu"
        pub_Height = pub_Height_DT + 12
        pub_Width = pub_Width_DT + 12
        pub_Sum = pub_SumDT
        Call PictureSetting
        usfPopup.Show
    End If
    Application.EnableEvents = True
End Sub
5) UserForm Module: usfPopup (chạy các sự kiện trên Form):

Mã:
Mã:
Option Explicit
''******************************************************************************************************************************
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" ( _
        ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" ( _
        ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
        ByVal lpClassName As String, ByVal lpWindowName As String) As Long
''******************************************************************************************************************************
Const WS_BORDER = &H800000
Const WS_CAPTION = &HC00000
Const WS_THICKFRAME = &H40000
Const WS_DLGFRAME = &H400000
Const WS_EX_DLGMODALFRAME = &H1&
Const GWL_STYLE = (-16)
Const GWL_EXSTYLE = (-20)
''******************************************************************************************************************************


Private Sub UserForm_Activate()
    LocateForm Me, Selection
End Sub


Private Sub UserForm_Click()
    If pub_Picture = "GiaDung" Then
        Summary.Range("D4") = pub_Sum
    Else
        Summary.Range("D5") = pub_Sum
    End If
    Unload Me
End Sub


Private Sub UserForm_Initialize()
    Dim TitleHeight As Double, hwnd&
    TitleHeight = Height - InsideHeight
    hwnd = FindWindow("ThunderDFrame", Caption)    'XL2000
    SetWindowLong hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) _
    And Not (WS_BORDER Or WS_CAPTION Or WS_THICKFRAME Or WS_DLGFRAME)
    SetWindowLong hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) _
    And Not WS_EX_DLGMODALFRAME
    Height = Height - TitleHeight
End Sub
''******************************************************************************************************************************
Private Sub UserForm_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    Me.Hide
End Sub
;;;;;;;;;;;Thiệt là cực khổ quá đi đó hả!!! +-+-+-+

Sao bạn lại khổ thế hả bạn?

Bạn đi vòng vèo nên mệt là phải rồi. Không mệt mới là chuyện lạ.

Bạn làm: copy range vào clipboard --> tạo sheet ẩn --> tạo trên sheet ẩn object Picture lấy từ clipboard --> sau đó lại copy cái Picture (shape) ngược trở lại vào clipboard --> lấy từ clipboard ra để tạo object IPictureDisp --> "nhồi vào UserForm

Toàn bộ khâu nhiều việc đỏ đỏ là không cần thiết

code của tôi

sheet
Mã:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim lastRow As Long, picHandle As Long
    If Target.Address = "$D$4" Then
        lastRow = GiaDung.Range("B" & Rows.Count).End(xlUp).Row
        picHandle = RangeToHBITMAP(GiaDung.Range("B3:D" & lastRow))
        UserForm1.Picture = HBITMAPToIPicture(picHandle)
        UserForm1.Show
    ElseIf Target.Address = "$D$5" Then
        lastRow = DienTu.Range("B" & Rows.Count).End(xlUp).Row
        picHandle = RangeToHBITMAP(DienTu.Range("B3:D" & lastRow))
        UserForm1.Picture = HBITMAPToIPicture(picHandle)
        UserForm1.Show
    End If
End Sub

modPicture
Mã:
Const PICTYPE_BITMAP = 1
Const CF_BITMAP = 2
Const IMAGE_BITMAP = 0
Const LR_COPYRETURNORG = &H4

' GUID
Private Type GUID
    D1 As Long
    D2 As Integer
    D3 As Integer
    D4(0 To 7) As Byte
End Type

Private Type PictDesc
    cbSizeofStruct As Long
    picType As Long
    hImage As Long
    hPal As Long
End Type

Private Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetClipboardData Lib "user32" (ByVal wFormat As Long) As Long
Private Declare Function CloseClipboard Lib "user32" () As Long

Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (lpPictDesc As PictDesc, riid As GUID, ByVal fOwn As Long, lplpvObj As Any) As Long
Private Declare Function CopyImage Lib "user32" (ByVal handle As Long, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As Long

Function RangeToHBITMAP(rng As Range) As Long
Dim hRes As Long
    rng.Copy
    OpenClipboard 0
    
    hRes = GetClipboardData(CF_BITMAP)
    If hRes <> 0 Then RangeToHBITMAP = CopyImage(hRes, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)
    CloseClipboard
    Application.CutCopyMode = False
End Function

Function HBITMAPToIPicture(ByVal hbmp As Long) As IPictureDisp
Dim lpPictDesc As PictDesc, riid As GUID, pic As IPictureDisp
    If hbmp <> 0 Then
'        GUID cua Interface IPicture - {7BF80980-BF32-101A-8BBB-00AA00300CAB}
        With riid
            .D1 = &H7BF80980
            .D2 = &HBF32
            .D3 = &H101A
            .D4(0) = &H8B
            .D4(1) = &HBB
            .D4(2) = &H0
            .D4(3) = &HAA
            .D4(4) = &H0
            .D4(5) = &H30
            .D4(6) = &HC
            .D4(7) = &HAB
        End With
        With lpPictDesc
            .cbSizeofStruct = Len(lpPictDesc)
            .picType = PICTYPE_BITMAP  
            .hImage = hbmp
            .hPal = 0
        End With
        If OleCreatePictureIndirect(lpPictDesc, riid, True, pic) = 0 Then
            Set HBITMAPToIPicture = pic
        End If
    End If
End Function

UserForm
Mã:
Private Sub UserForm_Click()
    Unload Me
End Sub

Tất nhiên tôi không làm: bỏ thanh tiêu đề của Form, xác định kích thước, ̣định vị Form, thêm thanh trượt.
Không làm vì chỉ muốn nhấn mạnh cách tạo Picture mà thôi.
 

File đính kèm

Cảm ơn các bác rất nhiều, đặc biệt là 2 bác Hoàng Trọng Nghĩa và phucbugis đã bỏ thời gian và công sức làm cho em 2 cái file rất pro ^_^

Ở máy của em thì Form của bác phucbugis hoạt động bình thường. Có điều là các Code trong này hơi "cao siêu" so với trình của em **~**, chắc phải ít thời gian nữa mới lĩnh hội được.

Các bác cho em hỏi là 2 cách làm này về cơ bản khác nhau như thế nào ạ? Em đang phân vân không biết nên làm theo cái nào.
 
Sao bạn lại khổ thế hả bạn?
....
Tất nhiên tôi không làm: bỏ thanh tiêu đề của Form, xác định kích thước, ̣định vị Form, thêm thanh trượt.
Không làm vì chỉ muốn nhấn mạnh cách tạo Picture mà thôi.

xin bác xem lại, khi e chèn thêm hàng bên sheet Giadung thì popup nó ra thế này
siwtom.png
 
Sao bạn lại khổ thế hả bạn?

Bạn đi vòng vèo nên mệt là phải rồi. Không mệt mới là chuyện lạ.

Bạn làm: copy range vào clipboard --> tạo sheet ẩn --> tạo trên sheet ẩn object Picture lấy từ clipboard --> sau đó lại copy cái Picture (shape) ngược trở lại vào clipboard --> lấy từ clipboard ra để tạo object IPictureDisp --> "nhồi vào UserForm

Toàn bộ khâu nhiều việc đỏ đỏ là không cần thiết

code của tôi

sheet
Mã:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim lastRow As Long, picHandle As Long
    If Target.Address = "$D$4" Then
        lastRow = GiaDung.Range("B" & Rows.Count).End(xlUp).Row
        picHandle = RangeToHBITMAP(GiaDung.Range("B3:D" & lastRow))
        UserForm1.Picture = HBITMAPToIPicture(picHandle)
        UserForm1.Show
    ElseIf Target.Address = "$D$5" Then
        lastRow = DienTu.Range("B" & Rows.Count).End(xlUp).Row
        picHandle = RangeToHBITMAP(DienTu.Range("B3:D" & lastRow))
        UserForm1.Picture = HBITMAPToIPicture(picHandle)
        UserForm1.Show
    End If
End Sub

modPicture
Mã:
Const PICTYPE_BITMAP = 1
Const CF_BITMAP = 2
Const IMAGE_BITMAP = 0
Const LR_COPYRETURNORG = &H4

' GUID
Private Type GUID
    D1 As Long
    D2 As Integer
    D3 As Integer
    D4(0 To 7) As Byte
End Type

Private Type PictDesc
    cbSizeofStruct As Long
    picType As Long
    hImage As Long
    hPal As Long
End Type

Private Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetClipboardData Lib "user32" (ByVal wFormat As Long) As Long
Private Declare Function CloseClipboard Lib "user32" () As Long

Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (lpPictDesc As PictDesc, riid As GUID, ByVal fOwn As Long, lplpvObj As Any) As Long
Private Declare Function CopyImage Lib "user32" (ByVal handle As Long, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As Long

Function RangeToHBITMAP(rng As Range) As Long
Dim hRes As Long
    rng.Copy
    OpenClipboard 0
    
    hRes = GetClipboardData(CF_BITMAP)
    If hRes <> 0 Then RangeToHBITMAP = CopyImage(hRes, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)
    CloseClipboard
    Application.CutCopyMode = False
End Function

Function HBITMAPToIPicture(ByVal hbmp As Long) As IPictureDisp
Dim lpPictDesc As PictDesc, riid As GUID, pic As IPictureDisp
    If hbmp <> 0 Then
'        GUID cua Interface IPicture - {7BF80980-BF32-101A-8BBB-00AA00300CAB}
        With riid
            .D1 = &H7BF80980
            .D2 = &HBF32
            .D3 = &H101A
            .D4(0) = &H8B
            .D4(1) = &HBB
            .D4(2) = &H0
            .D4(3) = &HAA
            .D4(4) = &H0
            .D4(5) = &H30
            .D4(6) = &HC
            .D4(7) = &HAB
        End With
        With lpPictDesc
            .cbSizeofStruct = Len(lpPictDesc)
            .picType = PICTYPE_BITMAP  
            .hImage = hbmp
            .hPal = 0
        End With
        If OleCreatePictureIndirect(lpPictDesc, riid, True, pic) = 0 Then
            Set HBITMAPToIPicture = pic
        End If
    End If
End Function

UserForm
Mã:
Private Sub UserForm_Click()
    Unload Me
End Sub

Tất nhiên tôi không làm: bỏ thanh tiêu đề của Form, xác định kích thước, ̣định vị Form, thêm thanh trượt.
Không làm vì chỉ muốn nhấn mạnh cách tạo Picture mà thôi.

Vâng cám ơn Thầy vì đã hướng dẫn cách này, bởi cái thủ tục Insert Picture đó là học (nói đúng ra là sao chép toàn bộ) của Thầy NDU vì thấy nó hay quá mà em chưa thể tự mình nghiên cứu được khi phải trả lời ngay bài này ạ.

Cám ơn Thầy đã chỉ cho một hướng đi ngắn gọn.
 
Cảm ơn các bác rất nhiều, đặc biệt là 2 bác Hoàng Trọng Nghĩa và phucbugis đã bỏ thời gian và công sức làm cho em 2 cái file rất pro ^_^
Ở máy của em thì Form của bác phucbugis hoạt động bình thường. Có điều là các Code trong này hơi "cao siêu" so với trình của em **~**, chắc phải ít thời gian nữa mới lĩnh hội được.
Các bác cho em hỏi là 2 cách làm này về cơ bản khác nhau như thế nào ạ? Em đang phân vân không biết nên làm theo cái nào.

nếu đơn giản chỉ cần Popup thì bạn chọn cách làm của bác HTN, còn muốn đi xa hơn nữa (ví dụ như tìm kiếm, update, chỉnh sửa số liệu trực tiếp từ sheet SUMMARY thì nên đi theo hướng của mình --=0)
 
Lần chỉnh sửa cuối:
xin bác xem lại, khi e chèn thêm hàng bên sheet Giadung thì popup nó ra thế này
View attachment 117006
Thầy siwtom chỉ ra hướng đi cho việc Insert Picture thôi, còn những "linh tinh" khác thì tự giải quyết được bạn ơi, như đặt thuộc tính Picture trên Form, như canh đều hình ảnh, như lấy địa chỉ trên cells v.v.. thì tôi nghĩ mình tự làm được điều này.
 
Cảm ơn các bác rất nhiều, đặc biệt là 2 bác Hoàng Trọng Nghĩa và phucbugis đã bỏ thời gian và công sức làm cho em 2 cái file rất pro ^_^

Ở máy của em thì Form của bác phucbugis hoạt động bình thường. Có điều là các Code trong này hơi "cao siêu" so với trình của em **~**, chắc phải ít thời gian nữa mới lĩnh hội được.

Các bác cho em hỏi là 2 cách làm này về cơ bản khác nhau như thế nào ạ? Em đang phân vân không biết nên làm theo cái nào.
Bạn thấy cái nào thuận tiện cho mình thì cứ làm thôi, không cần câu nệ gì cả. Nếu chỉ để xem biểu mẫu và nhập số liệu tồn của mỗi biểu mẫu thì thực hiện theo cách của tôi, còn xa hơn bạn có thể sử dụng Form với ListView (tôi đã không thích "em" này nữa rồi, "em" đẹp lắm nhưng "chảnh" lắm nên không chơi nữa).
 
xin bác xem lại, khi e chèn thêm hàng bên sheet Giadung thì popup nó ra thế này
View attachment 117006

Thì tôi viết rõ mà
Trích
Tất nhiên tôi không làm: bỏ thanh tiêu đề của Form, xác định kích thước, ̣định vị Form, thêm thanh trượt.
Không làm vì chỉ muốn nhấn mạnh cách tạo Picture mà thôi.

Form có kích thước cố định không co dãn theo Range, không có thanh trượt nên với Range lớn dần thì ảnh bị cụt thôi.
Tôi đã nói là không làm các việc khác vì chỉ muốn "biểu diễn" cách tạo ảnh thôi. Tức bỏ tất cả các khâu đỏ đỏ của bạn Nghĩa.

Còn những việc tôi không làm kia thì bạn cũng chả phải tự làm vì bạn Nghĩa đã viết hết code cho các việc ấy rồi
 
Lần chỉnh sửa cuối:
Để làm được vậy tôi phải vận dụng nhiều thủ tục của các cao thủ cho bài viết này:

Mã:
Function PictureFromObject(ByVal [COLOR=#ff0000][B]Target As Object[/B][/COLOR], Optional ByVal bType As Boolean = True) As IPictureDisp

Cái chỗ màu đỏ này có thể truyền đối số là Range được mà (khỏi cần phải copy Range thành Picture cho mất công)
 
Sau khi được Thầy siwtom hướng dẫn làm trực tiếp trên form, tôi sửa lại code trở nên gọn gàng hơn, không phải lòng vòng như bài trước nữa!

Cái này mà ứng dụng khi biểu diễn đồ thị trên Form thì tuyệt vời luôn!

Xin xem file.
 

File đính kèm

Vâng cám ơn Thầy vì đã hướng dẫn cách này, bởi cái thủ tục Insert Picture đó là học (nói đúng ra là sao chép toàn bộ) của Thầy NDU vì thấy nó hay quá mà em chưa thể tự mình nghiên cứu được khi phải trả lời ngay bài này ạ.

Cám ơn Thầy đã chỉ cho một hướng đi ngắn gọn.

Nếu bạn hiểu được bản chất của mỗi vấn đề, nếu bạn hiểu được code, hiểu được system thì bạn sẽ biết cách thao tác.
Tôi thấy bạn thích "vọc" nên giải thích chút cho bạn hiểu. Nếu nói về IPictureDisp thì bạn đã biết cách tạo. Chỉ cần có bitmap handle là tạo được, bất luận bạn có bitmap handle từ đâu bằng cách nào.

Nếu nói về Picture (interface IPictureDisp) thì ta xét một đoạn của Function PictureFromObject

[GPECODE=vb]
If hPtr <> 0 Then
Dim uPicInfo As uPicDesc, IID_IDispatch As GUID, IPic As IPictureDisp
With IID_IDispatch
.Data1 = &H7BF80980
.Data2 = &HBF32
.Data3 = &H101A
.Data4(0) = &H8B
.Data4(1) = &HBB
.Data4(2) = &H0
.Data4(3) = &HAA
.Data4(4) = &H0
.Data4(5) = &H30
.Data4(6) = &HC
.Data4(7) = &HAB
End With
With uPicInfo
.Size = Len(uPicInfo)
.Type = IIf(PicType = CF_BITMAP, PicType_BITMAP, PicType_ENHMETAFILE)
.hPic = hCopy
End With
OleCreatePictureIndirect uPicInfo, IID_IDispatch, True, IPic
Set PictureFromObject = IPic
End If
[/GPECODE]

Tức muốn tạo IPic (as IPictureDisp) thì ta phải gọi hàm OleCreatePictureIndirect và truyền cấu trúc uPicInfo. Muốn thiết lập các trường của cấu trúc uPicInfo thì bạn phải có hCopy. Nếu bạn hiểu được API và hiểu được bitmap thì bạn sẽ biết hCopy chẳng qua là "bitmap handle". Thế thôi.

1. bitmap handle là gì. Trong Windows có rất nhiều "object" có cái gọi là handle. Hiểu nôm na là "số nhận dạng". Vd. bạn gọi SetWindowLong để thiết lập style cho cửa sổ (không có thanh tiêu đề) thì Windows muốn biết bạn định thao tác cho cửa sổ nào trong system vì trong system có muôn vàn cửa sổ. Vậy khi gọi hàm SetWindowLong thì bạn truyền cái gọi là "window handle" - hwnd (mà bạn có được "bằng cách nào đấy"). Có rất nhiều hàm API để thao tác với cửa sổ, bitmap, brusk, font, device context v...v mà mỗi "loại" thì có muôn vàn trong system, vậy khi gọi hàm API bạn phải truyền 1 tham số để system biết phải thao tác trên "object" cụ thể nào. Vì thế Windows "bịa" ra cái gọi là handle. Mỗi cửa sổ, bitmap, brusk, font, device context v...v được tạo ra trong RAM bởi Windows được Windows "gán" cho 1 con số gọi là handle (y như mã nhân viên vậy). Chúng là duy nhất trong system nên có thể dùng chúng để xác định được "object" đang nói tới là object nào trong RAM. Từ đó cứ gọi hàm API và truyền vào "handle" là Windows biết cần phải thao tác trên cửa sổ, bitmap, brusk, font, device context v...v nào trong muôn vàn cửa sổ, bitmap, brusk, font, device context v...v có trong system.

2. Handle "đọc" ra từ đâu? Có muôn vàn cách đọc. Ví như handle của cửa sổ bạn đọc ra bằng hàm FindWindow. Nhưng có muôn vàn cách khác, vd. EnumWindows, và nhiều cách khác nữa. Tương tự như bitmap handle. Bitmap handle là bitmap handle, nôm na là "số nhận dạng" của bitmap. Chả có liên quan gì tới clipboard cả. Tất nhiên nếu bitmap được copy vào clipboard thì có thể dùng API đọc ra bitmap handle. Nhưng có những bitmap không được copy vào clipboard. Chúng có handle nhưng làm gì có trong clipboard để mà đọc ra từ clipboard.

Tóm lại với bitmap handle cũng có muôn vàn cách đọc ra. Nếu đã được copy vào clipboard thì bạn biết cách đọc ra rồi. Nhưng cũng có muôn vàn cách đọc.

Tôi cho 2 vd.: tạo ảnh của cửa sổ Excel và tự tạo ảnh 1 hình vuông đỏ có chữ nền trong RAM rồi đọc ra bitmap handle của chúng. Sau khi có bitmap handle thì gọi OleCreatePictureIndirect để có IPictureDisp.

Trong 2 ví dụ này, hoặc nếu ta kiểm tra xem trong clipboard có bitmap không để đọc ra handle của nó (vd. do người dùng giải lao với Excel và làm việc một chút với Paint và copy ảnh nào đó vào clipboard) thì làm gì có object nào để mà truyền vào hàm PictureFromObject? Pictures, shape, Range?

module1
[GPECODE=vb]
Private Const SRCCOPY As Long = &HCC0020
Private Const TRANSPARENT As Long = 1

Private Type RECT
left As Long
top As Long
Right As Long
Bottom As Long
End Type

Private Declare Function CreateCompatibleDC Lib "gdi32.dll" (ByVal hdc As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hdc As Long) As Long
Private Declare Function GetObject Lib "gdi32.dll" Alias "GetObjectA" (ByVal hObject As Long,

ByVal nCount As Long, ByRef lpObject As Any) As Long
Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
Private Declare Function ReleaseDC Lib "user32.dll" (ByVal hwnd As Long, ByVal hdc As Long) As Long

Private Declare Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long
Private Declare Function GetWindowDC Lib "user32.dll" (ByVal hwnd As Long) As Long
Private Declare Function GetWindowRect Lib "user32.dll" (ByVal hwnd As Long, ByRef lpRect As RECT) As Long
Private Declare Function BitBlt Lib "gdi32.dll" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Declare Function CreateSolidBrush Lib "gdi32.dll" (ByVal crColor As Long) As Long
Private Declare Function Rectangle Lib "gdi32.dll" (ByVal hdc As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function FillRect Lib "user32.dll" (ByVal hdc As Long, ByRef lpRect As RECT, ByVal hBrush As Long) As Long
Private Declare Function SetRect Lib "user32.dll" (ByRef lpRect As RECT, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function TextOutW Lib "gdi32.dll" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal lpString As String, ByVal nCount As Long) As Long
Private Declare Function SetBkMode Lib "gdi32.dll" (ByVal hdc As Long, ByVal nBkMode As Long) As Long

Function HBITMAPFromWindowHandle(ByVal hwnd As Long)
Dim DC As Long, memDC As Long, hbmp As Long, w As Long, h As Long, old As Long, rc As RECT
' đọc ra device context (DC) for the entire window
DC = GetWindowDC(hwnd)
' đọc ra tọa độ của cửa sổ - hình chữ nhật trên desktop
GetWindowRect hwnd, rc
' chiều dài của cửa sổ
w = rc.Right - rc.left
' chiều cao của cửa sổ
h = rc.Bottom - rc.top
' tạo memory device context (DC) compatible với device context của cửa sổ
memDC = CreateCompatibleDC(DC)
' tạo bitmap compatible với device context của cửa sổ, có chiều dài là w và chiều rộng là h, tức có kích thước của cửa sổ
hbmp = CreateCompatibleBitmap(DC, w, h)
' chọn bitmap vừa tạo vào memory device context
old = SelectObject(memDC, hbmp)
' chuyển dữ liệu mầu từ device context của cửa sổ sang memory device context (có chứa bitmap được tạo)
BitBlt memDC, 0, 0, w, h, DC, 0, 0, SRCCOPY
' chọn lại bitmap đã có từ trước trong memory device context vào nó
SelectObject memDC, old
' hủy memory device context đã tạo lúc trước
DeleteDC memDC
' giải phóng device context của cửa sổ
ReleaseDC hwnd, DC
' trả về bitmap đã tạp trong bộ nhớ và đã có các dữ liệu mầu của cửa sổ
HBITMAPFromWindowHandle = hbmp
End Function

Function CreateSomethingBitmap() As Long
Dim ScreenDC As Long, memDC As Long, old As Long, tmpold As Long, tmpHandle As Long
Dim hBrush As Long, oldBrush As Long, rc As RECT, text As String
' tạo memory device context
ScreenDC = CreateCompatibleDC(0)
' đọc display device context
memDC = GetDC(0)
' tạo bitmap có dài và rộng = 300
tmpHandle = CreateCompatibleBitmap(memDC, 300, 300)
' giải phóng device context
ReleaseDC 0, memDC
' tạo memory device context
memDC = CreateCompatibleDC(0)
' chọn bitmap vào memory device context
tmpold = SelectObject(memDC, tmpHandle)
' tạo brush có mầu đỏ
hBrush = CreateSolidBrush(RGB(255, 0, 0))
' chọn brush vào memory device context
oldBrush = SelectObject(memDC, hBrush)
' thiết lập một "hình chữ nhật" có cạnh = 300
SetRect rc, 0, 0, 300, 300
' "đổ" mầu vào memory device context mà trong đó có bitmap 300x300 vào vùng có kích thước 300x300, tức đổ "kín" bitmap
FillRect memDC, rc, hBrush
' thiết lập background cho memory device context thành "trong suốt" vì nếu không thì khi viết Text sẽ có nền mầu trắng
SetBkMode memDC, TRANSPARENT
text = "He he he hic hic hic ten ten"
' viết text lên bitmap có trong memory device context
TextOutW memDC, 50, 150, StrConv(text, vbUnicode), Len(text)
' chọn old brush vào lại memory device context
SelectObject memDC, hBrush
' chọn lại old bitmap vào memory device context
SelectObject memDC, tmpold
' hủy brush đã tạo
DeleteObject hBrush
' hủy device context đã tạo lúc trước
DeleteDC ScreenDC
' hủy device context đã tạo lúc trước
DeleteDC memDC
' trả về bitmap handle đã tạo, đã được "đổ" mầu và viết text
CreateSomethingBitmap = tmpHandle
End Function
[/GPECODE]

module2
[GPECODE=vb]
Sub Button1_Click()
Dim hbmp As Long
hbmp = HBITMAPFromWindowHandle(Application.hwnd)
UserForm1.Picture = HBITMAPToIPicture(hbmp)
With UserForm1
.left = Application.left
.top = Application.top
.Width = Application.Width
.Height = Application.Height
End With
UserForm1.Show
End Sub

Sub Button2_Click()
Dim hBitmap As Long
hBitmap = CreateSomethingBitmap
UserForm1.Picture = HBITMAPToIPicture(hBitmap)
UserForm1.Show
End Sub
[/GPECODE]

module modPicture
[GPECODE=vb]
Const PICTYPE_BITMAP = 1
Const CF_BITMAP = 2
Const IMAGE_BITMAP = 0
Const LR_COPYRETURNORG = &H4

'cấu trúc GUID
Private Type GUID
D1 As Long
D2 As Integer
D3 As Integer
D4(0 To 7) As Byte
End Type

Private Type PictDesc
cbSizeofStruct As Long
picType As Long
hImage As Long
hPal As Long
End Type

Private Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetClipboardData Lib "user32" (ByVal wFormat As Long) As Long
Private Declare Function CloseClipboard Lib "user32" () As Long

Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (lpPictDesc As PictDesc, riid As GUID, ByVal fOwn As Long, lplpvObj As Any) As Long
Private Declare Function CopyImage Lib "user32" (ByVal handle As Long, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As Long

Function RangeToHBITMAP(rng As range) As Long
Dim hRes As Long
rng.Copy
OpenClipboard 0

hRes = GetClipboardData(CF_BITMAP)
If hRes <> 0 Then RangeToHBITMAP = CopyImage(hRes, IMAGE_BITMAP, 0, 0,

LR_COPYRETURNORG)
CloseClipboard
Application.CutCopyMode = False
End Function

Function HBITMAPToIPicture(ByVal hbmp As Long) As IPictureDisp
Dim lpPictDesc As PictDesc, riid As GUID, pic As IPictureDisp
' nếu c bitmap handle
If hbmp <> 0 Then
' GUID của Interface IPicture - {7BF80980-BF32-101A-8BBB-00AA00300CAB}
With riid
.D1 = &H7BF80980
.D2 = &HBF32
.D3 = &H101A
.D4(0) = &H8B
.D4(1) = &HBB
.D4(2) = &H0
.D4(3) = &HAA
.D4(4) = &H0
.D4(5) = &H30
.D4(6) = &HC
.D4(7) = &HAB
End With
With lpPictDesc
.cbSizeofStruct = Len(lpPictDesc) ' độ lớn của cấu trúc PictDesc
.picType = PICTYPE_BITMAP ' dạng ảnh
.hImage = hbmp ' bitmap handle
.hPal = 0
End With
' tạo Picture
If OleCreatePictureIndirect(lpPictDesc, riid, True, pic) = 0 Then
Set HBITMAPToIPicture = pic
End If
End If
End Function
[/GPECODE]
 

File đính kèm

Lần chỉnh sửa cuối:
Sau khi được Thầy siwtom hướng dẫn làm trực tiếp trên form, tôi sửa lại code trở nên gọn gàng hơn, không phải lòng vòng như bài trước nữa! Cái này mà ứng dụng khi biểu diễn đồ thị trên Form thì tuyệt vời luôn! Xin xem file.



He he.
Tôi rất thích graphic và API. Nhớ những ngày đầu học lập trình là cố tìm hiểu để viết code cho cá bơi trên màn hình. Tất nhiên hình cá thì lấy ảnh ở đâu cũng được nhưng đã là hình thì bao giờ cũng có hình dạng hình chữ nhật. Phải "cắt" riêng con cá ra rồi để nó trôi trên màn hình theo nhịp của Timer (SetTimer).

Nói thực là muốn viết trong API thì phải hiểu được system nó hoạt động như thế nào. Làm việc với bộ nhớ, clipboard, file system, thông điệp, các object có trong system v...v Bởi đã viết trong API thì không qua người trung gian nào cả, tự mình phải gọi các hàm API. Viết trong VB, VBA, Delphi v...v là đã qua trung gian. Mình chỉ viết Copy là ảnh hay text nó nhẩy vào trong clipboard. Nhưng sau "cánh gà" thì VBA, VB, Delphi phải biến cái "copy" kia thành các gọi hàm API để "đặt" ảnh hay text vào clipboard. Nó giống như anh A (lập trình viên API) phải tự cưa, bào, ghép (gọi các hàm API) để làm cái tủ chè, còn anh B (lập trình viên VB, VBA, Delphi) thì chỉ "gọi, đặt đơn": Cho tủ chè (gọi Copy) là anh C (VB, VBA, Delphi, tức lập trình viên API) sẽ phải biến cái "đơn đặt hàng" kia thành cưa, bào, ghép (gọi các hàm API) để làm cái tủ chè mà dâng cho anh B.
 
He he.
Tôi rất thích graphic và API. Nhớ những ngày đầu học lập trình là cố tìm hiểu để viết code cho cá bơi trên màn hình. Tất nhiên hình cá thì lấy ảnh ở đâu cũng được nhưng đã là hình thì bao giờ cũng có hình dạng hình chữ nhật. Phải "cắt" riêng con cá ra rồi để nó trôi trên màn hình theo nhịp của Timer (SetTimer).

Nói thực là muốn viết trong API thì phải hiểu được system nó hoạt động như thế nào. Làm việc với bộ nhớ, clipboard, file system, thông điệp, các object có trong system v...v Bởi đã viết trong API thì không qua người trung gian nào cả, tự mình phải gọi các hàm API. Viết trong VB, VBA, Delphi v...v là đã qua trung gian. Mình chỉ viết Copy là ảnh hay text nó nhẩy vào trong clipboard. Nhưng sau "cánh gà" thì VBA, VB, Delphi phải biến cái "copy" kia thành các gọi hàm API để "đặt" ảnh hay text vào clipboard. Nó giống như anh A (lập trình viên API) phải tự cưa, bào, ghép (gọi các hàm API) để làm cái tủ chè, còn anh B (lập trình viên VB, VBA, Delphi) thì chỉ "gọi, đặt đơn": Cho tủ chè (gọi Copy) là anh C (VB, VBA, Delphi, tức lập trình viên API) sẽ phải biến cái "đơn đặt hàng" kia thành cưa, bào, ghép (gọi các hàm API) để làm cái tủ chè mà dâng cho anh B.
Thầy có thể gửi tặng "con cá" của Thầy lên đây để mọi người thưởng thức không ạ?

Cám ơn Thầy rất nhiều!
 
Thầy có thể gửi tặng "con cá" của Thầy lên đây để mọi người thưởng thức không ạ?

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

Trước hết xin nói là tôi viết rất rõ. Tôi không nói là con cá bơi, tức vẫy đuôi, há miệng đớp ô xi v...v
Tôi viết là con cá trôi, tức cái hình con cá nó trôi, nó dịch từ từ trên màn hính.

Một hình ảnh bao giờ cũng là hình chữ nhật. Ảnh có con cá (cô gái, ô tô ...) gồm 2 phần: phần con cá (cô gái, ô tô ...) và phần nền. Ta phải làm sao mà chỉ thấy phần con cá (cô gái, ô tô ...) trôi thôi, phần nền không nhìn thấy.

Khi tôi vọc thì tôi tìm hiểu 2 phương pháp mà cho cùng hiệu ứng.

1. Cách này tôi biết sau cách 2. Ta phải có ảnh con cá mà phần nền có cùng một mầu gọi là mầu trong suốt, khác với mầu của con cá. Tức từ 1 ảnh thực ta phải soạn ra trước 1 ảnh mà phần nền có cùng 1 mầu trong suốt. Tiếp đó ta load ảnh lên 1 cửa sổ (theo cách hiểu của window thì vd. nút Start trong XP là 1 window, khay hệ thống có đồng hồ cũng là window ...) làm hình nền. Ta tạo một region có hình dạng là vùng trùng với phần cá. Sau đó dùng hàm API SetWindowRgn. Những phần không thuộc Region sẽ không nhìn thấy, tức chúng không được system vẽ nữa, do đó cửa sổ sẽ có hình của con cá, mà nền của cửa sổ là hình con cá (do lúc trước ta load hình nền). Bây giờ trong TimerProc ta thay đổi Left, Top của cửa sổ thì thấy nó trôi thôi.

Trong tập tin ở bài #1 ở link sau

http://www.giaiphapexcel.com/forum/...-các-Ảnh-nhỏ-từ-một-Ảnh-lớn-theo-tọa-độ/page2

thì khi bạn nhấn nút "Form biến dạng" thì chính là cách này. Những cái bạn nhìn thấy thì nó chính là con cá, còn những cái không nhìn thấy thì là phần nền. Ở đây tôi không load hình nền, nhưng nếu bạn tưởng tượng là cái Region kia tôi tạo ra nó có hình con cá và tôi có load bức tranh con cá làm hình nền vào Form thì rõ rằng lúc đó bạn không chỉ nhìn thấy hình con cá mà là nhìn thấy con cá thực sự, có mầu, có mắt có vây. Thêm TimerProc nữa để thay đổi Left, Top của Form thì ta thấy con cá trôi.

Cũng có thể không tô cùng mầu phần nền mà chỉ cần có 1 loạt các điểm trên "đường viền" con cá rồi từ tập điểm ấy tạo ra Region mà đường viền kia là "đường biên". Càng nhiều điểm, chi chít điểm thì đường biên càng chính xác, trơn chứ không góc cạnh. Có thể viết phần mềm để đọc các điểm kia: load ảnh làm nền cho Form --> trong MouseUp đọc tọa độ của điểm click.

2. Cách này tôi vọc đầu tiên. Không dùng cửa sổ nào cả. Chỉ dùng bitmap và các hàm GDI - Graphics Device Interface thôi. Tức dùng các hàm GDI để copy lên desktop, hoặc cửa sổ nào đó, sao cho phần nền của ảnh "biến mất". Tôi không nhớ liệu đã gửi ví dụ nào lên GPE chưa. Nếu chưa thì bạn phải đợi chút. Bây giờ cuối tuần tôi cũng có chút việc.
 
Thầy có thể gửi tặng "con cá" của Thầy lên đây để mọi người thưởng thức không ạ?

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

Như đã hứa tôi gửi vd. về cá trôi

1. Trong thư mục "Troi tren window" và "Troi tren desktop" có 2 project viết trong VB6.
2. Tôi đã compile 2 project VB6 nên nếu ai không có VB6 thì chạy luôn EXE để xem.
3. Khi chạy "Troi tren desktop" thì Cá sẽ troi trên nền hình mà ta nhìn thấy ở thời điểm nhấn nút Animation. Tức nếu ở thời điểm nhấn Animation nếu ta nhìn thấy cửa sổ thư mục nào đó thì Cá sẽ trôi trên hình cửa sổ thư mục đó. Nếu lúc đó nhìn thấy desktop với những icons (sau khi chạy EXE thì kéo hết mọi ct xuống thanh task bar bằng cách nhấn nút trên thanh đó --> click icon của EXE trên task bar để chỉ kéo riêng EXE lên desktop) thì khi nhấn Animation ta sẽ thấy Cá trôi trên hình desktop.
4. Trong thư mục Excel có tập tin Excel. Cá sẽ trôi trên hình cửa sổ Excel.

Đây là cách 2 nói ở bài #30. Tức chỉ dùng các hàm GDI để vẽ hình lên nền thôi. Cách 1 là dùng SetWindowRgn (cho Form (đã có hình nền) có hình dạng gì đó vd. con cá) rồi di chuyển Form trong nhịp đồng hồ bằng hàm API MoveWindow hoặc thay đổi Left và Top của Form.

Tôi chú thích code rất kỹ. Nếu ai muốn tự vọc mà có câu hỏi thì cứ mạnh dạn.
 

File đính kèm

Như đã hứa tôi gửi vd. về cá trôi

1. Trong thư mục "Troi tren window" và "Troi tren desktop" có 2 project viết trong VB6.
2. Tôi đã compile 2 project VB6 nên nếu ai không có VB6 thì chạy luôn EXE để xem.
3. Khi chạy "Troi tren desktop" thì Cá sẽ troi trên nền hình mà ta nhìn thấy ở thời điểm nhấn nút Animation. Tức nếu ở thời điểm nhấn Animation nếu ta nhìn thấy cửa sổ thư mục nào đó thì Cá sẽ trôi trên hình cửa sổ thư mục đó. Nếu lúc đó nhìn thấy desktop với những icons (sau khi chạy EXE thì kéo hết mọi ct xuống thanh task bar bằng cách nhấn nút trên thanh đó --> click icon của EXE trên task bar để chỉ kéo riêng EXE lên desktop) thì khi nhấn Animation ta sẽ thấy Cá trôi trên hình desktop.
4. Trong thư mục Excel có tập tin Excel. Cá sẽ trôi trên hình cửa sổ Excel.

Đây là cách 2 nói ở bài #30. Tức chỉ dùng các hàm GDI để vẽ hình lên nền thôi. Cách 1 là dùng SetWindowRgn (cho Form (đã có hình nền) có hình dạng gì đó vd. con cá) rồi di chuyển Form trong nhịp đồng hồ bằng hàm API MoveWindow hoặc thay đổi Left và Top của Form.

Tôi chú thích code rất kỹ. Nếu ai muốn tự vọc mà có câu hỏi thì cứ mạnh dạn.

Thú vị thật đó Thầy siwtom, thấy cái đầu của mình chạy chạy là tít mắt luôn! }}}}}

Cám ơn Thầy rất nhiều.
 
Như đã hứa tôi gửi vd. về cá trôi

1. Trong thư mục "Troi tren window" và "Troi tren desktop" có 2 project viết trong VB6.
2. Tôi đã compile 2 project VB6 nên nếu ai không có VB6 thì chạy luôn EXE để xem.
3. Khi chạy "Troi tren desktop" thì Cá sẽ troi trên nền hình mà ta nhìn thấy ở thời điểm nhấn nút Animation. Tức nếu ở thời điểm nhấn Animation nếu ta nhìn thấy cửa sổ thư mục nào đó thì Cá sẽ trôi trên hình cửa sổ thư mục đó. Nếu lúc đó nhìn thấy desktop với những icons (sau khi chạy EXE thì kéo hết mọi ct xuống thanh task bar bằng cách nhấn nút trên thanh đó --> click icon của EXE trên task bar để chỉ kéo riêng EXE lên desktop) thì khi nhấn Animation ta sẽ thấy Cá trôi trên hình desktop.
4. Trong thư mục Excel có tập tin Excel. Cá sẽ trôi trên hình cửa sổ Excel.

Đây là cách 2 nói ở bài #30. Tức chỉ dùng các hàm GDI để vẽ hình lên nền thôi. Cách 1 là dùng SetWindowRgn (cho Form (đã có hình nền) có hình dạng gì đó vd. con cá) rồi di chuyển Form trong nhịp đồng hồ bằng hàm API MoveWindow hoặc thay đổi Left và Top của Form.

Tôi chú thích code rất kỹ. Nếu ai muốn tự vọc mà có câu hỏi thì cứ mạnh dạn.
bác làm ơn cho em hỏi, chổ cái nút hình excel đó, no chỉ hiển thị lên userform cái màn hinh thực tế từ dòng 1 đến 23 của excel thôi hả có cách nào có thể điều chỉnh kéo dài thêm được không vì dữ liệu trên file excel có thể nhiều hơn 23 dòng hoặc có thể chọn vùng dữ liệu tùy thích được không vậy
thêm 1 điều nữa là hiện tại code đang hiểu là sheet hiện tại trên màn hình là sheets nào thì nó sẽ show ra sheet đó, vậy có thể chọn sheet tùy thích đươc không?
cám ơn
 
Lần chỉnh sửa cuối:

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

Back
Top Bottom