Có control nào thay thế image trên userform không?

Liên hệ QC

phuoclocvl

Thành viên thường trực
Tham gia
28/3/12
Bài viết
220
Được thích
32
Dạ xin chào các Anh/ Chị.
Em không biết gửi ở chổ nào nên gửi lên đây.
Cho em hỏi mình có công cụ control nào thay thế image để hiện thị ảnh không ạ, do công cụ này không hiển thị được ảnh có đuôi .png .
Xin cảm ơn,
 
Dạ xin chào các Anh/ Chị.
Em không biết gửi ở chổ nào nên gửi lên đây.
Cho em hỏi mình có công cụ control nào thay thế image để hiện thị ảnh không ạ, do công cụ này không hiển thị được ảnh có đuôi .png .
Xin cảm ơn,
Có control thay thế hay không thì không biết, nhưng chỉ biết code để Load Image vào Image thôi.
 

File đính kèm

  • 956417.xlsm
    26 KB · Đọc: 37
Upvote 0
Có control thay thế hay không thì không biết, nhưng chỉ biết code để Load Image vào Image thôi.
Cảm ơn bác.
 
Upvote 0
Upvote 0
Em thấy trang này không hay bằng GPE mình Thầy ạ. Em không biết tiếng Anh toàn nhờ Google. Nhưng code họ viết cứ đập thẳng xuống bảng tính thôi ạ
Tôi có dám so sánh trang đó với GPE, chỉ tại thấy họ dùng thì mình chỉ giúp còn trang GPE đã từng thấy anh @batman1 viết rồi nhưng tìm chưa ra thôi.
 
Upvote 0
Em thấy trang này không hay bằng GPE mình Thầy ạ. Em không biết tiếng Anh toàn nhờ Google. Nhưng code họ viết cứ đập thẳng xuống bảng tính thôi ạ
Trang nào cũng có cái hay của nó do không hợp với nhu cầu của bạn nên bạn cảm thấy không hay thôi.
 
Upvote 0
Dạ xin chào các Anh/ Chị.
Em không biết gửi ở chổ nào nên gửi lên đây.
Cho em hỏi mình có công cụ control nào thay thế image để hiện thị ảnh không ạ, do công cụ này không hiển thị được ảnh có đuôi .png .
Xin cảm ơn,
Không hiểu ý của bạn muốn như thế nào khi muốn sử dụng Control nào khác nữa, để gán hình thì tôi chỉ biết Image.
 

File đính kèm

  • HOC MA CHOI.rar
    1.7 MB · Đọc: 37
Upvote 0
Cho em hỏi mình có công cụ control nào thay thế image để hiện thị ảnh không ạ, do công cụ này không hiển thị được ảnh có đuôi .png .

Bạn muốn chèn ảnh có nền trong suốt à?
Dùng BMP cũng được nhưng định dạng này không có nén ảnh nên hơi nặng. Tốt hơn thì chuyển ảnh sang GIF là nhẹ nhất (chỉ có 256 màu - 8 bit). Nếu ảnh chỉ dùng ít màu, ít màu chuyển, ảnh dạng text nhiều...thì nên lưu sang GIF để đưa vào Userform cho nhẹ.
Bổ sung là:
- Đốii với ảnh BMP thì dùng làm icon nút lệnh sẽ có hỗ trợ nền trong suốt (transparent) nhưng khi đưa vào Image control thì vẫn giữ nguyên nền trắng. Vụ này cũng lạ.
- GIF thì làm icon hay dùng trong Image đều hỗ trợ nền trong suốt.

Minh hoạ dùng cùng 1 file ảnh BMP (16 bit màu, hỗ trợ kênh Alpha). Thực chất hàm UDF LoadImage() theo link của bác giaiphap cung cấp là chuyển đổi file PNG sang BMP rồi load lên Userform.
Dung lượng file ảnh: 100 x 100 (px)
- BMP: 34k
- PNG: 23k
- GIF: 16k

Screen Shot 2020-03-16 at 11.52.59 PM.png
 

File đính kèm

  • Screen Shot 2020-03-16 at 11.42.55 PM.png
    Screen Shot 2020-03-16 at 11.42.55 PM.png
    38.2 KB · Đọc: 4
