Sunday, February 23, 2014

BelajarVBA 102 - ComboBox 02

Coretan Mr. Kid

Isilah masa sehat dengan kebaikan sebelum tiba masa sakit.

Pada pembahasan tentang combobox yang lalu, sumber data merujuk ke suatu area range yang diisikan ke dalam combobox dengan memanfaatkan properti ListFillRange (combobox di dalam worksheet) atau RowSource (combobox di dalam userform). Properti ListFillRange akan membuat daftar dalam combobox menjadi terikat erat dengan sumbernya, sehingga pengubahan isi daftar tanpa mengubah sumber data tidak dimungkinkan. Pengubahan bisa dilakukan bila yang diubah adalah nilai-nilai dalam sumber data.

Bahasan kali ini masih akan menggunakan data sumber untuk combobox list dari suatu range. Hanya saja, kali ini pengisian daftar combobox dilakukan tanpa properti ListFillRange atau RowSource, tetapi menggunakan methods AddItem ataupun properti List dari combbox. Hal ini akan membuat daftar menjadi lebih terbuka untuk diubah isinya. Kadang kala pengubahan isi daftar juga dibutuhkan pada beberapa kasus.

File yang akan digunakan adalah BelajarVBA102_02.xlsm. Data yang digunakan diletakkan pada sheet bernama 'populate' dengan nama object di dalam VBProject adalah Sheet2. Data tersebut seperti gambar berikut :


ComboBox list berisi satu kolom

Kali ini, pembahasan combobox akan memanfaat userform. Pada bahasan tentang combobox berisi satu kolom ini, akan menggunakan userform bernama frmNomor2 seperti gambar berikut :
yang berisi object controls utama sebagai berikut :
  • Combobox bernama cboProd : combobox dengan list berisi satu kolom yang dibaca dari sheet bernama populate (object Sheet2) pada range A2:A5.
  • Textbox bernama txtListIndex : menampilkan nilai yang disimpan oleh properti ListIndex milik cboProd.
  • Textbox bernama txtText : menampilkan nilai yang disimpan oleh properti Text milik cboProd.
  • Textbox bernama txtValue : menampilkan nilai yang disimpan oleh properti Value milik cboProd.
Pada kasus ini, textbox digunakan untuk menampilkan data dan tidak mengijinkan user untuk bisa mengubah nilainya. Maka properti Locked milik seluruh textbox diset bernilai TRUE. Properti milik cboProd menggunakan nilai default setiap pembuatan object baru di sebuah userform, yang antara lain adalah :
  • BoundColumn dan ColumnCount bernilai 1
  • ColumnHeads bernilai FALSE
  • ColumnWidths, ControlSource, RowSource, Text, Value dalam keadaan kosong
  • ListWidth bernilai 0pt
  • TextColumn bernilai -1
Alur proses pada frmNomor2 adalah sebagai berikut :
  1. Saat frmNomor2 dimuat ke memori komputer, maka dilakukan pengisian item-item daftar pilihan combobox, yang dilanjutkan dengan manampilkan frmNomor2 kepada user. Artinya, proses pengisian dilakukan saat frmNomor2 sedang diinisialisasi ke memori komputer. Jadi, proses pengisian item ke combobox llist dilakukan pada sebuah event milik userform frmNomor2, yaitu event Initialize.
  2. Ketika user mengubah pada combobox cboProd, maka akan dilakukan proses penulisan nilai-nilai yang disimpan oleh properti ListIndex, Text, dan Value dari cboProd ke :
    • sheet bernama populate (Sheet2 di VBProject Explorer) pada range B13 sampai B15.
    • textbox control terkait, yaitu txtListIndex, txtText, dan txtValue
    Proses ini dilaksanakan saat user mengubah isi cboProd, yang berarti akan memicu event Change milik cboProd. Jadi proses dilakukan didalam event Change milik cboProd.
  3. Saat userform frmNomor2 ditutup oleh user, maka dilakukan pembersihan isi range B13 sampai B15 yang ada di sheet bernama populate. Proses ini terjadi saat ada permintaan penutupan userform oleh user, yang akan memicu event milik userform yang bernama QueryClose. Jadi, proses pembersihan isi range tertentu tersebut diletakkan di dalam event QueryClose milik userform.
