Nhờ giúp đỡ tạo Userform sửa, cập nhập và xóa dữ liệu

Trạng thái
Không mở trả lời sau này.

phamthach

Yêu THVBA nhất
Ở đây em có 1 userform như trong file đính kèm. Em muốn các anh/ chị giúp đỡ em với nội dung như sau:
- Ở nút "THÊM MỚI" OptionButton1 và OptionButton2 em muốn khi chọn thì dữ liệu dc chọn sẽ thêm vào cột G
- Listbox1: Hiển thị toàn bộ dữ liệu theo dòng và cột ở Table1 trong sheet2 với điều kiện cột G (Tình trạng NK) là chưa nhập kho. Tức chỉ hiển thị dữ liệu theo dòng và cột của Table1 với điều kiện cột G là chưa nhập kho
- Bình thường listbox1 chỉ để xem. Khi ấn "SỬA" thì sẽ chọn được dữ liệu trong listbox. Khi clickduple vào 1 dòng dữ liệu trong listbox1 thì dữ liệu sẽ lấy từ listbox xuống các textbox, combobox,OptionButton tương ứng. Và sẽ sửa dữ liệu tại các textbox, combobox,OptionButton.
Bấm "CẬP NHẬP" Thì dữ liệu vừa sửa sẽ cập nhập vào Table1 trong excel tại dòng cột tương ứng
Bấm "XÓA" thì sẽ xóa dòng dữ liệu dc chọn tại listbox đồng thời xóa dòng dữ liệu tương ứng trong Table1 trong excel
 

tuhocvba

Administrator
Thành viên BQT
Bạn nên xem lại cách trình bày, tôi giúp bạn lần này, lần sau cố gắng trình bày sao cho dễ hiểu, nên bớt chút thời gian ra suy nghĩ.
Dưới đây là vài điểm mà bạn có thể cải thiện để bài viết rõ ràng ý tưởng thiết kế.
1. File có hai sheet Data, và sheet 2: Người dùng sẽ hỏi, vậy dữ liệu cần nhập vào sheet nào?
Tôi phán đoán: Sheet 2.
2. OptionButton1 Và OptionButton2 là gì? Làm thế nào để bất cứ ai cũng hiểu được? Trong trường hợp này, nên dùng hình ảnh minh họa, để mọi người nhìn vào hiểu được ngay câu chuyện của bạn.
Bạn cần đăng nhập để thấy đính kèm

Ở đây có câu hỏi tiếp theo: Nếu không có OptionButton nào được tích chọn, người dùng ấn nút THÊM MỚI thì nội dung khi ấy là gì, hoặc chương trình xử lý như thế nào, chưa thấy bạn nhắc tới.
Tất nhiên tôi cũng có thể phán đoán, có lẽ mong muốn chương trình sẽ báo lỗi như thế nào đó, tuy nhiên đây là công việc của bạn, bạn là người thiết kế, bạn chưa cần phải biết code. Và đối với người thiết kế, hãy đưa ra ý tưởng rõ ràng.

3. Biết là điền vào cột G nhưng là điền trên dòng nào?
Tôi phán đoán là điền trên dòng mới, bởi vì ở đây là nút THÊM MỚI, chứ không phải nút CẬP NHẬT.
Câu hỏi tiếp theo là, chỉ điền vào cột G thôi à? Các thông tin khác không cần à?
Như vậy output mong muốn là như thế này đúng không? (Phần tô màu vàng)
Bạn cần đăng nhập để thấy hình ảnh
 

phamthach

Yêu THVBA nhất
Trước tiên mình cảm ơn admin vì đã hỗ trợ mình cách diễn giải.
Phần "Thêm mới" mình đã làm dc. Giờ mình xin trình bày ý tưởng tiếp như sau:
Link File mình mới làm thêm phần "thêm mới":
- Mình muốn listbox của mình như thế này
Bạn cần đăng nhập để thấy đính kèm

