Nhằm cũng cố kiến thức về VBA cho các bạn mới bắt đầu và cả những bạn đang ứng dụng mà chưa hiểu nhiều về nó, tôi mở topic này với mong mõi qua những câu hỏi vui, các bạn sẽ nhận định lại sự hiểu biết cũa mình... (Kễ cã chính tôi cũng đang tập tành nên có rất nhiều cái chưa biết)
Mong rằng topic sẽ mang đến cho các bạn những khám phá thú vị với những cái tưỡng chừng như đã biết
Mong nhận dc bài viết về câu đố cũa các cao thủ! Còn các bạn mới thì đừng ngại khi đưa ra ý kiến cũa mình.. Có sai có sữa sẽ hoàn thiện!
Tôi xin mỡ màn trước bằng 1 câu hỏi đơn giãn
ANH TUẤN
CÂU HỎI 1: Tại sao biến K ko hoạt động?
Tôi muốn khi nhấn vào 1 button thì cell A1 sẽ tăng lên 1 đơn vị... Tôi đã làm như sau:
-Tạo 1 Command Button (nút nhấn thuộc thanh Control Toolbox), click phải chuột lên nút nhấn, chọn View code, rồi gõ vào đoạn code sau:
PHP:
Private Sub CommandButton1_Click()
K = K + 1
Range("A1").Value = K
End Sub
Ban đầu K chưa có gì, xem như =0, nhấn nút lần thứ nhất thì K dc tăng thêm 1, vậy K hiện tại sẽ bằng 1, và gán K vào cell A1 thì đương nhiên A1 sẽ =1... Nhấn nút lần 2, K lại dc tăng thêm 1 nên hiện tại K sẽ =2 và cell A1 cũng sẽ =2... vân vân.. từ đó diễn tiến tiếp...
Hi.. hi.. Điều này nghe qua có vẽ rất hợp lý, ấy thế mà khi nhấn nút nó chỉ hoạt động dc duy nhất 1 lần (A1 = 1) rồi thôi ko nhút nhít nữa...
Các bạn có thể giãi thích tại sao lại như thế ko? Tại sao những lần nhấn nút sau đó K lại ko tăng thêm tí nào (vì thực tế A1 vẫn cứ = 1 hoài) ?
ANH TUẤN
Anh cài Script Vietnamese, sau đó mã hóa font cho chúng thành font CP 1258 bằng sự kiện Lost_Focus mà chúng mã hóa thành chữ quái quỷ gì đâu không à! Nhưng trong khi mình thử nhập nguồn bằng font VNI và định dạng font của nó cũng là font VNI thì nó lại hiển thị tiếng Việt mới ghê! Trong khi cái thuộc tính Text và Value của nó cũng là những chữ "quái quỷ"! Lạ thật, phải chi UNI lại xài được như VNI nhỉ!
Với ComboBox trên Sheet (hay ActiveX controls nói chung) thì việc hiển thị tiếng Việt Unicode thật sự có vấn đề:
- Khi ta bấm mũi tên xổ xuống của ComboBox, ta nhìn thấy tiếng Việt Unicode hiển thị rất tốt:
- Nhưng ngay khi chọn xong 1 Item rồi click chuột vào đâu đó trên bảng tính thì lập tức những ký tự có dấu bị biến thành dấu ?
Xin hỏi các bạn: Dùng cách gì để khắc phục tình trạng này? Tức có thể hiển thị tiếng Việt Unicode trên ComboBox trong mọi lúc
Câu hỏi này tuy là đố vui nhưng ứng dụng thiết thực đây
Option Explicit
Private Sub ComboBox1_GotFocus()
Sheet1.Shapes("Rectangle 1").Visible = msoFalse
End Sub
Private Sub ComboBox1_LostFocus()
With Sheet1.Shapes("Rectangle 1")
.TextFrame.Characters.Text = ComboBox1
.Visible = msoTrue
End With
End Sub
[COLOR=#008000]'Gan macro nay cho Shape:[/COLOR]
Sub HideShape()
Sheet1.Shapes(Application.Caller).Visible = msoFalse
Sheet1.ComboBox1.Activate
End Sub
Option Explicit
Private Sub ComboBox1_GotFocus()
Sheet1.Shapes("Rectangle 1").Visible = msoFalse
End Sub
Private Sub ComboBox1_LostFocus()
With Sheet1.Shapes("Rectangle 1")
.TextFrame.Characters.Text = ComboBox1
.Visible = msoTrue
End With
End Sub
[COLOR=#008000]'Gan macro nay cho Shape:[/COLOR]
Sub HideShape()
Sheet1.Shapes(Application.Caller).Visible = msoFalse
Sheet1.ComboBox1.Activate
End Sub
Trong Video đúng là đối tượng ComboBox của Form Controls, mà nếu sử dụng cái này thì cần quái gì phải thay đổi ngôn ngữ hệ thống, cứ để mặc định của Windows cũng dùng được vậy.
Hỏi hay ghê hen!
"Nếu là thầy" thì thầy sẽ... hổng nói, cứ để cho "trò" tự nghiên cứu
Mà dùng gì chẳng được, miễn đạt được mục đích
Ẹc... Ẹc...
--------------------
Hỏi hay ghê hen!
"Nếu là thầy" thì thầy sẽ... hổng nói, cứ để cho "trò" tự nghiên cứu
Mà dùng gì chẳng được, miễn đạt được mục đích
Ẹc... Ẹc...
--------------------
Đối tượng Combobox của bạn trong video này là đối tượng ở trên Userform, nhưng nó là một ActiveX Control, đặt trên Form thì không vấn đề gì rồi, nhưng bạn thử chèn nó lên Sheet xem nào. Lưu ý khi chèn trên Sheet có 2 loại đối tượng ComboBox đấy nhé, 1 cái thuộc Form Controls, 1 cái thuộc ActiveX Controls. Trong video trước của bạn tại bài #897 là ComboBox của Form Controls, mà với cái này thì cứ để thiết lập mặc định của Windows cũng hiển thị được tiếng Việt Unicode.
Cái của sư phụ là CHƠI ĂN GIAN nha! Đó không phải là cách giải quyết vấn đề triệt để!
Nếu như cho ComboBox vào 1 Frame thì lại khác à nha: Nó hiện tiếng Việt Unicode trong mọi trường hợp luôn
Cái của sư phụ là CHƠI ĂN GIAN nha! Đó không phải là cách giải quyết vấn đề triệt để!
Nếu như cho ComboBox vào 1 Frame thì lại khác à nha: Nó hiện tiếng Việt Unicode trong mọi trường hợp luôn
Ừ thì dễ! Nhưng code dài quá. Tôi làm bài này đúng 10 dòng code và chỉ gọn trong sheet1
Như thế này:
Mã:
Public WithEvents cbox As MSForms.ComboBox
Private Sub cbox_Click()
Sheet1.Frame1.Controls("TextBox1").Text = cbox.Text
End Sub
Private Sub Frame1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If TypeName(cbox) = "Nothing" Then
Set cbox = Sheet1.Frame1.Controls("ComboBox1")
cbox.List() = Sheet1.Range("A1:A10").Value
End If
End Sub
Với Range("A1:A10") là dữ liệu và Frame có chứa TextBox1
Mục đích của Frame1_MouseMove là để phòng ngừa code bị lỗi sẽ dùng nó khởi tạo lại sự kiện
-----------------------
Mình đâu có hỏi cái chuyện xuất ra sheet đâu! Đương nhiên khi xuất ra sheet thì dữ liệu được bảo toàn rồi (tiếng Việt vẫn là tiếng Việt)
Vấn đề ở đây là khi click chuột vào 1 cell, nhìn lại text trên ComboBox cảm thấy rất... ghét ---> Bạn tự nhìn vào cái ComboBox của mình đi
Lúc đầu em cũng tính làm vậy, nhưng em lại định kích cỡ cho Frame bằng với Combobox cho không ai nhận ra có sự tồn tại của nó; nhưng làm vậy thì không chạy được sự kiện MouseMove của Frame!
Theo em nghĩ nha, em có cảm giác là một khi sử dụng sự kiện MouseMove thì y như rằng nó đang xài Do ... DoEvents ... Loop và như thế nó cứ chạy code liên tục vậy ạ!
Lúc đầu em cũng tính làm vậy, nhưng em lại định kích cỡ cho Frame bằng với Combobox cho không ai nhận ra có sự tồn tại của nó; nhưng làm vậy thì không chạy được sự kiện MouseMove của Frame!
Theo em nghĩ nha, em có cảm giác là một khi sử dụng sự kiện MouseMove thì y như rằng nó đang xài Do ... DoEvents ... Loop và như thế nó cứ chạy code liên tục vậy ạ!
Chính vì sợ nó chạy liên tục nên tôi có đoạn If TypeName(cbox) = "Nothing"Then rồi đó. Bảo đảm chỉ chạy 1 lần
(không tin có thể dùng MsgBox đặt trong If để thử)
Vậy: Khi nào code gì đó bị lỗi thì cbox sẽ = Nothing và sự kiện MouseMove sẽ tự khởi tạo lại nó
------------------
Mà công nhận chú Nghĩa này tiếp cận vấn đề nhanh ghê! Gợi ý 1 chút đã làm trúng phốc!
Ẹc... Ẹc...
Thì cũng đúng! Vậy tùy trường hợp mà chọn lựa thôi
Chính vì sợ nó chạy liên tục nên tôi có đoạn If TypeName(cbox) = "Nothing"Then rồi đó. Bảo đảm chỉ chạy 1 lần
(không tin có thể dùng MsgBox đặt trong If để thử)
Vậy: Khi nào code gì đó bị lỗi thì cbox sẽ = Nothing và sự kiện MouseMove sẽ tự khởi tạo lại nó
------------------
Mà công nhận chú Nghĩa này tiếp cận vấn đề nhanh ghê! Gợi ý 1 chút đã làm trúng phốc!
Ẹc... Ẹc...
Em nhìn thấy code Thầy chứ! Tuy nhiên Thầy đặt con trỏ vào frame rồi để y đó, không cần rê đi đâu hết, Thầy sẽ thấy màn hình nó chớp chớp nhanh liên tục và dung lượng máy tăng lên thấy rõ rệt (theo máy em); Thầy thử test xem sao!
Em có cảm giác thế này, mà hình như là đúng: Việc ta vẽ lên sheet 1 cái Frame rồi đặt Combobox lên đó thì ComboBox hiển thị được tiếng Việt Unicode, cũng giống như đặt ComboBox này lên trên Form vậy. Như vậy cái Frame này đóng vai trò như một "điểm tựa" cho anh ComboBox đặt chân. Từ đó có thể suy ra rằng ta có thể thay Frame bởi MultiPage cũng được.
Em nhìn thấy code Thầy chứ! Tuy nhiên Thầy đặt con trỏ vào frame rồi để y đó, không cần rê đi đâu hết, Thầy sẽ thấy màn hình nó chớp chớp nhanh liên tục và dung lượng máy tăng lên thấy rõ rệt (theo máy em); Thầy thử test xem sao!
Thì cũng giống như khi ta dùng sự kiện Change hoặc SelectionChange thôi. Dù ta đã "rào" bằng dòng lệnh If Target.Address = ... hoặc If Not Intersect(....).... gì gì đó thì code sự kiện nó vẫn có công đoạn kiểm tra vậy? Kiểm tra đúng điều kiện thì nó làm, không thì... nghỉ
Vậy chẳng lẽ vì sợ cái vụ kiểm tra ấy mà ta nghỉ xài Change và SelectionChange?
---------------------------
Còn cái vụ chớp chớp giật giật gì đó thì máy tôi không bị, CPU trong Task Manager cũng không tăng (cần quay phim màn hình để chứng minh không?)
---> Suy ra: Máy Nghĩa cùi bắp? Ẹc... Ẹc...
Còn cái vụ chớp chớp giật giật gì đó thì máy tôi không bị, CPU trong Task Manager cũng không tăng (cần quay phim màn hình để chứng minh không?)
---> Suy ra: Máy Nghĩa cùi bắp? Ẹc... Ẹc...
Tải file về xem cả buổi mà chẳng hiểu cách làm thế nào để ra được như thế. Hic, tạm thời lưu file khi nào cần thì cứ đem ra xào nấu lại. Em nghĩ chắc cũng có người giống em thôi. Nếu không phải thì chắc là mình thuộc dạng .... nhất.
Cái vụ chớp màn hình gì đó thì máy tính mình cũng không bị. Anh Nghĩa thay máy tính đi nha
Nó chớp rất nhẹ chứ không phải giật giật, ở trên Window Caption đấy, không chớp trong sheet đâu! Window7 cái caption nó gần như trong suốt nên dễ nhận ra điều này.
Còn đây là "hàng" của mình đây! Làm theo hướng MouseMove (canh rất chuẩn mới không thấy "em" Frame)! Xài bằng RowSource nên không Add List!
[GPECODE=vb]
Option Explicit
Private WithEvents Fra1Cbx1 As MSForms.ComboBox
Private IsCheck As Boolean
''---------------------------------------------------
'' New version!
''---------------------------------------------------
Private Sub Frame1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If IsCheck Then Exit Sub
Set Fra1Cbx1 = Sheet1.Frame1.Controls("ComboBox1")
IsCheck = True
End Sub
Private Sub Fra1Cbx1_Change()
MsgBox Fra1Cbx1
End Sub[/GPECODE]
Đối tượng Combobox của bạn trong video này là đối tượng ở trên Userform, nhưng nó là một ActiveX Control, đặt trên Form thì không vấn đề gì rồi, nhưng bạn thử chèn nó lên Sheet xem nào. Lưu ý khi chèn trên Sheet có 2 loại đối tượng ComboBox đấy nhé, 1 cái thuộc Form Controls, 1 cái thuộc ActiveX Controls. Trong video trước của bạn tại bài #897 là ComboBox của Form Controls, mà với cái này thì cứ để thiết lập mặc định của Windows cũng hiển thị được tiếng Việt Unicode.
cái Anh hỏi là cái này chắc ko sai chứ ???? đưa file lên Youtube nó cứ đòi đợi xử lý lâu quá
[video=youtube;M7wvk_utY9g]http://www.youtube.com/watch?v=M7wvk_utY9g[/video]
Mặc định là trong hộp ActiveX Controls là không có anh này rồi đó!
Vẽ tại đâu?
Bấm vào nút More Controls (cái biểu tượng cái búa và cái khóa chéo nhau).
Sau khi hộp More Controls hiện ra, chọn vào mục Microsoft Form 2.0 Frame, rồi OK, sau đó ta vẽ trên sheet cái Frame này.
2) Add Controls lên Frame:
Trong chế độ Design, Click phải chuột vào Frame, chọn Frame Objects > Edit
Lúc này ta sẽ có được một ToolBox để add vào Frame.
3) Tạo thuộc tính cho Controls:
Click phải vào control, lúc này có một menu hoàn toàn xa lạ, kệ nó, ta chọn vào mục Properties của nó.
Lưu ý, cái Properties này nó cũng khác với các Properties ta vẫn thường thấy, đó là ta chọn một thuộc tính bất kỳ trong đó, thì nó sẽ hiện giá trị lên trên một Combobox ở trên cùng, muốn thay đổi gì thì thay đổi tại ComboBox đó rồi bấm nút Apply.
4) Bước này là tạo sự kiện cho nó, tạo như thế nào thì đã có các bài nói về chúng rồi, mình không nói lại nữa.
Private Sub Frame1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If TypeName(cbox) = "Nothing" Then
Set cbox = Sheet1.Frame1.Controls("ComboBox1")
cbox.List() = Sheet1.Range("A1:A10").Value
End If
End Sub
Với code trên, theo em nghĩ nó vẫn chưa tối ưu. Khi một code bị lỗi, ngay lập tức TẤT CẢ CÁC BIẾN sẽ giải phóng bộ nhớ hoàn toàn và trả về dạng mặc định của chúng (kể cả biến boolean cũng sẽ trả về dạng False). Vì thế việc Set lại cbox là hoàn toàn hợp lý. Nhưng việc nạp lại List cho cbox thì thừa là vì dù cho có lỗi cách nào đi chăng nữa thì List của nó không thay đổi, có nghĩa là list của nó không tự clear, cho nên ta mỗi lần Set là mỗi lần nạp list sẽ không hay. Nếu dữ liệu ngần ấy thì chả nói gì, còn như dữ liệu vài chục ngàn dòng, sau đó Filter, tính toán, Unique v.v... rồi mới tạo ra một cái Array để gán vào thì hơi ẹc ẹc ...
Theo em thì nên làm như vầy:
Mã:
Private Sub Frame1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If TypeName(cbox) = "Nothing" Then
With Frame1.Controls("ComboBox1")
[B][COLOR=#0000ff] If .ListCount = 0 Then .List = Range("A1:A10").Value[/COLOR][/B]
End With
Set cbox= Frame1.Controls("ComboBox1")
End If
End Sub
Có như vậy thì ta chỉ việc Set cho cbox thôi mà không cần phải nạp lại list một lần nào nữa!
Returns a string created by joining a number of substrings contained in an array.
Syntax:
Join(sourcearray[, delimiter])
sourcearray: Required.One-dimensional array containing substrings to be joined.
delimiter: Optional. String character used to separate the substrings in the returned string. If omitted, the space character (" ") is used. If delimiter is a zero-length string (""), all items in the list are concatenated with no delimiters.
Chắc có lẽ không ai thèm trả lời câu hỏi này, vậy mình "tự sướng" luôn! (tự sướng nên chả thú vị gì cả).
Các kiểu khai báo mảng thuộc kiểu String, Variant, hoặc không khai báo loại nào đều không phát sinh lỗi:
Mã:
Sub Test1()
Dim i As Byte
Dim Arr(0 To 9)
Dim sArr(0 To 9) As String
Dim vArray(0 To 9) As Variant
For i = 0 To 9
Arr(i) = i
sArr(i) = i
vArray(i) = i
Next
MsgBox Join(Arr, ",")
MsgBox Join(sArr, ",")
MsgBox Join(vArray, ",")
End Sub
Nhưng những dạng khai báo là kiểu Numeric (Byte, Long, Double, Date, v.v...) thì sẽ bị "dính chưởng"!
Mã:
Sub Test2()
Dim i As Byte
[COLOR=#008000]'Tat ca bien kieu Numeric deu bi loi:[/COLOR]
[B][COLOR=#0000cd]Dim Arr(0 To 9) As Long [/COLOR][COLOR=#008000]' Integer, Byte, Double, Currency, Date v.v...[/COLOR][/B][COLOR=#0000cd][/COLOR]
For i = 0 To 9
Arr(i) = i
Next
MsgBox Join(Arr, ",")
End Sub
Vì thế, khi thực hiện hàm Join thì người lập trình phải luôn luôn nằm lòng vấn đề này để mà tránh vì đôi khi mình đã làm đúng với cấu trúc rồi, nhưng tại sao lại phát sinh lỗi thì là do "kiểu nó như thế".
Nhưng những dạng khai báo là kiểu Numeric (Byte, Long, Double, Date, v.v...) thì sẽ bị "dính chưởng"!
Mã:
Sub Test2()
Dim i As Byte
[COLOR=#008000]'Tat ca bien kieu Numeric deu bi loi:[/COLOR]
[B][COLOR=#0000cd]Dim Arr(0 To 9) As Long [/COLOR][COLOR=#008000]' Integer, Byte, Double, Currency, Date v.v...[/COLOR][/B]
For i = 0 To 9
Arr(i) = i
Next
MsgBox Join(Arr, ",")
End Sub
Vì thế, khi thực hiện hàm Join thì người lập trình phải luôn luôn nằm lòng vấn đề này để mà tránh vì đôi khi mình đã làm đúng với cấu trúc rồi, nhưng tại sao lại phát sinh lỗi thì là do "kiểu nó như thế".
Bạn căn cứ vào đâu để nghĩ rằng: "vì đôi khi mình đã làm đúng với cấu trúc rồi"?. Bạn lấy cái gì ra làm chuẩn, tiêu chí nào để nghĩ là mình làm đúng?.
Người lập trình nào cũng phải đọc help. Dù là ngôn ngữ nào. Không chịu đọc help thì quên luôn chuyện lập trình đi.
Mà đọc help thì phải tìm mọi cách để hiểu. Có người cũng đọc help nhưng cóc hiểu hoặc không hiểu chính xác, không hiểu hết.
Bạn đã đọc về hàm Join trong VBA. Help nói với tôi:
Mã:
Join Function
Description
Returns a string created by joining a number of [B][COLOR=#ff0000]substrings contained in an array[/COLOR][/B].
Syntax
Join(sourcearray[, delimiter])
The Join function syntax has these named arguments:
Part Description
sourcearray Required. One-dimensional [B][COLOR=#ff0000]array containing substrings[/COLOR][/B] to be joined.
delimiter Optional. String character used to separate the substrings in the returned string. If omitted, the space character (" ") is used. If delimiter is a zero-length string (""), all items in the list are concatenated with no delimiters.
Trường hợp xanh là dạng số cũng trả kết quả là 1 và trường hợp đỏ là dạng số trong chuỗi, cũng trả kết quả là 1
Thêm nữa, 2 trường hợp mảng này, một dạng là số và một dạng số trong dạng chuỗi thì hàm Join vẫn cho ra kết quả:
Mã:
Sub Macro1()
Dim Arr()
Arr = Array(1, 2, 3, 4, 5)
MsgBox Join(Arr, ", ")
End Sub
Sub Macro2()
Dim Arr()
Arr = Array("1", "2", "3", "4", "5")
MsgBox Join(Arr, ", ")
End Sub
vậy cái Substring (chuỗi con) đâu nhất thiết phải là một chuỗi hay một số?
Và thủ tục này, i là biến kiểu Byte, nếu một biến X nào đó (không để kiểu) mà bằng i (X = i) thì X chính là dạng Byte, điều này ai cũng biết. Thế thì:
Mã:
Sub Test()
Dim i As Byte
Dim Arr(0 To 9)
For i = 0 To 9
Arr(i) = i
Next
[B][COLOR=#0000ff] MsgBox TypeName(Arr(1))[/COLOR][/B]
[COLOR=#ff0000][B] MsgBox Join(Arr, ",")[/B][/COLOR]
End Sub
Nhưng Byte thì Byte hàm JOIN vẫn "xơi" tuốt luốt, không bị "mắc xương"!
Thêm nữa, trong HELP không nói rằng "không được dùng mảng một chiều có dạng Numeric Data Type". Chính vì điều này mới đố chứ (he he) và đương nhiên nếu nó đưa câu đó ra thì ai đố làm gì vì ai cũng có thể xem Help!
Nếu bạn muốn tranh luận với tôi thì nên dùng từ chính xác.
Thêm nữa, 2 trường hợp mảng này, một dạng là số và một dạng số trong dạng chuỗi thì hàm Join vẫn cho ra kết quả:
Mã:
Sub Macro1()
Dim Arr()
Arr = Array(1, 2, 3, 4, 5)
MsgBox Join(Arr, ", ")
End Sub
Sub Macro2()
Dim Arr()
Arr = Array("1", "2", "3", "4", "5")
MsgBox Join(Arr, ", ")
End Sub
vậy cái Substring (chuỗi con) đâu nhất thiết phải là một chuỗi hay một số?
Sub Macro1()
Dim Arr()
Arr = Array(1, 2, 3, 4, 5)
MsgBox Arr(0)
MsgBox TypeName(Arr(0))
Arr(0) = "hic hic"
MsgBox Arr(0)
MsgBox TypeName(Arr(0))
End Sub
thì không có lỗi nào. Đơn giản vì mỗi phần tử của Arr là VARIANT
Nhưng chạy
Mã:
Sub Macro2()
Dim Arr(0 To 4) As Long, index As Long
For index = 0 To 4
Arr(index) = index + 1
Next
MsgBox Arr(0)
MsgBox TypeName(Arr(0))
Arr(0) = "hic hic" ' <-- lỗi
MsgBox Arr(0)
MsgBox TypeName(Arr(0))
End Sub
Sub Macro2()
Dim Arr(0 To 4) As Long, index As Long
For index = 0 To 4
Arr(index) = index + 1
Next
MsgBox Arr(0)
MsgBox TypeName(Arr(0))
Arr(0) = "7689"
MsgBox Arr(0)
MsgBox TypeName(Arr(0))
MsgBox TypeName(Arr(1))
End Sub
Đơn giản vì VBA theo tôi quá thân thiện. Bạn có thể nhập STRING nhưng phải ở dạng có thể convert được sang LONG. Lúc đó cái bạn nhập vào sẽ được convert sang LONG nếu cần thiết (nhập Long thì không cần thiết convert, nhập string thì cần) và kiểu của nó là LONG dù bạn đã nhập vào chuỗi.
Và thủ tục này, i là biến kiểu Byte, nếu một biến X nào đó (không để kiểu) mà bằng i (X = i) thì X chính là dạng Byte, điều này ai cũng biết. Thế thì:
Mã:
Sub Test()
Dim i As Byte
Dim Arr(0 To 9)
For i = 0 To 9
Arr(i) = i
Next
[B][COLOR=#0000ff] MsgBox TypeName(Arr(1))[/COLOR][/B]
[COLOR=#ff0000][B] MsgBox Join(Arr, ",")[/B][/COLOR]
End Sub
Nhưng Byte thì Byte hàm JOIN vẫn "xơi" tuốt luốt, không bị "mắc xương"!
Thêm nữa, trong HELP không nói rằng "không được dùng mảng một chiều có dạng Numeric Data Type". Chính vì điều này mới đố chứ (he he) và đương nhiên nếu nó đưa câu đó ra thì ai đố làm gì vì ai cũng có thể xem Help!
Nhưng thực chất đây là một số trong dạng chuỗi (Number stored as text) >> không tính toán được với số này! Thế thôi!
Mã:
Sub Macro2()
Dim Arr(0 To 4) As Long, index As Long
For index = 0 To 4
Arr(index) = index + 1
Next
MsgBox Arr(0)
MsgBox TypeName(Arr(0))
Arr(0) = "hic hic" ' <-- lô~i
MsgBox Arr(0)
MsgBox TypeName(Arr(0))
End Sub
Vì dạng Long không chấp nhận chuỗi nên phát sinh ra lỗi. OK
Nhưng với VD dưới đây, kiểu số nó ngán kiểu chuỗi chứ thằng kiểu chuỗi nó đâu có ngán kiểu số:
Mã:
Sub test3()
Dim Arr(0 To 3) [COLOR=#ff0000][B]As String[/B][/COLOR]
Arr(0) = "Hoang"
Arr(1) = "Trong"
Arr(2) = "Nghia"
[COLOR=#ff0000][B] Arr(3) = 2151[/B][/COLOR]
MsgBox Arr(3)
End Sub
Vì thế, JOIN là hàm xử lý ghép các phần tử trong mảng ra chuỗi (số cũng sẽ cho ra chuỗi, mà chuỗi đương nhiên là chuỗi), thì tại sao phải vấp lỗi khi gặp mảng dữ liệu kiểu số?
À, mình làm kiểu "lụi" đó mà, sau khi lấy nguồn từ Unicode cho ComboBox, CBB này đã được cài sẳn Script Vietnamese, sau đó chọn một mục trong list, rồi click vào một cell nào đó. Trong quá trình này mình chạy sự kiện LostFocus để chuyển chuỗi của CBB từ chuỗi Uni sang chuỗi CP1258. Kiểu "lụi" này không thành công.
Còn file của nmhung49 là đã tạo cái source ngay từ đầu rồi.
Nhưng thực chất đây là một số trong dạng chuỗi (Number stored as text) >> không tính toán được với số này! Thế thôi!
Vì dạng Long không chấp nhận chuỗi nên phát sinh ra lỗi. OK
Nhưng với VD dưới đây, kiểu số nó ngán kiểu chuỗi chứ thằng kiểu chuỗi nó đâu có ngán kiểu số:
Mã:
Sub test3()
Dim Arr(0 To 3) [COLOR=#ff0000][B]As String[/B][/COLOR]
Arr(0) = "Hoang"
Arr(1) = "Trong"
Arr(2) = "Nghia"
[COLOR=#ff0000][B] Arr(3) = 2151[/B][/COLOR]
MsgBox Arr(3)
End Sub
Vì thế, JOIN là hàm xử lý ghép các phần tử trong mảng ra chuỗi (số cũng sẽ cho ra chuỗi, mà chuỗi đương nhiên là chuỗi), thì tại sao phải vấp lỗi khi gặp mảng dữ liệu kiểu số?
Hùa thêm với bác Siwtom một chút nữa, theo phần tài liệu hoá của Microsoft về hàm Join tại trang dưới đây, họ viết khá rõ về tham số đầu vào cho hàm Join.
[GPECODE=csharp]Function Join(
ByVal SourceArray() As { Object | String },
Optional ByVal Delimiter As String = " "
) As String[/GPECODE] http://msdn.microsoft.com/en-us/library/b65z3h4h(v=vs.90).aspx
Ngoài ra, còn một điểm khác, khi không đặc tả kiểu số liệu của tham số thì hàm Join sẽ sử dụng tính năng chuyển đổi kiểu dữ liệu thụ động (implicit conversion) đưa số liệu về kiểu ký tự. Xem liên kết.
[http://msdn.microsoft.com/en-us/library/kca3w8x6.aspx]
Nếu đặc tả kiểu dữ liệu đầu vào thì nó sẽ sử dụng kiểu dữ liệu đó, không chuyển đổi kiểu số liệu nữa và xử lý, nếu xử lý không được thì nó sẽ báo lỗi về sai cấu trúc dữ liệu. Tất nhiên nếu khai báo kiểu số liệu là Variant thì implicit conversion sẽ được kích hoạt để đưa tham số đầu vào về khuôn khổ.
Xin được võ vẽ thêm đôi câu cùng Nghĩa!
Ví dụ mình sửa lại một tí đoạn test của Nghĩa để thí nghiệm vụ Implicit và Explicit...
[GPECODE=vb]Sub testJoin() 'Tat ca bien kieu Numeric deu bi loi:
Dim i As Long
Dim Arr1() As Date ' Integer, Byte, Double, Currency, Date v.v...
Dim Arr() As Variant
ReDim Arr1(9)
ReDim Arr(9)
For i = 0 To 9
Arr1(i) = i
Arr(i) = Arr1(i)
Next
Debug.Print Join(Arr, ",")
End Sub[/GPECODE]
Xin được võ vẽ thêm đôi câu cùng Nghĩa!
Ví dụ mình sửa lại một tí đoạn test của Nghĩa để thí nghiệm vụ Implicit và Explicit...
[GPECODE=vb]Sub testJoin() 'Tat ca bien kieu Numeric deu bi loi:
Dim i As Long
Dim Arr1() As Date ' Integer, Byte, Double, Currency, Date v.v...
Dim Arr() As Variant
ReDim Arr1(9)
ReDim Arr(9)
For i = 0 To 9
Arr1(i) = i
Arr(i) = Arr1(i)
Next
Debug.Print Join(Arr, ",")
End Sub[/GPECODE]
1) Tôi đã xem đường dẫn đó, vẫn không nói kiểu dữ liệu của mảng 1 chiều là kiểu dữ liệu nào.
2) Trong Exceptions cũng chỉ đề cập đến lỗi là không phải mảng 1 chiều
3) Với Sub TestJoin thì như bài trả lời câu đố đã nói:
Mã:
[COLOR=#ff0000]Các kiểu khai báo mảng thuộc kiểu String, Variant, hoặc không khai báo loại nào [B]đều không phát sinh lỗi[/B][/COLOR]
Thì:
Mã:
Debug.Print Join(Arr, ",")
Với Arr cũng là một mảng không khai báo kiểu dữ liệu, bởi:
Mã:
ReDim Arr(9)
Vì thế làm sao phát sinh được lỗi???
Nguyên tắc ReDim thực chất là tạo ra một mảng mới (nhưng vẫn giữ kiểu dữ liệu ban đầu), cho nên với mảng 1 chiều mà không khai báo gì ở ban đầu, hoặc khai báo kiểu chuỗi, kiểu variant thì không làm ảnh hưởng đến hàm Join.
Mà bạn làm lòng vòng chi vậy, làm như vầy là cũng test được mà:
Mã:
Sub TestDate()
Dim Arr(2)
Dim d As [COLOR=#ff0000]Date[/COLOR]
For d = 0 To 2
Arr(d) = d
Next
Debug.Print Join(Arr, ",")
''Hoac:
Arr(0) = #1/1/2013#
Arr(1) = #1/2/2013#
Arr(2) = #1/3/2013#
Debug.Print Join(Arr, ",")
End Sub
XIN LƯU Ý CHO CÂU ĐỐ NÀY: CẤU TRÚC JOIN KHÔNG SAI VÌ TA DÙNG MẢNG 1 CHIỀU, NÓ CŨNG KHÔNG "KÉN" KIỂU DỮ LIỆU CỦA CÁC PHẦN TỬ BÊN TRONG MẢNG, NÓ CHỈ BỊ LỖI ĐỐI VỚI KIỂU DỮ LIỆU CỦA MẢNG LÀ KIỂU NUMERIC MÀ THÔI!
[GPECODE=vb]Function Join(
ByVal SourceArray() As { Object | String },
Optional ByVal Delimiter As String = " "
) As String[/GPECODE]
Nói khác hơn nó đòi hỏi kiểu dữ liệu đầu vào phải là như vậy - khi viết hướng dẫn hàm, họ sẽ không nói quá dài được, phần remark thì nó định nghĩa như thế rồi (Required. One-dimensional array containing substrings to be joined). Tạm dịch là: Bắt buộc: Mảng một chiều chứa các chuỗi con cần nối.
Nếu bẻ chữ một chút, điều này có thể coi là (1) Các chuỗi con của một mảng được đặc tả kiểu, tuy nhiên các phần tử của mảng lại được gán với các giá trị là chuỗi, vì vậy trình biên dịch sẽ tự dịch từ chuỗi sang kiểu số liệu của mảng khi thực hiện phép gán; và nếu gán được thì tốt, gán không được nó sẽ báo lỗi. hoặc (2) Muốn các chuỗi con, hay các phần tử của mảng có kiểu số liệu là chuỗi thì mảng đó cũng phải có kiểu số liệu là chuỗi. Thiết nghĩ điều đó đã rõ ràng rồi mà!
Khi kiểu dữ liệu tham số không được đặc tả hoặc đặc tả dạng không cụ thể (variant chẳng hạn) thì cơ chế biên dịch sẽ tự động dịch sang kiểu dữ liệu cần thiết cho hàm xử lý (Implicit Conversion).
Nhưng nếu đã đặc tả kiểu (số, ngày, giờ ...vv) thì cơ chế chuyển kiểu dữ liệu sẽ không được thực hiện và nó sẽ đưa nguyên số liệu đầu vào theo cách ban đầu Nghĩa đã đặc tả. Và như vậy sẽ gây lỗi số 5 (sai cấu trúc).
Còn trong ví dụ của mình, việc redim chỉ là nghịch ngợm tí thôi - cái quan trọng là lệnh gán từ kiểu số sang mảng không định nghĩa kiểu dữ liệu ý. Cái đó nó sẽ bảo trình dịch tiếp tục thực hiện việc chuyển đổi kiểu số liệu thụ động để đưa đúng món ăn vào cho hàm Join.
Xin võ vẽ thế!
[GPECODE=vb]Function Join(
ByVal SourceArray() As { Object | String },
Optional ByVal Delimiter As String = " "
) As String[/GPECODE]
Nói khác hơn nó đòi hỏi kiểu dữ liệu đầu vào phải là như vậy - khi viết hướng dẫn hàm họ sẽ không nói quá dài được, phần remark thì nó định nghĩa như thế rồi (Required. One-dimensional array containing substrings to be joined.).
Khi kiểu dữ liệu tham số không được đặc tả hoặc đặc tả dạng không cụ thể (variant chẳng hạn) thì cơ chế biên dịch sẽ tự động dịch sang kiểu dữ liệu cần thiết cho hàm xử lý (Implicit Conversion).
Nhưng nếu đã đặc tả kiểu (số, ngày, giờ ...vv) thì cơ chế chuyển kiểu dữ liệu sẽ không được thực hiện và nó sẽ đưa nguyên số liệu đầu vào theo cách ban đầu Nghĩa đã đặc tả. Và như vậy sẽ gây lỗi số 5 (sai cấu trúc).
Còn trong ví dụ của mình, việc redim chỉ là nghịch ngợm tí thôi - cái quan trọng là lệnh gán từ kiểu số sang mảng không định nghĩa kiểu dữ liệu ý. Cái đó nó sẽ bảo trình dịch tiếp tục thực hiện việc chuyển đổi kiểu số liệu thụ động để đưa đúng món ăn vào cho hàm Join.
Xin võ vẽ thế!
Nghĩa ơi, cái SourceArray() As{ Object| String} mà còn không thể hiện kiểu SourceArray là String à! Cú pháp viết tham số hàm đây thôi!
Thông thường phần giải thích thêm chỉ giúp ta hiểu thêm một chút thôi còn cái gì đã quy định trong cú pháp thì cứ y thế mà làm. Có chăng phần giải thích thêm của bác Bill nên thêm mấy chữ. Mảng chứa các phần tử cần nối phải là mảng một chiều có kiểu dữ liệu là chuỗi (One-dimensional array of string data-type containing string elements to be joined).
Ví dụ: ByVal XX() as Long hoặc ByVal YY() as String thì ta nên hiểu thế nào?
Bài viết 942 mình có viết thêm mấy chữ nữa trong lúc bạn đang viết bài!
Tôi không nói về mình, nhưng theo tôi nghĩ thì có nhiều người trên GPE có thể trả lời câu hỏi của bạn. Chỉ có điều có thể người ta không quan tâm, người ta không đọc chủ đề của bạn hoặc đơn giản người ta chưa truy cập GPE. Mà tham gia trả lời có phải nghĩa vụ đâu.
Nhưng nếu cái bạn viết trên làm cho bạn thấy vui hơn thì là tốt rồi.
Nghĩa ơi, cái SourceArray() As{ Object| String} mà còn không thể hiện kiểu SourceArray là String à! Cú pháp viết tham số hàm đây thôi!
Thông thường phần giải thích thêm chỉ giúp ta hiểu thêm một chút thôi còn cái gì đã quy định trong cú pháp thì cứ y thế mà làm. Có chăng phần giải thích thêm của bác Bill nên thêm mấy chữ. Mảng chứa các phần tử cần nối phải là mảng một chiều có kiểu dữ liệu là chuỗi (One-dimensional array of string data-type containing string elements to be joined).
Ví dụ: ByVal XX() as Long hoặc ByVal YY() as String thì ta nên hiểu thế nào?
Bài viết 942 mình có viết thêm mấy chữ nữa trong lúc bạn đang viết bài!
Cũng như ListBox ta chỉ được tạo 10 cột, nhưng với tiểu xảo ta vẫn tạo số cột nhiều hơn.
Hàm JOIN này cũng vậy, nếu ta khai báo rõ ràng, tường mình thì đôi khi lại gặp lỗi, nhưng ngược lại, nếu ta không tường minh thì lại chạy hiệu quả (mặc dù bên trong toàn dữ liệu kiểu numeric).
Nói vậy cho mọi người hiểu rằng đôi khi ta vẫn "lách luật" trong VBA được là vậy.
Cũng như ListBox ta chỉ được tạo 10 cột, nhưng với tiểu xảo ta vẫn tạo số cột nhiều hơn.
Hàm JOIN này cũng vậy, nếu ta khai báo rõ ràng, tường mình thì đôi khi lại gặp lỗi, nhưng ngược lại, nếu ta không tường minh thì lại chạy hiệu quả (mặc dù bên trong toàn dữ liệu kiểu numeric).
Nói vậy cho mọi người hiểu rằng đôi khi ta vẫn "lách luật" trong VBA được là vậy.
cái này bac "siwtom" đã nói rõ ở bài trên rồi
đồng thời Ông Bill cũng đã định nghĩa rõ ràng hàm Join là làm nối các chuỗi trong mảng thành 1 chuỗi
vậy theo Anh ý nó là gì????
theo mình hiểu câu ấy là Join sẽ nối các chuỗi ký tự lại với nhau, nhưng các số vẫn có thể nối với nhau bởi vì Ông Bill hiểu các số ấy ko phải là các "con số "mà là "chữ số " (là chuỗi)
còn khi anh khai báo biến là biến số thì anh đang ép Ông Bill hiểu đây là những con số nên Ông Bill phản lại : đây ko phải công việc của tôi, tôi ko cố nghĩa vụ phải làm, nên việc này tao ko làm ------> dẫn đến báo lỗi thế thôi
đùa chút thôi theo mình nghĩ quan điểm của Ông Bill là các con số thì phải dùng trong tính toán chứ ko phải dùng để nối nên việc nối chỉ cho phép thực hiện ở chế độ chuỗi
cái này bac "siwtom" đã nói rõ ở bài trên rồi
đồng thời Ông Bill cũng đã định nghĩa rõ ràng hàm Join là làm nối các chuỗi trong mảng thành 1 chuỗi
vậy theo Anh ý nó là gì????
theo mình hiểu câu ấy là Join sẽ nối các chuỗi ký tự lại với nhau, nhưng các số vẫn có thể nối với nhau bởi vì Ông Bill hiểu các số ấy ko phải là các "con số "mà là "chữ số " (là chuỗi)
còn khi anh khai báo biến là biến số thì anh đang ép Ông Bill hiểu đây là những con số nên Ông Bill phản lại : đây ko phải công việc của tôi, tôi ko cố nghĩa vụ phải làm, nên việc này tao ko làm ------> dẫn đến báo lỗi thế thôi
đùa chút thôi theo mình nghĩ quan điểm của Ông Bill là các con số thì phải dùng trong tính toán chứ ko phải dùng để nối nên việc nối chỉ cho phép thực hiện ở chế độ chuỗi
The Variant data type is the data type for all variables that are not explicitly declared as some other type (using statements such as Dim, Private, Public, or Static). The Variant data type has no type-declaration character.
A Variant is a special data type that can contain any kind of data except fixed-length String data. (Variant types now support user-defined types.) A Variant can also contain the special values Empty, Error, Nothing, and Null. You can determine how the data in a Variant is treated using the VarType function or TypeName function.
Numeric data can be any integer or real number value ranging from -1.797693134862315E308 to -4.94066E-324 for negative values and from 4.94066E-324 to 1.797693134862315E308 for positive values. Generally, numeric Variant data is maintained in its original data type within the Variant. For example, if you assign an Integer to a Variant, subsequent operations treat the Variant as an Integer. However, if an arithmetic operation is performed on a Variant containing a Byte, an Integer, a Long, or a Single, and the result exceeds the normal range for the original data type, the result is promoted within the Variant to the next larger data type. A Byte is promoted to an Integer, an Integer is promoted to a Long, and a Long and a Single are promoted to a Double. An error occurs when Variant variables containing Currency, Decimal, and Double values exceed their respective ranges.
You can use the Variant data type in place of any data type to work with data in a more flexible way. If the contents of a Variant variable are digits, they may be either the string representation of the digits or their actual value, depending on the context. For example:
Dim MyVar As Variant
MyVar = 98052
In the preceding example,
MyVar
contains a numeric representation — the actual value
98052
. Arithmetic operators work as expected on Variant variables that contain numeric values or string data that can be interpreted as numbers. If you use the + operator to add
MyVar
to another Variant containing a number or to a variable of a numeric type, the result is an arithmetic sum.
The value Empty denotes a Variant variable that hasn't been initialized (assigned an initial value). A Variant containing Empty is 0 if it is used in a numeric context and a zero-length string ("") if it is used in a string context.
Don't confuse Empty with Null.Null indicates that the Variant variable intentionally contains no valid data.
In a Variant, Error is a special value used to indicate that an error condition has occurred in a procedure. However, unlike for other kinds of errors, normal application-level error handling does not occur. This allows you, or the application itself, to take some alternative action based on the error value. Error values are created by converting real numbers to error values using the CVErr function.
đương nhiên đoạn code trên chạy tốt rồi, vì như Bác đã nói "đỏ đỏ" đó đã được chấp thuận, nên em nó phải làm việc thôi, vì khi ko khai báo mảng Arr thuôc loại nào mặt nhiên nó là variant , tuy biến i đc khai báo nhưng mảng vẫn là variant Nhưng có ai đó nói: Thế tại sao với code
vậy cảm phiềm test thêm 2 đoạn code này
[GPECODE=vb]Sub Test()
Dim i As Byte
Dim Arr(1 To 9) as Byte
For i = 1 To 9
Arr(i) = i
Next
MsgBox TypeName(Arr(1))
MsgBox Join(Arr, ",")
End Sub[/GPECODE]
ta thiết lập mạng là kiểu byte Dim Arr(1 To 9) as Byte
và đoạn này [GPECODE=vb]Sub Test()
Dim i As Byte
Dim Arr(1 To 9)
For i = 1 To 9
Arr(i) = i
Next
MsgBox TypeName(Arr)
MsgBox Join(Arr, ",")
End Sub[/GPECODE]
MsgBox TypeName(Arr)
Join đang làm việc với mảng nên mình cần xem mảng đó là mảng kiểu gì mà thôi
Thầy ơi, khi em xài cái Frame này ở WinXP, Excel 2003 thì khi mở file nó hiện thông báo như thế này (xem hình), chọn Yes thì nó cho sử dụng, No thì như cái shape vậy đó. Cho em hỏi cách khắc phục đừng cho nó hiện thông báo này mỗi lần mở thì như thế nào ạ?
Nội dung:
This condition is related to how ActiveX controls are initialized within Office applications. ActiveX controls are programs and can store data in either the registry or within the control itself. ActiveX controls are similar to Visual Basic for Applications macros except they are compiled to a special file type and can be linked to a data file that can support them (for instance, Word documents and Excel workbooks). When you receive a file with an ActiveX control linked to it, the possibility that there may be "persistent" data (data stored with the control) is unknown; the data could be malicious - used to plant a virus or to modify your computer with the intent to do harm. Office applications provide, by default, the ability to block ActiveX controls from using persisted data. However, if you have specifically changed the default setting for ActiveX initialization during the installation process, or by using a policy, the system will perform the initialization of the ActiveX control based on your custom settings.If you choose to allow the application to run the control and to allow it to use persisted data, you do so at your own risk. It is highly recommended to not run ActiveX controls in documents submitted to you by people you do not know. However, if you trust the source of the document and the individual or company who sent it to you, then you can load the control and allow it to use persisted data. However, this is no guarantee that the data associated with the control is actually safe to use.
There are limitations to the ability of Office applications to migrate ActiveX control link references from one format of a document or file to another format. For instance, you can link an ActiveX control to a Word document and save that file. However, when you convert that Word document to another format the link reference to the ActiveX control might not convert into that file format. Therefore, conversion of files that have links to ActiveX controls may not be supported for all file types, partly because not all file types can support ActiveX control linking or activation. For files with links to ActiveX controls, it is necessary to accept and activate the ActiveX control prior to converting the file to another file type that supports ActiveX linking and activation. If the ActiveX control is not activated, the link reference is dropped from the file and will not appear in the converted file. More information about this error message online.
Thầy ơi, khi em xài cái Frame này ở WinXP, Excel 2003 thì khi mở file nó hiện thông báo như thế này (xem hình), chọn Yes thì nó cho sử dụng, No thì như cái shape vậy đó. Cho em hỏi cách khắc phục đừng cho nó hiện thông báo này mỗi lần mở thì như thế nào ạ?
Lang thang trên Google, mãi mới tìm được cách "khống chế", cũng để cho nó hoạt động mà không thông báo, chứ cách này liên quan đến an toàn (http://support.microsoft.com)
Làm theo các bước sau:
1) Bấm start, bấm run, gõ regedit, và sau đó bấm OK.
Thiết lập như thế này thật tình là không được nếu ta dùng sự kiện Workbook_Open và trên file đó chứa ActiveX Controls (cái thông báo nó nhảy ra trước khi sự kiện hoạt động).
Mã:
Sub UFIControlsSetting(ByVal Input_from_1_to_6 As Byte)
''Chi nhap tu 1 den 6
''Kem an toan nhat: 1
''An toan nhat: 4
Dim regKey As String
regKey = "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\Common\Security\UFIControls"
CreateObject("WScript.Shell").RegWrite regKey, Input_from_1_to_6, "REG_DWORD"
End Sub
Mã:
Private Sub Workbook_Open()
UFIControlsSetting 1
End Sub
Hôm nay mình muốn đưa ra 1 câu đố vui, mong anh em cùng thử 1 chút:
Trên Form UserForms1 chỉ có 1 Button là CommandButton1. Không thêm bất kể 1 đối tượng nào trên Form và không dùng hình nền cho Form làm sao có thể khi nhấn Button sẽ xuất hiện dòng chữ WELLCOME lên trên Form khi RunTime
Hôm nay mình muốn đưa ra 1 câu đố vui, mong anh em cùng thử 1 chút:
Trên Form UserForms1 chỉ có 1 Button là CommandButton1. Không thêm bất kể 1 đối tượng nào trên Form và không dùng hình nền cho Form làm sao có thể khi nhấn Button sẽ xuất hiện dòng chữ WELLCOME lên trên Form khi RunTime
Hôm nay mình muốn đưa ra 1 câu đố vui, mong anh em cùng thử 1 chút:
Trên Form UserForms1 chỉ có 1 Button là CommandButton1. Không thêm bất kể 1 đối tượng nào trên Form và không dùng hình nền cho Form làm sao có thể khi nhấn Button sẽ xuất hiện dòng chữ WELLCOME lên trên Form khi RunTime
Bài này nếu dùng API thì không có vấn đề gì cả (cho hiển thị Text lên bất cứ đâu cũng được)
Vấn đề là do đang ĐỐ VUI nên không biết anh sealandcó mẹo gì không? Nếu anh sealand khẳng định loại trừ cách dùng hàm API thì quả thật mình chưa nghĩ ra được cách gì khác
Private Sub CommandButton1_Click()
Dim Frm As [COLOR=#ff0000]MSforms.UserForm[/COLOR] ' MSforms.Frame ' Object
Set Frm = CommandButton1.Parent
Frm.Caption = "WELLCOME"
End Sub
Private Sub CommandButton2_Click()
Dim Frm As [COLOR=#ff0000]MSforms.UserForm[/COLOR]
Set Frm = CommandButton2.Parent
Frm.Caption = "WELLCOME"
End Sub
Giả sử tôi có 3 sheet
- Sheet thứ nhất có tên là "Nguyễn" và Sheet CodeName là "S1"
- Sheet thứ hai có tên là "Anh" và Sheet CodeName là "S2"
- Sheet thứ ba có tên là "Tuấn" và Sheet CodeName là "S3"
-----------
Giờ ta gõ:
- Cell A1 chữ S1
- Cell A2 chữ S2
- Cell A3 chữ S3 Xin hỏi: Dùng code thế nào để:
- Kết quả tại B1 là Nguyễn
- Kết quả tại B2 là Anh
- Kết quả tại B3 là Tuấn
-------------
Tức biến đầu vào là Sheet CodeName (dạng String) và kết quả đầu ra là Sheet Name (dạng String)
Câu hỏi ngược lại: Viết một hàm để tìm Sheet CodeName nếu biết Sheet Name. (Chỉ một dòng Code tương tự bài #521) Mở rộng: Dùng vòng lặp duyệt qua các WorkSheets để đổi tên tất cả các Sheet CodeName.
Câu hỏi ngược lại: Viết một hàm để tìm Sheet CodeName nếu biết Sheet Name. (Chỉ một dòng Code tương tự bài #521) Mở rộng: Dùng vòng lặp duyệt qua các WorkSheets để đổi tên tất cả các Sheet CodeName.
Sub RenameSheetCodeName(ByVal wb As Workbook, ByVal SheetName As String, ByVal newSheetCodeName As String)
Dim CodeName As String
CodeName = wb.Sheets(SheetName).CodeName ' đọc Sheet CodeName nếu biết Sheet Name
wb.VBProject.VBComponents(CodeName).name = newSheetCodeName
End Sub
Sub RenameSheetCodeName(ByVal wb As Workbook, ByVal SheetName As String, ByVal newSheetCodeName As String)
Dim CodeName As String
CodeName = wb.Sheets(SheetName).CodeName ' đọc Sheet CodeName nếu biết Sheet Name
wb.VBProject.VBComponents(CodeName).name = newSheetCodeName
End Sub
Cho danh mục các tên Sheet vào cột A, với dòng code đó, bạn làm sao để viết hàm để lấy CodeName các sheet đã liệt kê vào cột B đây?, yêu cầu là viết hàm mà, hàm đó cũng có thể dùng trong VBA.
Rồi thêm câu hỏi khuyến mãi nữa: Thủ tục đổi tên tất cả các Sheet CodeName trong các WorkSheets
PS: Bạn chỉ giùm đường dẫn chổ nào đã có "cái này" trên GPE!
Cho danh mục các tên Sheet vào cột A, với dòng code đó, bạn làm sao để viết hàm để lấy CodeName các sheet đã liệt kê vào cột B đây?, yêu cầu là viết hàm mà, hàm đó cũng có thể dùng trong VBA.
Cho danh mục các tên Sheet vào cột A, với dòng code đó, bạn làm sao để viết hàm để lấy CodeName các sheet đã liệt kê vào cột B đây?, yêu cầu là viết hàm mà, hàm đó cũng có thể dùng trong VBA.
Rồi thêm câu hỏi khuyến mãi nữa: Thủ tục đổi tên tất cả các Sheet CodeName trong các WorkSheets
PS: Bạn chỉ giùm đường dẫn chổ nào đã có "cái này" trên GPE!
Function CodeShName(ByVal ShName As String) As String
CodeShName = Sheets(ShName).CodeName
End Function
Công thức cho B1
Mã:
=CodeShName(A1)
Sử dụng trong VBA
PHP:
Sub Test()
MsgBox CodeShName("Sheet1")
End Sub
Đổi tên tất cả các Sheet CodeName
PHP:
Sub ChangeAllCodeShName()
Dim Sh As Worksheet, i As Long
For Each Sh In Sheets
If Sh.Type = xlWorksheet Then
i = i + 1
ActiveWorkbook.VBProject.VBComponents(Sh.CodeName).Name = "CodeShName" & i
End If
Next
End Sub
Function CodeShName(ByVal ShName As String) As String
CodeShName = Sheets(ShName).CodeName
End Function
Công thức cho B1
Mã:
=CodeShName(A1)
Sử dụng trong VBA
PHP:
Sub Test()
MsgBox CodeShName("Sheet1")
End Sub
Đổi tên tất cả các Sheet CodeName
PHP:
Sub ChangeAllCodeShName()
Dim Sh As Worksheet, i As Long
For Each Sh In Sheets
If Sh.Type = xlWorksheet Then
i = i + 1
ActiveWorkbook.VBProject.VBComponents(Sh.CodeName).Name = "CodeShName" & i
End If
Next
End Sub
Hì mình chịu bạn rồi, sory nha!, tại vì phương án của mình đưa ra hơi khác, không nghờ bạn lại có một cách khác Mình dùng Properties tương tự như HLMT làm ở đây.
Cho danh mục các tên Sheet vào cột A, với dòng code đó, bạn làm sao để viết hàm để lấy CodeName các sheet đã liệt kê vào cột B đây?, yêu cầu là viết hàm mà, hàm đó cũng có thể dùng trong VBA.
Rồi thêm câu hỏi khuyến mãi nữa: Thủ tục đổi tên tất cả các Sheet CodeName trong các WorkSheets
PS: Bạn chỉ giùm đường dẫn chổ nào đã có "cái này" trên GPE!
Cho danh mục các tên Sheet vào cột A, với dòng code đó, bạn làm sao để viết hàm để lấy CodeName các sheet đã liệt kê vào cột B đây?, yêu cầu là viết hàm mà, hàm đó cũng có thể dùng trong VBA.
Rồi thêm câu hỏi khuyến mãi nữa: Thủ tục đổi tên tất cả các Sheet CodeName trong các WorkSheets
Chẳng liên quan gì tới tôi nhưng xin phép cho tôi thắc mắc: tại sao lại phải dùng hàm? Nếu dùng hàm thì trả về cái gì? Công việc là lấy dữ liệu cột A, kết quả ghi vào cột B. Vậy thì mục đích của trị được hàm trả về là gì?
Bạn cần Hàm thì ta vẫn sử dụng Hàm được chớ sao.
Mình ngại viết lại Code lấy danh sách từ sheet mà đổi luôn Sub của HuuThang thử xem
Mã:
Function ChangeAllCodeShName() As Boolean
Dim Sh As Worksheet, i As Long
For Each Sh In Sheets
If Sh.Type = xlWorksheet Then
i = i + 1
ActiveWorkbook.VBProject.VBComponents(Sh.CodeName).Name = "CodeShName" & i
End If
Next
ChangeAllCodeShName = True
End Function
'------------------------------------------------------------------------------------
Sub Test()
MsgBox ChangeAllCodeShName()
End Sub
- Tôi vẽ 1 Button và Assign Macro Button này với Sub GiGiDo
- Xin hỏi:Tôi phải viết gì trong Sub để chỉ cho phép chạy code khi nhấn vào Button mà thôi ---> Có nghĩa là không chạy được trực tiếp trong cửa sổ lập trình, cũng không chạy được bằng cách Alt + F8 để gọi Sub
-------------------
Nói thêm: Nội dung Sub GiGiDo các bạn muốn viết cái gì tùy ý, miễn đạt được mục đích như dòng màu xanh ở trên
Câu đố này rất dễ
Chẳng liên quan gì tới tôi nhưng xin phép cho tôi thắc mắc: tại sao lại phải dùng hàm? Nếu dùng hàm thì trả về cái gì? Công việc là lấy dữ liệu cột A, kết quả ghi vào cột B. Vậy thì mục đích của trị được hàm trả về là gì?
Bạn cần Hàm thì ta vẫn sử dụng Hàm được chớ sao.
Mình ngại viết lại Code lấy danh sách từ sheet mà đổi luôn Sub của HuuThang thử xem
Mã:
Function ChangeAllCodeShName() As Boolean
Dim Sh As Worksheet, i As Long
For Each Sh In Sheets
If Sh.Type = xlWorksheet Then
i = i + 1
ActiveWorkbook.VBProject.VBComponents(Sh.CodeName).Name = "CodeShName" & i
End If
Next
ChangeAllCodeShName = True
End Function
'------------------------------------------------------------------------------------
Sub Test()
MsgBox ChangeAllCodeShName()
End Sub
Em nhất định bảo đấy là hàm rồi nhưng không phải hàm em cần, hu hu ...
Còn đây là code của mình:
Mã:
Sub EditWorksheetCodenames()
Dim Ws As Worksheet, i As Integer
If TypeName(ActiveWorkbook) = "Nothing" Then Exit Sub
For Each Ws In ActiveWorkbook.Worksheets
i = i + 1
On Error Resume Next
'Cells(i, 1) = Ws.Name
Ws.Parent.VBProject.vbcomponents(Ws.CodeName).Properties("_CodeName") = "GPE" & i
'On Error GoTo 0
Next Ws
End Sub
Function shCodeName(ByVal shName As String) As String
On Error Resume Next ' Neu khong tim thay Sheet shName
shCodeName = ActiveWorkbook.VBProject.vbcomponents(Sheets(shName).CodeName).Properties("_CodeName")
End Function
BUTTON thì mới Assign Macro được chứ anh!
Còn anh thì lại sửa thành COMMANDBUTTON, 2 loại control khác nhau hoàn toàn
Thêm nữa, em ghi Sub GiGiDo() hoàn toàn không có cái vụ "tham số truyền" ở đây! Chỉ hỏi mọi người về NỘI DUNG của sub thôi
Tóm lại: Nội dung code của Sub này sẽ quyết định tất cả
Sub EditWorksheetCodenames()
Dim Ws As Worksheet, i As Integer
If TypeName(ActiveWorkbook) = "Nothing" Then Exit Sub
For Each Ws In ActiveWorkbook.Worksheets
i = i + 1
On Error Resume Next
'Cells(i, 1) = Ws.Name
Ws.Parent.VBProject.vbcomponents(Ws.CodeName).Properties("_CodeName") = "GPE" & i
'On Error GoTo 0
Next Ws
End Sub
Function shCodeName(ByVal shName As String) As String
On Error Resume Next ' Neu khong tim thay Sheet shName
shCodeName = ActiveWorkbook.VBProject.vbcomponents(Sheets(shName).CodeName).Properties("_CodeName")
End Function
- Tôi vẽ 1 Button và Assign Macro Button này với Sub GiGiDo
- Xin hỏi:Tôi phải viết gì trong Sub để chỉ cho phép chạy code khi nhấn vào Button mà thôi ---> Có nghĩa là không chạy được trực tiếp trong cửa sổ lập trình, cũng không chạy được bằng cách Alt + F8 để gọi Sub
-------------------
Nói thêm: Nội dung Sub GiGiDo các bạn muốn viết cái gì tùy ý, miễn đạt được mục đích như dòng màu xanh ở trên
Câu đố này rất dễ
procedure TForm1.Button1Click([COLOR=#ff0000]Sender[/COLOR]: TObject);
begin
' code A kiểm tra Sender
end;
Nếu ta nhấn Button1 và trong procedure/sub đọc ra thì Sender chính là đối tượng Button1.
Nhưng nếu ta gọi thế này:
Mã:
procedure TForm1.Button2Click(Sender: TObject);
begin
Button1Click(Form1); '<-- tức CALL sub Button1Click
end;
thì code A sẽ trả về đối tượng Sender = Form1. THay cho Form1 có thể truyền bắt cứ con, cháu, chắt, chít nào của TObject (Object), tức class/object di truyền từ TObject (Object), hoặc truyền NIL (trong VBA tương ứng là NOTHING.
------------
Trong VBA thì chắc là Caller
Mã:
Sub GiGiDo()
If TypeName(Application.Caller) = "String" Then
If Application.Caller = "Button1" Then
MsgBox "Xin moi xin moi"
Else
MsgBox "bye-bye"
End If
Else
MsgBox "bye-bye"
End If
End Sub
Sub test()
GiGiDo
End Sub
Gọi test không được, nhấn Button2 (cùng macro) cũng không được
Thì ndu cũng thường nhắc nhở rằng topic này là để vui, mới lạ, độc đáo mà.
Trước khi hỏi mình cũng tìm hiểu Properties của Sheet CodeName đâu có trên GPE đâu. Hỏi để tìm giải pháp mới, với mục đích làm phong phú cho Forum chớ chưa hẳn đã hay hoặc hay nhất.
Trước đây mình đã thấy một thấy một số cao thủ nói rằng Sheet CodeName là thuộc tính Read-only rất khó Edit bằng code sao gọi là chuyện bình thường được?
Đó chính là vấn đề (cái Caller)
Còn việc viết gì trong Sub thì tùy ý mọi người, miễn sao có sự tồn tại của Caller để CHỈ DUY NHẤT BUTTON mới gọi được Sub
Thế thôi!
Như em, viết thế này cũng được
Mã:
Sub GiGiDo()
MsgBox Application.Caller
End Sub
(Đúng ra sau 3 ngày nữa, nếu không ai trả lời thì anh siwtom mới có quyền trả lời, thế mới công bằng chứ nhỉ?)
Ẹc... Ẹc...
---------------
Trước đây mình đã thấy một thấy một số cao thủ nói rằng Sheet CodeName là thuộc tính Read-only rất khó Edit bằng code sao gọi là chuyện bình thường được?
Edit thì khó nhưng chỉ LẤY TÊN CÙA NÓ thì đúng là chuyện bình thường mà anh! Và em cũng chỉ đang nói về vụ lấy tên thôi
(trong khi chỉ cái chuyện lấy tên bình thường ấy anh lại đi đường vòng cho mất công.... Ẹc... Ẹc...)
...
Có lẽ bạn chưa biết Sheet CodeName, bạn xem hàm trong file bên dưới.
...
Còn đây là code của mình:
Mã:
[COLOR=#ff0000][B]Sub EditWorksheetCodenames()[/B][/COLOR]
Dim Ws As Worksheet, i As Integer
If TypeName(ActiveWorkbook) = "Nothing" Then Exit Sub
For Each Ws In ActiveWorkbook.Worksheets
i = i + 1
On Error Resume Next
'Cells(i, 1) = Ws.Name
Ws.Parent.VBProject.vbcomponents(Ws.CodeName).Properties("_CodeName") = "GPE" & i
'On Error GoTo 0
Next Ws
End Sub
Function shCodeName(ByVal shName As String) As String
On Error Resume Next ' Neu khong tim thay Sheet shName
shCodeName = ActiveWorkbook.VBProject.vbcomponents(Sheets(shName).CodeName).Properties("_CodeName")
End Function
Việc biết hay không biết Sheet CodeName chả liên quan gì đến hàm cả.
EditWorksheetCodenames() trả về cái gì mới quan trọng. Theo code trên thì nó là Sub.
Thắc mắc của tôi thực ra là câu đố ngầm. Nên cho nó trả về cái gì?
Theo như code trước đây thì có vài bạn muốn trả về True/False. Nhưng nếu tôi viết hàm này thì tôi cho nó trả về số sheetsmà nó đổi tên - dĩ nhiên là nếu nó chả đổi sheet nào thì nó trả về 0.
Đó chính là vấn đề (cái Caller)
Còn việc viết gì trong Sub thì tùy ý mọi người, miễn sao có sự tồn tại của Caller để CHỈ DUY NHẤT BUTTON mới gọi được Sub
Thế thôi!
Như em, viết thế này cũng được
^^ bh em biết thêm được caller <--- cảm ơn diễn đàn GPE nói chung và anh Ndu, Switom nói riêng
Nếu em viết như vầy :
Mã:
Sub GPE()
Dim button1 As Button
Set button1 = Worksheets(1).Buttons(Application.Caller)
MsgBox button1.Name
End Sub
Application.Caller --> như là một Name động của các button vậy , ta chỉ cần click bất kỳ button nào thì code đều chạy hết --> em hiểu vậy có đúng không các anh nhj ??
^^ bh em biết thêm được caller <--- cảm ơn diễn đàn GPE nói chung và anh Ndu, Switom nói riêng
Nếu em viết như vầy :
Mã:
Sub GPE()
Dim button1 As Button
Set button1 = Worksheets(1).Buttons(Application.Caller)
MsgBox button1.Name
End Sub
Application.Caller --> như là một Name động của các button vậy , ta chỉ cần click bất kỳ button nào thì code đều chạy hết --> em hiểu vậy có đúng không các anh nhj ??
Bất cứ button nào Assign Maco với Sub GPE chứ
Giải thích thế này sẽ dễ hiểu:
- TUI chính là Sub GPE
- Còn thằng nào "gọi điện thoại" đến TUI thì thằng đó là CALLER
Sub FindFrozenCell()
With ActiveWindow
If .Split Then
.ScrollRow = 1
.ScrollColumn = 1
MsgBox ActiveWindow.VisibleRange(1, 1).Address(0, 0)
Else
MsgBox "Khong co cot, hang nao bi dong bang."
End If
End With
End Sub
Nếu không có ScrollRow và ScrollColumn thì sẽ không chính xác được địa chỉ giao nhau đâu nhé!
Sub FindFrozenCell()
With ActiveWindow
If .Split Then
.ScrollRow = 1
.ScrollColumn = 1
MsgBox [B][COLOR=#ff0000]ActiveWindow.VisibleRange(1, 1)[/COLOR][/B].Address(0, 0)
Else
MsgBox "Khong co cot, hang nao bi dong bang."
End If
End With
End Sub
Nếu không có ScrollRow và ScrollColumn thì sẽ không chính xác được địa chỉ giao nhau đâu nhé!
Sẳn đây đố luôn, nếu thay "cái vụ" màu đỏ thành một thủ tục khác, vậy theo các bạn sẽ thay vào đó cái gì để có kết quả như chưa thay? Nói chung là không dùng VisibleRange.
Sub FindFrozenCell()
With ActiveWindow
If .Split Then
.ScrollRow = 1
.ScrollColumn = 1
MsgBox ActiveWindow.VisibleRange(1, 1).Address(0, 0)
Else
MsgBox "Khong co cot, hang nao bi dong bang."
End If
End With
End Sub
Nếu không có ScrollRow và ScrollColumn thì sẽ không chính xác được địa chỉ giao nhau đâu nhé!
Chắc HLMT đang nói đến Panes(x) với x từ 1 đến 4 phải không? Nhưng trước khi làm phải Panes.Count trước đấy nhé, không thôi sẽ gặp lỗi đấy! (chỉ đóng băng dòng hoặc đóng băng cột).
Nhưng để tránh rắc rối, hay bẩy lỗi nhiều, mình không dùng VisibleRange luôn.
Chắc HLMT đang nói đến Panes(x) với x từ 1 đến 4 phải không? Nhưng trước khi làm phải Panes.Count trước đấy nhé, không thôi sẽ gặp lỗi đấy! (chỉ đóng băng dòng hoặc đóng băng cột).
Nhưng để tránh rắc rối, hay bẩy lỗi nhiều, mình không dùng VisibleRange luôn.
Sub GoHome()
With ActiveWindow
.ScrollRow = 1
.ScrollColumn = 1
Application.Goto Cells(.ScrollRow, .ScrollColumn), True
End With
MsgBox ActiveCell.Address
End Sub
Sub GoHome()
With ActiveWindow
.ScrollRow = 1
.ScrollColumn = 1
Application.Goto Cells(.ScrollRow, .ScrollColumn), True
End With
MsgBox ActiveCell.Address
End Sub
Sub FindFrozenCell()
With ActiveWindow
If .Split Then
.ScrollRow = 1
.ScrollColumn = 1
MsgBox [COLOR=#ff0000]Cells(.ScrollRow, .ScrollColumn)[/COLOR].Address(0, 0)
Else
MsgBox "Khong co cot, hang nao bi dong bang."
End If
End With
End Sub
Tuy rằng ta đặt .ScrollRow = 1, .ScrollColumn = 1, nhưng nếu gặp FreezePanes nó sẽ dừng lại nơi mà ta thiết lập, vì thế, lợi dụng điểm này ta có thể lấy được hàng và cột của chúng.
Sub FindFrozenCell()
With ActiveWindow
If .Split Then
[COLOR=#ff0000] .ScrollRow = 1
.ScrollColumn = 1[/COLOR]
MsgBox Cells([COLOR=#ff0000].ScrollRow, .ScrollColumn[/COLOR]).Address(0, 0)
Else
MsgBox "Khong co cot, hang nao bi dong bang."
End If
End With
End Sub
Tuy rằng ta đặt .ScrollRow = 1, .ScrollColumn = 1, nhưng nếu gặp FreezePanes nó sẽ dừng lại nơi mà ta thiết lập, vì thế, lợi dụng điểm này ta có thể lấy được hàng và cột của chúng.