Setelah desain userform frmNomor2 terbentuk dan alur proses terjabarkan dengan jelas, maka penulisan script VBA untuk setiap proses tersebut bisa dilakukan dengan tartil. Berikut ini langkah penulisan script VBA untuk contoh kasus di atas.
  1. Event Initialize milik userform frmNomor2
    Diisi dengan proses pengisian daftar untuk combobox cboProd dengan langkah penyusunan sebagai berikut :
    • Membuat blok prosedur sub event Initialize milik userform frmNomor2 dengan cara :
      • double click userform frmNomor2, sehingga muncul blok event Click milik userform frmNomor2
      • pada combobox prosedur (sudut kanan atas area penulisan script) -> pilih Initialize
      • letakkan cursor ke dalam blok prosedur sub event Initialize yang terbentuk
    • Mendeklarasikan variabel yang dibutuhkan selama proses pengisian Pengisian item list combobox cboProd bisa dilakukan dengan 2 (dua) cara, yaitu : (pilih salah satu)
      • Memanfaatkan methods AddItem dengan cara melakukan loop terhadap setiap item sumber data. Sumber data yang berada di sheet populate (object Sheet2) pada range A2:A5 membutuhkan variabel loop bertipe Range. Maka perlu pendeklarasian variabel tersebut, yang misalnya diberi nama rngCurrent. Baris kode deklarasi tersebut berbunyi :
        Dim rngCurrent As Range
      • Memanfaatkan properti List dengan cara menyediakan array seluruh item dari sumber data. Berarti, sumber data yang ada di sheet populate (object Sheet2) pada range A2:A5 dibaca dan disimpan sebagai sebuah array. Area range sebenarnya adalah suatu array dan tipe data yang memungkinkan dengan mudah untuk menyimpan array dari suatu range adalah tipe Variant. Maka perlu pendeklarasian variabel bertipe variant, yang misalnya diberi nama vSumber. Baris kode deklarasi tersebut berbunyi :
        Dim vSumber As Variant
    • Menyusun baris kode proses pengisian
      Sumber data di sheet populate (Sheet2) pada range A2:A5.
      • Jika memilih menggunakan methods AddItem, maka dibutuhkan proses loop terhadap setiap item dalam range A2:A5. Di dalam proses loop tersebut diisi proses penambahan item ke list milik combobox dengan methods AddItem. Baris-baris kode proses ini adalah :
        For Each rngCurrent In Sheet2.Range("a2:a5")
           cboProd.AddItem rngCurrent.Value
        Next rngCurrent
      • Jika memilih menggunakan properti List, maka dibutuhkan proses penyimpanan sumber data range A2:A5 menjadi array, yaitu ke dalam variabel vSumber. Kemudian array dalam variabel vSumber dimasukkan ke properti List. Baris-baris kode proses ini adalah :
        vSumber = Sheet2.Range("a2:a5")
        cboProd.List = vSumber
    • Bentuk akhir prosedur sub event Initialize menjadi :
      • Methods AddItem
        Private Sub UserForm_Initialize()
           Dim rngCurrent As Range

           For Each rngCurrent In Sheet2.Range("a2:a5")
              cboProd.AddItem rngCurrent.Value
           Next rngCurrent
        End Sub
      • Properti List
        Private Sub UserForm_Initialize()
           Dim vSumber As Variant

           vSumber = Sheet2.Range("a2:a5")
           cboProd.List = vSumber
        End Sub
  2. Event Change milik cboProd
    Diisi dengan proses penulisan ke lokasi target dengan langkah penyusunan sebagai berikut :
    • Membentuk blok prosedur sub event Change milik cboProd dengan cara double click object control combobox cboProd yang ada di dalam userform frmNomor2 dan meletakkan cursor di dalam blok prosedur yang terbentuk
    • Menulis nilai-nilai properti ListIndex, Text, dan Value secara berurutan ke :
      • Sheet populate pada range B13 sampai B15 dengan baris-baris kode :
        Sheet2.Range("b13").Value = cboProd.ListIndex
        Sheet2.Range("b14").Value = cboProd.Text
        Sheet2.Range("b15").Value = cboProd.Value
      • Object controls textbox txtListIndex, txtText, dan txtValue dengan baris-baris kode :
        txtListIndex.Text = cboProd.ListIndex
        txtText.Text = cboProd.Text
        txtValue.Text = cboProd.Value
    • Bentuk akhir blok prosedur sub event Change milik cboProd :
      Private Sub cboProd_Change()
         Sheet2.Range("b13").Value = cboProd.ListIndex
         Sheet2.Range("b14").Value = cboProd.Text
         Sheet2.Range("b15").Value = cboProd.Value

         txtListIndex.Text = cboProd.ListIndex
         txtText.Text = cboProd.Text
         txtValue.Text = cboProd.Value
      End Sub
  3. Event QueryClose milik userform frmNomor2
    Diisi dengan proses menghapus isi range B13 sampai B15 pada sheet bernama populate (object Sheet2), yang menjadi lokasi penulisan pada proses di dalam event Change milik cboProd. Langkah-langkah penyusunannya adalah sebagai berikut :
    • Membentuk blok prosedur sub event QueryClose milik userform frmNomor2 dengan cara :
      • double click userform frmNomor2
      • pada combobox daftar prosedur (pojok kanan atas area penulisan script) -> pilih QueryClose
      • letakkan cursor di dalam blok prosedur yang terbentuk
    • Menulis baris kode untuk menghapus isi range B13 sampai B15 di sheet populate (object Sheet2) yang berbunyi :
      Sheet2.Range("b13:b15").ClearContents
    • Bentuk akhir blok prosedur sub event QueryClose menjadi :
      Private Sub UserForm_QueryClose( _
                                Cancel As Integer _
                              , CloseMode As Integer)
         Sheet2.Range("b13:b15").ClearContents
      End Sub