Tức là listbox hiển thị dữ liệu của Table1 nhưng chỉ hiển thị những dòng có giá trị cột G là " Chưa nhập kho"
- Bình thường khi chưa lick vào nút "Sửa" thì khi lick vào listbox chỉ là dạng xem
- Khi lick nút "Sửa" và click dup vào 1 dòng dữ liệu nào đó trong list thì dữ liệu dòng đó sẽ nhảy zô các textbox, combobox, OptionButton tương ứng với tiêu đề là các lable và có thể sửa dc dữ liệu tại các textbox, combobox, OptionButton đó.
- Nút " cập nhập" chỉ có tác dụng khi đã ấn nút "sửa" trc đó
- Khi ấn" sửa" và sửa dữ liệu tại các textbox, combobox, OptionButton ấn "Cập nhập" thì dữ liệu mới sửa sẽ cập nhập dữ liệu mới zô Table1 trong excel đúng dòng cũ
- Nút "Xóa" chỉ có tác dụng khi đã ấn nút "sửa" trc đó
- Khi ấn "sửa" thì sẽ chọn dc dòng dữ liệu trên listbox (1 click) ấn "xóa" và ấn " Cập nhập" thì sẽ xóa dòng dữ liệu tương ứng tại table1 và đẩy dòng dữ liệu dưới lên ( như kiểu : delete up)
Rất mong sự giúp đỡ của mọi người ạ
 

tuhocvba

Administrator
Thành viên BQT
1. Lỗi thiết kế.
Bạn muốn hiển thị tiêu đề, thì tức là phải sử dụng Rowsource. Mà như đã từng đề cập, chúng ta hạn chế dùng cái này.
Đính chính: Tiêu đề của Listbox như hình dưới đây.
Bạn cần đăng nhập để thấy hình ảnh

Nó không được như bạn mô tả. Thậm chí người dùng cũng có thể click vào dòng tiêu đề đó nữa.
Như vậy để hiện thị tiêu đề, tôi khuyên bạn nên dùng Label để hiển thị tiêu đề.
Do đó, bạn cần cập nhật 8 Label vào UserForm để ghi tiêu đề vào đây. Và khi đó, người code sẽ dựa vào độ rộng của Label mà bạn đã định sẵn để fix độ rộng cột hiển thị theo Label. Màu label thì tùy bạn chọn, cái này ý tưởng thiết kế là ở bạn.
Bạn nhờ bất cứ ai làm dự án này cho bạn cũng vậy thôi. Người code sẽ cố gắng không dùng Rowsource.
Bạn cần đăng nhập để thấy hình ảnh


Bạn cần đăng nhập để thấy hình ảnh


2. Ý tưởng thiết kế chưa rõ ràng:
Người dùng ấn Sửa -> sửa dữ liệu xong ấn Cập Nhật (tiếng việt không nên dùng CẬP NHẬP), muốn sửa tiếp thì có phải ấn lại vào Sửa nữa không?
Có hai hướng suy nghĩ: Không cần ấn lại vì phiền hà.
Hoặc: Cần ấn lại vì quy trình sửa ban đầu đã kết thúc rồi, muốn sửa mục khác phải ấn lại vào Sửa.

Tư vấn:
Theo tôi, cứ cho hiện Listbox bình thường. Bạn không cần phải giấu đi. Người ta click vào mục nào thì thông tin mục đó load vào các ô textbox, combobox, option...
Người ta ấn THÊM MỚI thì là thêm, ấn sửa CẬP NHẬT thì là cập nhật, ấn xóa thì là xóa. Tự nhiên cho nút SỬA chả hiểu để phòng chống cái gì.

Bạn có thể tham khảo topic Listbox ở đã trình bày ở đây, ở đây có đủ kiến thức cho bạn dùng:

Đính chính: 8 cột dữ liệu, nên bạn cần tạo 8 Label tương ứng tiêu đề cho 8 cột. Vì việc tạo Label này cũng nằm trong khả năng của bạn, cho nên người giúp sẽ chỉ giúp code (phần mà bạn gặp khó khăn).
Vậy hãy thêm label và gửi lại file nhé.
Độ dài ngắn data của từng cột khác nhau, nên bạn hãy tính toán để các Label có độ rộng hợp lý tương ứng với tỷ lệ độ rộng các cột trên sheet thì tốt nhất.
 

Euler

Administrator
Thành viên BQT
Bạn trình bày rất khó hiểu. Ít nhất bạn ghi chú mục nào tương ứng với mục nào để người ta nhìn vào hiểu được ngay chứ.
Việc như thế khó quá hay là bạn mặc định người khác phải hiểu trong đầu bạn nghĩ gì.
Bạn cần đăng nhập để thấy đính kèm