Lần chỉnh sửa cuối:
Upvote 0
Không hiểu ý của bạn muốn như thế nào khi muốn sử dụng Control nào khác nữa, để gán hình thì tôi chỉ biết Image.
Ý người ta thế này. Người ta có ảnh, rất nhiều ảnh PNG. Người ta không muốn bỏ công convert thành vd. JPG. Nhưng LoadPicture không phục vụ PNG. Tức vd. của bạn là thừa vì dùng LoadPicture + JPG. Người ta không có khó khăn với LoadPicture + JPG, chỉ có khó khăn với PNG. Và người ta muốn biết là ngoài Image thì còn có control nào khác không.
---------
Thực ra cái ta cần là đối tượng (interface) IPictureDisp để "nhồi" vào Image. LoadPicture chẳng qua là trả về IPictureDisp.
Nhưng LoadPicture lại không phục vụ PNG nên code của bạn giaiphap dùng hàm OleCreatePictureIndirect để tạo IPicture.

Theo tôi hiểu thì cho dù ảnh có định dạng thế nào thì trong memory chỉ có HBITMAP. Tức các chương trình xử lý ảnh sẽ đọc tập tin ảnh, cho dù nó ở định dạng nào, và trên cơ sở dữ liệu của tập tin ảnh sẽ tạo HBITMAP trong memory. Có HBITMAP rồi thì có thể dùng các hàm API để làm mọi việc. Vd. có thể sao chép ảnh hay một phần của nó vào device context của vd. một cửa sổ nào đó. Có thể tạo HBITMAP mới có được từ HBITMAP kia bằng hiệu ứng emboss. Vân vân và vân vân.

Nếu là ảnh BMP thì Windows có hàm đơn giản như LoadImage. Nếu là các định dạng khác thì phải dùng cỗ máy lớn GDI+. Code của bạn giaiphap là dùng các hàm GDI+.

Trong mỗi môi trường lập trình thường có control để dùng cho ảnh. Vd. trong VBA có Image, trong Delphi có TImage, trong VB thì là PictureBox. Nhưng nếu lập trình trong API thuần thì khi chỉ cần hiển thị ảnh một cách đơn giản thì cũng có thể dùng control STATIC của system. STATIC có thể dùng với text để thay cho Label, cũng có thể dùng với ảnh để thay cho Image. Cũng là dùng hàm nào đó để đọc, tạo trong memory HBITMAP. Do cần mở PNG nên ta dùng GDI+. Có HBITMAP rồi thì, như tôi đã viết, có thể làm nhiều việc. Việc ta cần ở đây là "dâng" HBITMAP cho STATIC. Thế thôi. Trong tập tin ví dụ tôi lấy các hàm GDI+, bỏ qua OleCreatePictureIndirect, không dùng Image của VBA.

Ảnh được nhập vào STATIC ở kích thước thực. Nếu ta có khung cố định và muốn nhập ảnh vào khung đó thì ta phải tạo hBitmapDest. Thao tác như sau. Ta tạo device context trong memory DC1 (CreateCompatibleDC) rồi chọn hBitmap1 trả về bởi LoadPictureGDI vào device context DC1 (SelectObject), ghi nhớ oldBitmap1 trả về bởi SelectObject. Tiếp theo tạo device context trong memory DC2 (CreateCompatibleDC). Tạo bitmap trong memory hBitmap2 (CreateCompatibleBitmap) với kích thước của STATIC (ảnh khít STATIC) hoặc giữ đúng tỷ lệ 2 chiều của ảnh nguồn (ảnh center trong STATIC), rồi chọn nó vào device context DC2 (SelectObject), ghi nhớ oldBitmap2 trả về bởi SelectObject. Dùng hàm StretchBlt để copy hBitmap1 từ DC1 sang DC2. Tiếp theo chọn lại oldBitmap1 vào DC1 (SelectObject) rồi hủy hBitmap1 do LoadPictureGDI trả về (DeleteObject), hủy device context DC1 (DeleteDC). Cũng chọn oldBitmap2 vào DC2 (SelectObject), rồi hủy DC2 (DeleteDC). Ta không hủy hBitmap2 vì ta sẽ "dâng" nó cho STATIC. Chỉ đến khi đóng Form thì trước khi hủy STATIC thì code sẽ hủy hBitmap2 (DeleteObject).

Không dùng tập tin đính kèm. Hãy thử dùng tập tin trong bài #16.
 

File đính kèm

  • CreateStatic.rar
    269.6 KB · Đọc: 40
Lần chỉnh sửa cuối:
Upvote 0
Chắc có thời gian em mò API quá thấy các anh nói mà không hiểu gì hết hichic
 