Prosedur-prosedur di atas membentuk sebuah proses sederhana tentang pengisian item list combobox dan pengambilan nilai dari item terpilih ke suatu lokasi target (range tertentu ataupun object control tertentu).

ComboBox list multi kolom hanya menampilkan satu kolom

Kali ini, dibuat sebuah userform yang diberi nama frmNomor3 yang mirip dengan userform frmNomor2. Perbedaannya adalah penambahan textbox bernama txtCol2 untuk menampilkan nilai dari kolom ke-3 list combobox. Properti BoundColumn di-set bernilai 2 dan properti TextColumn di-set bernilai 1. Sedangkan properti ColumnCount tetap bernilai 1 karena hanya akan menampilkan satu kolom saja. Sumber data untuk list combobox diambil dari sheet populate (object Sheet2) pada area range A2:C5 (3 kolom). Bentuk userform frmNomor3 seperti gambar berikut :

> Proses pend-disable-an event pada userform

Kebutuhan untuk membaca nilai dari kolom list yang tidak ditunjuk oleh properti BoundColumn maupun TextColumn, seperti ingin mengambil nilai dari kolom list ke-3 [indeks kolom bernilai 2 = Column(2)] bisa memicu timbulnya error ketika nilai pada kolom yang dirujuk tersebut belum terdefinisikan. Contohnya adalah ingin mengambil nilai dari kolom ke-3, tetapi pada baris item tersebut baru 2 kolom saja yang didefinisikan.