Câu hỏi: Mục 6 tại sao lại dùng combobox? Tại sao bạn không dùng textbox? Có phải bạn muốn list hết tên hàng nạp vào combobox? Nếu mà như vậy thì trong yêu cầu phải nói rõ ra chứ?
Ngoài ra, nếu như topic này đang được hỗ trợ, tức là người khác phải bỏ thời gian ra làm, mà ở một nơi khác, cũng có người đang làm giúp bạn, thì bạn suy nghĩ như thế nào về việc có hai người cùng làm nhưng bạn chỉ sử dụng một cái. Bạn suy nghĩ thế nào về thời gian người khác phải bỏ ra như vậy nhưng rất có thể bạn không sử dụng?
 

vbano1

SMod
Thành viên BQT
Anh ơi, tác giả đã nạp sẵn vào combobox rồi.
Bạn cần đăng nhập để thấy hình ảnh

Tuy nhiên không hiểu listbox sẽ hiện hết các mặt hàng có tên tương ứng trong listbox hay là dữ liệu có bao nhiêu thì load hết ra. Nên xác nhận ý tưởng thiết kế trước, không lại làm việc tốn công tốn sức mà không có hiệu quả. Mất thời gian vô ích.
 

Euler

Administrator
Thành viên BQT
@phamthach : Bạn tự code cho nút cập nhật và xóa.
Tôi đã load thông tin vào listbox cho bạn rồi.
File download ở đây:
Bạn cần đăng nhập để thấy hình ảnh
 

phamthach

Yêu THVBA nhất
@phamthach : Bạn tự code cho nút cập nhật và xóa.
Tôi đã load thông tin vào listbox cho bạn rồi.
Bạn có thể kiểm tra giúp mình. Khi mình tải file về muốn lưu file nó báo:
"Be careful ! Parts of your document may include personal information that can't be removed by the Document inspector"
 

vbano1

SMod
Thành viên BQT
Nó có nghĩa là tài liệu đã được người tạo file chỉnh sửa lại thông tín cá nhân của file hoặc bạn không thể cập nhật thông tin cá nhân của mình vào file.
Đó không phải là vấn đề gì. Nếu bạn muốn file đó thể hiện là của mình, bạn hãy trích xuất các module và userform và import vào file mới của bạn.
 

Euler

Administrator
Thành viên BQT
Tôi giải thích qua code:
UserForm_Initialize: Tức là lúc Userform chuẩn bị hiện ra trước mắt bạn, tôi cho thực thi gọi thủ tục tuhocvba để load thông tin vào listbox, sẽ trình bày ở dưới. Chú ý là lúc này Userform đang chuẩn bị thông tin để hiện ra trước mắt bạn nhé. Nó chưa thực sự hiện ra đâu.
Tôi thấy bạn dùng: UserForm_Activate để làm gì đó, tôi thấy có lẽ cho vào cái ở trên (UserForm_Initialize) mới là đúng thì phải. Mà tùy bạn.


Đoạn code này để lấy thông tin mà người dùng select trên listbox cho hiển thị trên các ô textbox và combobox mà bạn yêu cầu.
Mã:
Private Sub ListBox1_Click()
'tuhocvba
    Dim i As Integer
    Dim n As Integer
    n = ListBox1.ListCount
   
    If n = 0 Then Exit Sub
    For i = 0 To n - 1 Step 1
        If ListBox1.Selected(i) = True Then
            txtngay.text = ListBox1.List(i, 0)
            txtsl.text = ListBox1.List(i, 4)
            txtdu.text = ListBox1.List(i, 5)
            OptionButton1.Value = True
            cbten.text = ListBox1.List(i, 2)
            txtnote.text = ListBox1.List(i, 7)
        End If
    Next i