Upvote 0
Ý người ta thế này. Người ta có ảnh, rất nhiều ảnh PNG. Người ta không muốn bỏ công convert thành vd. JPG. Nhưng LoadPicture không phục vụ PNG. Tức vd. của bạn là thừa vì dùng LoadPicture + JPG. Người ta không có khó khăn với LoadPicture + JPG, chỉ có khó khăn với PNG. Và người ta muốn biết là ngoài Image thì còn có control nào khác không.
---------
Thực ra cái ta cần là đối tượng (interface) IPictureDisp để "nhồi" vào Image. LoadPicture chẳng qua là trả về IPictureDisp.
Nhưng LoadPicture lại không phục vụ PNG nên code của bạn giaiphap dùng hàm OleCreatePictureIndirect để tạo IPicture.

Theo tôi hiểu thì cho dù ảnh có định dạng thế nào thì trong memory chỉ có HBITMAP. Tức các chương trình xử lý ảnh sẽ đọc tập tin ảnh, cho dù nó ở định dạng nào, và trên cơ sở dữ liệu của tập tin ảnh sẽ tạo HBITMAP trong memory. Có HBITMAP rồi thì có thể dùng các hàm API để làm mọi việc. Vd. có thể sao chép ảnh hay một phần của nó vào device context của vd. một cửa sổ nào đó. Có thể tạo HBITMAP mới có được từ HBITMAP kia bằng hiệu ứng emboss. Vân vân và vân vân.

Nếu là ảnh BMP thì Windows có hàm đơn giản như LoadImage. Nếu là các định dạng khác thì phải dùng cỗ máy lớn GDI+. Code của bạn giaiphap là dùng các hàm GDI+.

Trong mỗi môi trường lập trình thường có control để dùng cho ảnh. Vd. trong VBA có Image, trong Delphi có TImage, trong VB thì là PictureBox. Nhưng nếu lập trình trong API thuần thì khi chỉ cần hiển thị ảnh một cách đơn giản thì cũng có thể dùng control STATIC của system. STATIC có thể dùng với text để thay cho Label, cũng có thể dùng với ảnh để thay cho Image. Cũng là dùng hàm nào đó để đọc, tạo trong memory HBITMAP. Do cần mở PNG nên ta dùng GDI+. Có HBITMAP rồi thì, như tôi đã viết, có thể làm nhiều việc. Việc ta cần ở đây là "dâng" HBITMAP cho STATIC. Thế thôi. Trong tập tin ví dụ tôi lấy các hàm GDI+, bỏ qua OleCreatePictureIndirect, không dùng Image của VBA.

Ảnh được nhập vào STATIC ở kích thước thực. Nếu ta có khung cố định và muốn nhập ảnh vào khung đó thì ta phải tạo hBitmapDest. Thao tác như sau. Ta tạo device context trong memory DC1 (CreateCompatibleDC) rồi chọn hBitmap1 trả về bởi LoadPictureGDI vào device context DC1 (SelectObject), ghi nhớ oldBitmap1 trả về bởi SelectObject. Tiếp theo tạo device context trong memory DC2 (CreateCompatibleDC). Tạo bitmap trong memory hBitmap2 (CreateCompatibleBitmap) với kích thước của STATIC (ảnh khít STATIC) hoặc giữ đúng tỷ lệ 2 chiều của ảnh nguồn (ảnh center trong STATIC), rồi chọn nó vào device context DC2 (SelectObject), ghi nhớ oldBitmap2 trả về bởi SelectObject. Dùng hàm StretchBlt để copy hBitmap1 từ DC1 sang DC2. Tiếp theo chọn lại oldBitmap1 vào DC1 (SelectObject) rồi hủy hBitmap1 do LoadPictureGDI trả về (DeleteObject), hủy device context DC1 (DeleteDC). Cũng chọn oldBitmap2 vào DC2 (SelectObject), rồi hủy DC2 (DeleteDC). Ta không hủy hBitmap2 vì ta sẽ "dâng" nó cho STATIC. Chỉ đến khi đóng Form thì trước khi hủy STATIC thì code sẽ hủy hBitmap2 (DeleteObject).
Em mới thử trên Windows10x64 + Office2016x64
1584405990703.png
 
Upvote 0
Bạn muốn chèn ảnh có nền trong suốt à?
Dùng BMP cũng được nhưng định dạng này không có nén ảnh nên hơi nặng. Tốt hơn thì chuyển ảnh sang GIF là nhẹ nhất (chỉ có 256 màu - 8 bit). Nếu ảnh chỉ dùng ít màu, ít màu chuyển, ảnh dạng text nhiều...thì nên lưu sang GIF để đưa vào Userform cho nhẹ.
Bổ sung là:
- Đốii với ảnh BMP thì dùng làm icon nút lệnh sẽ có hỗ trợ nền trong suốt (transparent) nhưng khi đưa vào Image control thì vẫn giữ nguyên nền trắng. Vụ này cũng lạ.
- GIF thì làm icon hay dùng trong Image đều hỗ trợ nền trong suốt.