Error seperti ini sering terjadi ketika proses pengambilan nilai pada suatu kolom list dilakukan pada event Change milik combobox terkait (misal pada event Change milik cboProd). Artinya, dibutuhkan sebuah proses untuk mengabaikan kerja prosedur sub event change pada saat mengubah isi list, karena tidak ada pengaturan untuk men-disable event milik userform. Cara men-disable event di suatu userform dalam VBA dilakukan dengan menggunakan variabel ber-scope module yang diperiksa oleh setiap prosedur sub event yang tidak ingin dijalankan dengan alasan tertentu.

Pada contoh kasus ini, proses pada event Initialize userform frmNomor3 bisa memunculkan error, karena proses pengisian list combobox akan memicu event Change milik cboProd. Untuk men-disable event, maka dibuat sebuah variabel bernama bEventTidakBolehJalan dengan scope module. Baris kode deklarasi variabel ini diletakkan pada area General Declaration (di baris pertama area penulisan script atau setelah baris berbunyi Option Explicit). Baris kode tersebut berbunyi :
   Private bEventTidakBolehJalan As Boolean

Pada event Change milik cboProd, yaitu dibaris pertama setelah baris deklarasi variabel diberi proses cek terhadap nilai variabel bEventTidakBolehJalan dengan bentuk baris kode :
   If bEventTidakBolehJalan Then
      Exit Sub
   End If
yang berarti, jika variabel bEventTidakBolehJalan sedang bernilai TRUE, maka proses langsung keluar dari prosedur alias tidak ada proses dalam prosedur event tersebut yang dilakukan.

Cara menggunakannya, pada prosedur yang salah satu prosesnya bisa memicu si event Change milik cboProd untuk dijalankan, seperti pada event Initialize milik userform frmNomor3, diberi baris kode untuk mengubah nilai variabel bEventTidakBolehJalan menjadi TRUE. Penempatannya adalah tepat sebelum proses yang memicu berjalannya event Change milik cboProd. Pada kasus ini, berarti baris kode berbunyi :
   bEventTidakBolehJalan = TRUE
diletakkan tepat sebelum proses pengisian item list cboProd di dalam prosedur sub event Initialize. Pada bagian akhir event Initialize, yaitu tepat sebelum keluar dari prosedur atau sebelum baris End Sub, diberi baris kode untuk mengembalikan nilai variabel bEventTidakBolehJalan menjadi FALSE. Baris kode tersebut berbunyi :
   bEventTidakBolehJalan = FALSE

> Proses pengisian multi kolom list dengan methods AddItem

Cara mengisi multi kolom list menggunakan methods AddItem memerlukan sebuah variabel penyimpan indeks baris list yang akan ditambahkan. Misalkan variabel ini diberi nama lCboIndex. Maka pada event Initialize diberi penambahan deklarasi variabel tersebut. Maka baris deklarasi variabel pada event Initialize menjadi :
   Dim rngCurrent As Range, lCboIndex As Long

Kemudian nilai lCboIndex di-set dengan nilai awal indeks baris list yang base-0, yaitu dengan nilai 0 seperti dengan baris berbunyi :
   lCboIndex = 0

Proses loop yang tadinya berbentuk :
   For Each rngCurrent In Sheet2.Range("a2:a5")
      cboProd.AddItem rngCurrent.Value
   Next rngCurrent

diubah menjadi :
   For Each rngCurrent In Sheet2.Range("a2:a5")
      cboProd.AddItem rngCurrent.Value  'pembuat item baru


      'pengisi kolom list ke-2 dan seterusnya
      cboProd.List(lCboIndex, 1) = rngCurrent.Offset(0, 1).Value
      cboProd.List(lCboIndex, 2) = rngCurrent.Offset(0, 2).Value

      'set nilai indeks baris list berikutnya yang akan diisi

      lCboIndex = lCboIndex + 1
   Next rngCurrent


> Proses pengisian multi kolom list dengan properti List

Jika menggunakan properti List, maka yang diubah adalah rujukan range yang tadinya merujuk ke A2:A5 menjadi merujuk ke A2:C5 (lihat bagian yang di-merah).
   vSumber = Sheet2.Range("a2:c5")

> Penambahan baris penulisan pada event Change cboProd