End Sub
Tôi đoán là bạn muốn sửa hay cập nhật thì sẽ dựa vào mã hàng.
Mã hàng mà người dùng click là ListBox1.List(i, 1) , vì thế bạn có thể truyền vào tham số global để xử lý sau này.
Khai báo ở module bất kỳ:
Mã:
public mahang_thvba as string
Rồi ở đoạn code trên thì viết thêm dòng code :
Mã:
If ListBox1.Selected(i) = True Then
....
' chèn dòng code này vào trong lệnh If
mahang_thvba = ListBox1.List(i, 1)
End if
Tới đây thì bạn tự xử lý cập nhật hay xóa được rồi nhỉ. mahang_thvba là mã hàng mà người dùng đang select.
Ở Module1 tôi có viết thủ tục tuhocvba, nó có ý nghĩa là để nạp các giá trị từ bảng excel vào listbox với điều kiện là chưa nhập kho.
Có chú ý rằng, chưa nhập kho là ký tự tiếng việt có dấu, tôi không thể cứ thế viết s ="chưa nhập kho" cho nên tôi phải sử dụng Module2, đó là Module mà diễn đàn ta đã dày công xây dựng từ các nguồn tham khảo trên internet để xử lý tiếng việt có dấu.
Tóm lại Module2 chứa các hàm để chuyển ký tự như là "tuij hojc vba" thành "tụi học vba".
Trở lại Module1, thủ tục vba là để load lên listbox.
Đầu tiên làm việc với listbox thì cần chú ý:
Mã:
.Clear
        .ColumnCount = 8
        .ColumnWidths = "60;90;168;42;42;42;90;72"
        .ListStyle = fmListStyleOption
        .MultiSelect = False
Không cần biết listbox được gọi lần đầu hay thế nào, phải clear nội dung của listbox.
Tiếp theo, bạn muốn listbox có 8 cột cho nên thiết định bằng 8.
Dòng code tiếp theo là thiết định độ rộng các cột. Độ rộng này tại sao lại như vậy. Nếu bạn để ý thì trên UserForm tôi đã thêm 8 label cho nó.
Và độ rộng của 8 label này chính là dãy số ở trên: 60;90;168;42;42;42;90;72
Việc này đáng ra bạn tự làm được, chả hạn làm có xấu thì mọi người cũng sẽ sửa cho. Nhưng bạn không làm gì cả. Ngược lại còn đi nhờ nơi khác.
Vậy là nếu người ta và tôi cùng làm, thì chẳng phải là mất thời gian của một người nào đó trong hai người hay sao? Bạn nghĩ thế nào mà lại làm vậy? Giả sử có làm thế thì cũng nên lên topic này báo stop, các anh đừng làm nữa, em đang nhờ chỗ khác rồi.

Tiếp theo là kiểu dáng, cho hiển thị Option. Đó chính là cái hình tròn tròn mà bạn thấy đó.
Bạn cần đăng nhập để thấy đính kèm

Nếu không thiết định thì hình này không có. Mà bài học listbox ở đây đã cung cấp link cho bạn rồi, không biết bạn đã đọc chưa.
Cuối cùng: Multiselect = Flase, có nghĩa là tại một thời điểm, người dùng chỉ chọn vào một dòng mà thôi. Không có chuyện nay chọn 1 dòng, tí nữa chọn thêm dòng nữa thì là 2 dòng.
Ở đây, người dùng được select vào một dòng thôi.

Mã:
If CStr(arrthvba(i, 7)) = temp Then
               
                .AddItem CStr(arrthvba(i, 1))
                .List(cnt_tem, 1) = CStr(arrthvba(i, 2))
                .List(cnt_tem, 2) = CStr(arrthvba(i, 3))
                .List(cnt_tem, 3) = CStr(arrthvba(i, 4))
                .List(cnt_tem, 4) = CStr(arrthvba(i, 5))
                .List(cnt_tem, 5) = CStr(arrthvba(i, 6))
                .List(cnt_tem, 6) = CStr(arrthvba(i, 7))
                .List(cnt_tem, 7) = CStr(arrthvba(i, 8))
                cnt_tem = cnt_tem + 1
            End If