Minh hoạ dùng cùng 1 file ảnh BMP (16 bit màu, hỗ trợ kênh Alpha). Thực chất hàm UDF LoadImage() theo link của bác giaiphap cung cấp là chuyển đổi file PNG sang BMP rồi load lên Userform.
Dung lượng file ảnh: 100 x 100 (px)
- BMP: 34k
- PNG: 23k
- GIF: 16k

View attachment 233413
Dạ , em đang làm 1 cái forrm đó Anh.
Form em sẽ có . Textbox chứa tên mã hàng. 1 textbox hiển thị thông tin 1 image control để hiển thị ảnh. Và một số thông tin khác. ảnh thì có đuôi .jpg, .png, .bmp
Mà image lại không hiển thị .png được. Nên hỏi các anh chị xem có cách nào không
Cảm ơn Anh.
 
Upvote 0
Tức có lỗi? Tôi hơi sơ suất. Chính ra nên kiểm tra và với system của bạn thì phải là
Application.HinstancePtr (không là Application.Hinstance)

Bạn thử sửa lại xem.
Em mới sửa lại như Anh hướng dẫn chạy ko báo lỗi nhưng làm đơ file Excel xong thoát luôn
 
Upvote 0
Có control thay thế hay không thì không biết, nhưng chỉ biết code để Load Image vào Image thôi.
File này muốn chạy trên Win64 còn phải sửa một đống luôn
Bài đã được tự động gộp:

Em mới thử trên Windows10x64 + Office2016x64
View attachment 233422

Mạnh thử file này xem sao (tôi sửa từ bài 2)
 

File đính kèm

  • 956417.xlsm
    26.4 KB · Đọc: 33
Upvote 0
Em mới sửa lại như Anh hướng dẫn chạy ko báo lỗi nhưng làm đơ file Excel xong thoát luôn
Tôi không có office 64 bit nên không thể kiểm tra. Nhưng nhờ rà soát lại code mà tôi phát hiện mấy chỗ không chuẩn - chỉ là khai báo thôi.
1. Type GDIPlusStartupInput chưa được khai báo cho 64 bit

2. SendMessage được khai báo không trước sau như một - cho 64 bit là ByVal lParam As LongPtr, trong khi đó cho 32 bit là ByRef lParam As Any.

3. Private Declare PtrSafe Function FindWindowEx tôi dán vào cả 2 phần mà quên sửa lại cho phần #Else.

4. Private Declare PtrSafe Function GdiplusStartup tôi dán vào cả 2 phần nhưng ở phần #Else chỉ mới sửa các tham số thành Long mà quên không xóa PtrSafe.

Trong tập tin đính kèm tôi đã sửa lại.

Tôi sửa lại, thử không dùng Application.HinstancePtr nữa mà dùng GetWindowLong. Nếu vẫn không được thì thôi chịu, chỉ nhìn chay code thì thấy chả có chỗ nào đáng nghi. LoadPictureGDI và SendMessage tôi cho là không lỗi. Nhất là lỗi kiểu "chết đột ngột". Chả nhẽ Form trong 64 bit khác lạ? Không có 64 bit nên chỉ nhìn thôi.

Bạn giúp tôi test nốt lần này. Vì tôi cũng muốn biết kết quả thế nào. Tôi đã test trên: XP Home 32 bit + Excel 2010 32 bit, Windows 8 32 bit + Excel 2013 32 bit, Windows 10 64 bit + Excel 2007 32 bit đều không có lỗi gì cả. Chỉ có office 64 bit là tôi không có để test.
----------
Tôi nghĩ là bạn chỉ muốn thông báo vấn ̣đề chứ không muốn test. Vì khả năng của bạn thừa để kiểm tra. Hãy biến 3 dòng kể từ dòng
Mã:
If BitmapHandle <> 0 Then DeleteObject BitmapHandle
thành chú thích rồi chạy code. Nếu không có lỗi thì lưu tập tin, đóng tập tin rồi mở lại, rồi mở thêm dòng trên rồi lại chạy code. Nếu không có lỗi thì lưu lại tập tin rồi đóng nó, rồi mở lại. Sau đó lại mở thêm dòng thứ 2. Có tổng cộng 3 dòng thì cùng lắ mở 3 lần. Mỗi lần mở thêm dòng để kiểm tra thì lưu tập tin, đóng và mở lại (do khi chạy không có lỗi thì hStatic <> 0 nên nếu không đóng mà chạy tiếp thì code trong If ... End If không được thực hiện)
 