Pengambilan nilai kolom ke-3 (indeks kolom ke-2) dapat dilakukan dengan properti Column maupun properti List seperti yang telah dijelaskan pada coretan tentang combobox sebelum ini. Penulisan ke sheet populate pada kasus multi kolom list ini dilakukan ke range H18:H21, sehingga baris kode bagian penulisan ke sheet populate menjadi :
   Sheet2.Range("h18").Value = cboProd.ListIndex
   Sheet2.Range("h19").Value = cboProd.Text
   Sheet2.Range("h20").Value = cboProd.Value

   Sheet2.Range("h21").Value = cboProd.List(, 2)  'atau cboProd.Column(2)

Sedangkan pada penulisan ke textbox txtCol2 adalah dengan menambahkan baris kode :
   txtCol2.Text = cboProd.List(, 2)  'atau cboProd.Column(2)

Hasil akhir penyusunan script untuk userform frmNomor3 dapat dilihat pada file BelajarVBA102_02.xlsm.

Tambah, ubah, dan hapus item list combobox

> menambah sebuah item ke list combobox

Misalkan ada sebuah combobox bernama cboProd. Penambahan item list dilakukan dengan menggunakan methods AddItem dengan susunan :
   cboProd.AddItem nilai_baru, [indeks_posisi_penambahan]
Jika ingin menambahkan pada baris baru adalah dengan mengabaikan bagian [indeks_posisi_penambahan] seperti :
   cboProd.AddItem nilai_baru
Bagian nilai_baru adalah teks (bertipe String) yang akan menjadi nilai baru di kolom ke-1 (indeks kolom ke-0).
Contohnya adalah ingin menambahkan item baru 'X' di baris baru, maka baris kodenya adalah :
   cboProd.AddItem "X"
Jika item baru 'Y' ingin ditambahkan pada baris ke-1 list (indeks baris ke-0), maka baris kodenya adalah :
   cboProd.AddItem "Y", 0

Pada combobox dengan multi kolom list, maka bisa jadi dibutuhkan juga pengisian pada kolom ke-2 dan seterusnya (penambahan pada indeks kolom ke-1 dan seterusnya). Pengisian tersebut bisa dilakukan setelah ada penambahan item baru dengan methods AddItem di atas. Susunan penulisan item di kolom lain tersebut secara umum adalah :
   cboProd.List(idxrow, idxcol) = nilai_kolom_baru

Misalkan ingin menambahkan item 'Z' pada baris baru dengan nilai kolom ke-2 bernilai 'Z01' dan nilai kolom ke-3 bernilai 'ZZ11P001', maka baris-baris kode penambahan tersebut adalah :
   cboProd.AddItem "Z"
   cboProd.List( cboProd.ListCount-1 , 1) = "Z01"
   cboProd.List( cboProd.ListCount-1 , 2) = "ZZ11P001"
Properti ListCount dipergunakan untuk mendapatkan jumlah item dalam list. Karena properti ListIndex menggunakan base 0 (indeks baris item pertama bernilai 0), maka nilai properti ListCount - 1 adalah indeks baris item terakhir yang ada. Pada kasus di atas, berarti pada baris item 'Z' yang baru saja ditambahkan di baris baru.

Sedangkan jika ingin menyisipkan (menambah item pada baris tertentu) item 'M' pada baris ke-1 (indeks baris 0) dengan nilai kolom ke-2 bernilai 'M78' dan nilai kolom ke-3 bernilai 'KL98P113', maka baris-baris kode penambahan tersebut adalah :
   cboProd.AddItem "M", 0
   cboProd.List( 0 , 1) = "M78"
   cboProd.List( 0 , 2) = "KL98P113"