AddItem CStr(arrthvba(i, 1)) : Thêm một dòng mới vào listbox, bắt đầu dòng là giá trị arrthvba(i, 1), ở đây chính là ngày. giá trị bắt đầu dòng mới mặc định là cột 0 của Listbox. Chú ý listbox có 8 cột và bắt đầu từ 0,1,2,...7
Như vậy tôi đã có dòng mới và giá trị tại cột 0 đã được nạp. Tôi còn 7 cột nữa.
Đoạn code ở dưới:
Mã:
.List(cnt_tem, 1) = CStr(arrthvba(i, 2))
                .List(cnt_tem, 2) = CStr(arrthvba(i, 3))
                .List(cnt_tem, 3) = CStr(arrthvba(i, 4))
                .List(cnt_tem, 4) = CStr(arrthvba(i, 5))
                .List(cnt_tem, 5) = CStr(arrthvba(i, 6))
                .List(cnt_tem, 6) = CStr(arrthvba(i, 7))
                .List(cnt_tem, 7) = CStr(arrthvba(i, 8))
nghĩa là trên dòng mà tôi đang làm việc ấy, bây giờ trên các cột 1,2,3,...7 tôi lần lượt nạp giá trị cho nó.
Bạn cần đăng nhập để thấy đính kèm


Tôi không thấy bạn hỏi, nhưng mạn phép, đã giúp thì phải giúp người ta lý giải, viết mấy dòng này, lại còn tốn công chụp hình minh họa nữa, mà không biết bạn có chịu đọc không.
Chúc bạn thu hoạch được nhiều kiến thức trên diễn đàn.
 

vbano1

SMod
Thành viên BQT
Chỗ này nên thêm Exit For thì hay hơn, vì tìm kiếm ra rồi thì thôi không next nữa:
Mã:
If ListBox1.Selected(i) = True Then
            txtngay.text = ListBox1.List(i, 0)
            txtsl.text = ListBox1.List(i, 4)
            txtdu.text = ListBox1.List(i, 5)
            OptionButton1.Value = True
            cbten.text = ListBox1.List(i, 2)
            txtnote.text = ListBox1.List(i, 7)
            Exit for
End If
 

phamthach

Yêu THVBA nhất
Cảm ơn các bạn.
Mình xem hỏi thêm 1 cái nữa ạ.

Đường link mình để ở đây. File này mình làm demo để nói ý tưởng cho dễ
Trong File của mình có 2 sheet là :
Sheet "DATA" chứa data dữ liệu từ Từ A7 đến C ... ( tức là dữ liệu ở sheet data có thể thêm mới)
Sheet2 là sheet nhập liệu
Trong sheet2 mình có tạo 1 form để nhập liệu vào vùng A >>> E
Tuy nhiên mình chỉ muốn nhập những dữ liệu thay đổi như: Ngày, Tên hàng, số lượng
Còn Mã hàng và ĐVT tại cột B và D mình muốn nó tự động lấy theo Tên hàng ở sheet DATA. Mong mọi người giúp đỡ ạ
 

giaiphapvba

Administrator
Thành viên BQT
Bạn ơi, bạn trình bày thế này là người ta phải download file về xem mới hiểu được bạn ạ. Đấy là chưa kể sheet2 nhưng tên là sheet 1. Nếu người khó tính là họ sẽ feedback đấy.
Bạn tránh đặt tên mâu thuẫn như vậy. Mấy anh admin mà đọc thế này lại không hài lòng về cách trình bày.
Bạn cần đăng nhập để thấy hình ảnh

Bạn xem mình trình bày như thế này có dễ hiểu hơn không nhé. Nếu bài của bạn có thêm hình này, thì có phải là dễ hiểu hơn không nào.
Bạn cần đăng nhập để thấy hình ảnh