File đính kèm

  • CreateStaticBitmap.rar
    268.9 KB · Đọc: 31
Lần chỉnh sửa cuối:
Upvote 0
Bạn giúp tôi test nốt lần này. Vì tôi cũng muốn biết kết quả thế nào. Tôi đã test trên: XP Home 32 bit + Excel 2010 32 bit, Windows 8 32 bit + Excel 2013 32 bit, Windows 10 64 bit + Excel 2007 32 bit đều không có lỗi gì cả. Chỉ có office 64 bit là tôi không có để test.

File của bác chạy tốt trên Windows 7 64 bit + Office 2016 64 bit.
(file nguyên thuỷ đính kèm, không sửa dòng "If Bitmaphandle..." thành chú thích)
 
Upvote 0
Tôi không có office 64 bit nên không thể kiểm tra. Nhưng nhờ rà soát lại code mà tôi phát hiện mấy chỗ không chuẩn - chỉ là khai báo thôi.
1. Type GDIPlusStartupInput chưa được khai báo cho 64 bit

2. SendMessage được khai báo không trước sau như một - cho 64 bit là ByVal lParam As LongPtr, trong khi đó cho 32 bit là ByRef lParam As Any.

3. Private Declare PtrSafe Function FindWindowEx tôi dán vào cả 2 phần mà quên sửa lại cho phần #Else.

4. Private Declare PtrSafe Function GdiplusStartup tôi dán vào cả 2 phần nhưng ở phần #Else chỉ mới sửa các tham số thành Long mà quên không xóa PtrSafe.

Trong tập tin đính kèm tôi đã sửa lại.

Tôi sửa lại, thử không dùng Application.HinstancePtr nữa mà dùng GetWindowLong. Nếu vẫn không được thì thôi chịu, chỉ nhìn chay code thì thấy chả có chỗ nào đáng nghi. LoadPictureGDI và SendMessage tôi cho là không lỗi. Nhất là lỗi kiểu "chết đột ngột". Chả nhẽ Form trong 64 bit khác lạ? Không có 64 bit nên chỉ nhìn thôi.

Bạn giúp tôi test nốt lần này. Vì tôi cũng muốn biết kết quả thế nào. Tôi đã test trên: XP Home 32 bit + Excel 2010 32 bit, Windows 8 32 bit + Excel 2013 32 bit, Windows 10 64 bit + Excel 2007 32 bit đều không có lỗi gì cả. Chỉ có office 64 bit là tôi không có để test.
----------
Tôi nghĩ là bạn chỉ muốn thông báo vấn ̣đề chứ không muốn test. Vì khả năng của bạn thừa để kiểm tra. Hãy biến 3 dòng kể từ dòng
Mã:
If BitmapHandle <> 0 Then DeleteObject BitmapHandle
thành chú thích rồi chạy code. Nếu không có lỗi thì lưu tập tin, đóng tập tin rồi mở lại, rồi mở thêm dòng trên rồi lại chạy code. Nếu không có lỗi thì lưu lại tập tin rồi đóng nó, rồi mở lại. Sau đó lại mở thêm dòng thứ 2. Có tổng cộng 3 dòng thì cùng lắ mở 3 lần. Mỗi lần mở thêm dòng để kiểm tra thì lưu tập tin, đóng và mở lại (do khi chạy không có lỗi thì hStatic <> 0 nên nếu không đóng mà chạy tiếp thì code trong If ... End If không được thực hiện)
Cái này chạy tốt đó Anh
 
Upvote 0
Cái này chạy tốt đó Anh
Thật hả. Mừng quá.

Nhiều khi cẩu thả, cứ copy / paste. Cũng có kiểm tra đấy chứ nhưng nhiều khi quên. Dán 1 khai báo vào 2 nơi, vẫn biết là 1 nơi phải sửa lại. Nhưng có thể lúc đó có gì đó bận đột xuất, khi quay lại thì quên mất. Có cái thì dự định chốc nữa thêm, lát nữa sửa, nhưng rồi sau đó lại quên.

Cám ơn bạn đã kiểm tra và thông tin lại. Viết code mà không thể kiểm tra thì bực lắm, nhưng biết làm sao.
 
Upvote 0
Web KT
Back
Top Bottom