Kadangkala, pemeriksaan posisi penyisipan perlu dilakukan, sehingga dapat dipastikan bahwa indeks baris penyisipan tidak lebih dari nilai properti ListCount. Nilai properti ListCount bisa diartikan juga sebagai indeks baris baru jika akan dilakukan penambahan data. Bentuk pemeriksaan tersebut adalah sebagai berikut :
'0 adalah indeks baris lokasi penyisipan dan bisa diganti dengan variabel tertentu
If cboProd.ListCount >= 0 Then 
   'proses penyisipan diletakkan disini
   cboProd.AddItem "M", 0
   cboProd.List( 0 , 1) = "M78"
   cboProd.List( 0 , 2) = "KL98P113" 
End If

> mengubah isi sebuah item yang ada di list combobox

Misalkan ada sebuah combobox bernama cboProd. Pengubahan isi sebuah kolom dalam list combobox dilakukan dengan memanfaatkan propeti List. Bentuk umum beris kodenya adalah :
   cboProd.List(idxrow, idxcol) = nilai_kolom_baru
dengan syarat bahwa idxrow (indeks baris yang akan diubah isinya) kurang dari jumlah item (nilai properti ListCount). Biasanya, bentuk diatas disertai pemeriksaan terhadap indeks baris yang akan diubah isinya. Bentuk lengkapnya menjadi :
'idxrow adalah indeks baris yang akan diubah isinya
If idxrow < cboProd.ListCount Then
   cboProd.List(idxrow, idxcol) = nilai_kolom_baru
End If

Misalkan akan mengubah isi kolom ke-3 (indeks kolom ke-2) pada item di baris pertama (indeks baris ke-0) menjadi 'GJ71P123', maka baris kodenya adalah :
If 0 < cboProd.ListCount Then
   cboProd.List(0, 2) = "GJ71P123"
End If

> menghapus isi list combobox

Misalkan ada sebuah combobox bernama cboProd. Jika akan menghapus seluruh isi list combobox, maka dapat menggunakan methods Clear. Baris kodenya adalah :
   cboProd.Clear

Jika yang akan dihapus adalah sebuah item di baris tertentu, maka dilakukan dengan menggunakan methods RemoveItem disertai indeks baris (base 0) yang akan dihapus. Bentuk umum baris kodenya adalah :
   cboProd.RemoveItem indeks_baris_yang_dihapus
Biasanya, penghapusan sebuah baris item dilakukan setelah diperiksa bahwa baris yang akan dihapus ada didalam list. Artinya, indeks baris yang akan dihapus (indeks_baris_yang_dihapus) kurang dari jumlah item list (nilai properti ListCount). Jadi bentuk umum menghapus sebuah item list combobox yang disertai pemeriksaan keberadaan baris yang akan dihapus adalah :
   If indeks_baris_yang_dihapus < cboProd.ListCount Then
      cboProd.RemoveItem indeks_baris_yang_dihapus
   End If
Misalkan ingin menghapus item di baris ke-4 (indeks baris ke-3), maka baris kodenya adalah :
   If 3 < cboProd.ListCount Then
      cboProd.RemoveItem 3
   End If

Contoh tentang penerapan penambahan item, pengubahan isi, dan penghapusan item bisa dilihat di file BelajarVBA102_02.xlsm pada userform bernama frmNomor4 yang bisa diaktifkan melalui tombol di sheet bernama 'sisip_ubah_buang'. Pada userform tersebut juga disertakan contoh pemanfaatan event Click milik object control Label yang bisa menjadi alternatif sebuah tombol seperti object control CommandButton.

:)

Insya Allah pembahasan berikutnya tentang object control ListBox pada properti yang tidak dimiliki oleh ComboBox, yaitu properti MultiSelect. Selain itu, juga akan dibahas tentang object control Label, khususnya pada pemanfaatan properti Caption dan MultiLine. Object control textbox juga memiliki properti MultiLine, tetapi ada properti PasswordChar yang bisa digunakan untuk me-masking inputan user pada control tersebut. Event KeyDown acapkali terasa bermanfaat ketika digunakan oleh textbox, seperti membatasi inputan untuk numerik saja atau karakter tertentu saja dan sebagainya. Event KeyDown pada textbox juga akan dibahas disana.


No comments:

Post a Comment