Bạn dùng code sau nhé, cho vào module bất kỳ:
Mã:
Sub tuhocvba0812()
    Dim ngay_tem            As String
    Dim tenhang_tem      As String
    Dim soluong_tem     As String
    Dim rend_data_tem   As Integer 'Dong cuoi cua sheet DATA
    Dim arr_tem                         As Variant
    Dim i                           As Long
    Dim mahang_tem      As String
    Dim dv_tem                  As String
    Dim flag_tem                As Boolean 'Tim thay ten hang tren sheet DATA
    Dim sh                          As String
    
    
    Const rtile_data_tem As Byte = 6 'Dong tieu de cua sheet DATA
    ngay_tem = UserForm1.TextBox1.Text
    tenhang_tem = UserForm1.ComboBox1.Text
    soluong_tem = UserForm1.TextBox2.Text
    sh = ActiveSheet.Name   'Lay ten sheet hien hanh: Sheet 2
    
    'Kiem tra
    If ngay_tem = "" Then
        MsgBox "Chua nhap ngay"
        Exit Sub
    End If
    If tenhang_tem = "" Then
        MsgBox "Chua nhap ten hang"
        Exit Sub
    End If
    If soluong_tem = "" Then
        MsgBox "Chua nhap so luong"
        Exit Sub
    End If
    If IsNumeric(soluong_tem) = False Then
        MsgBox "Ban phai nhap so luong la CHU SO" 'Ex: 1,2,3,..
        Exit Sub
    End If
    'nap du lieu sheet DATA
    ThisWorkbook.Sheets("DATA").Activate
    rend_data_tem = ThisWorkbook.Sheets("DATA").Cells(Rows.Count, 1).End(xlUp).Row
    If rend_data_tem <= rtile_data_tem Then
        MsgBox "sheet DATA khong co du lieu. Vui long kiem tra lai"
        Exit Sub
    End If
    arr_tem = ThisWorkbook.Sheets("DATA").Range(Cells(rtile_data_tem + 1, 1), Cells(rend_data_tem, 3)).Value 'Nap du lieu sheet DATA vao mang
    flag_tem = False
    For i = LBound(arr_tem, 1) To UBound(arr_tem, 1) Step 1
        If CStr(arr_tem(i, 2)) = tenhang_tem Then 'tim theo ten hang
            mahang_tem = CStr(arr_tem(i, 1)) 'lay ma hang
            dv_tem = CStr(arr_tem(i, 3)) 'Lay DVT
            flag_tem = True
            Exit For
        End If
    Next i
    If flag_tem = False Then
        MsgBox "Khong tim thay ten hang tren sheet DATA. Vui long kiem tra lai"
        Exit Sub 'Ket thuc chuong trinh
    End If
    'neu khong co van de gi thi ghi thong tin
    ThisWorkbook.Sheets(sh).Activate
    rend_data_tem = ThisWorkbook.Sheets(sh).Cells(Rows.Count, 1).End(xlUp).Row + 1 'Lay dong cuoi + 1
    'Ghi thong tin:
    With ThisWorkbook.Sheets(sh)
        .Cells(rend_data_tem, 1) = ngay_tem 'ngay
        .Cells(rend_data_tem, 2) = mahang_tem 'ma hang
        .Cells(rend_data_tem, 4) = dv_tem 'DVT
        .Cells(rend_data_tem, 5) = soluong_tem 'So luong
        .Cells(rend_data_tem, 3) = tenhang_tem 'Tenhang
    End With
End Sub
Code cho nút bấm thì mình thấy bạn viết một phần, bỏ hết đi vì ở trên mình viết đủ rồi. Bạn chỉ cần gọi thủ tục ở trên là được.
Mã:
Private Sub CommandButton1_Click()
    Call tuhocvba0812
End Sub
Chú ý mã hàng là dãy số dài, nên bạn phải định dạng cột B của sheet DATAtext thì mới hiển thị đúng, số không bị biến dạng.
Bạn cần đăng nhập để thấy hình ảnh
 

phamthach

Yêu THVBA nhất
@giaiphapvba Cảm ơn bạn. Mình đã làm theo code của bạn và đã ra kết quả như mong muốn. Code của bạn viết rất rõ ràng nhưng chắc mình cần thời gian để coi lại và học thêm từ đó.
Cũng tại sheet nhập liệu mình muốn thêm cột STT tự động thêm số thứ tự từ 1 >>> ... theo dữ liệu nhập bạn có thể giúp mình lần nữa ạ.https://www.upsieutoc.com/image/untitled.J3qqLC
 

tuhocvba

Administrator
Thành viên BQT
Cấm chỉ các thành viên post code.
Chủ topic hãy ghi tất cả các yêu cầu ra. Đừng có lắt nhắt thông tin.
Bạn không thiết kế trước khi code à?
 
S

Snow24

Guest
phamthach Cái này đơn giản mà.Mình đã biết được dòng cuối ở đâu rồi.Vậy lấy dòng cuối trừ đi 6 thế là ra số thứ tự.
 

tuhocvba

Administrator
Thành viên BQT
Cấm chỉ post code. Bạn không có tinh thần tự học. Bạn đang lợi dụng mọi người.
 
Trạng thái
Không mở trả lời sau này.
Top