エクセルVBAクラス備忘録 ユーザーフォーム間の配列等の渡し方 パブリック変数以外で受け渡す方法

20240521 エクセルVBAクラス備忘録 ユーザーフォーム間の配列等の渡し方
パブリック変数以外で受け渡す方法
 
 
<前書き>
エクセルVBAオブジェクト指向、初心者です。
VBA自体は、以前から使用していました。
オブジェクト指向というものがあることは、知っていましたが、ネットなどを見てもよく分からず、挫折した過去あり。
<以上前書き>
 
(素人なので間違っているかもしれません。汗。)
 
今回:
いつも悩むユーザーフォーム間のデータの渡し方です。
自分用のメモなので分かりにくいです。
オブジェクト指向とは、少し、かすっています。汗)
 
 
 
 
 
何回か書いたかもしれないが、昔作ったVBAマクロで、ユーザーフォーム間で、データ(配列)の受け渡しがあり、どうしてもできず、
仕方なく、標準モジュールに、パブリック変数を置いた。
 
public hito() As Variant
 
みたいに置いた。
 
 
今回リファクタリングするにあたって、どうしようかと思った。
 
(注:今回は、実際には、元の配列をエクセルシートに配置して、ユーザーフォームごとに読み込んでもいいかなと思っている。(一番簡単))
(注:実際受け渡ししたい配列は2次元配列ですが、この記事では1次元配列を例に書いています。多分2次元でも同じだと思う。(思いたい。汗))
 
 
 
 
方法としては、上記のパブリック変数を使う以外に、いくつかできそうだが、やはりユーザーフォーム間の配列のやり取りはややこしい。
 
**********************************
配列でなく、テキストボックスの値などコントロールに関するものなら、ユーザーフォーム1で、userform2.showの前に、コードを書けば受け渡しできる。
 
 
Private Sub CommandButton1_Click()
 
    UserForm2.TextBox1.Value = "受け渡しOK"
 
    UserForm2.Show
End Sub
 
こんな感じ。
 
リストボックスでも可能なので、
 
 
Private Sub CommandButton1_Click()
    UserForm2.ListBox1.Clear
    On Error Resume Next   'リストがない場合
    For i = 1 To UBound(hito)
        UserForm2.ListBox1.AddItem hito(i, 3)
    Next i
    
     If Err.Number Then
        Err.Clear
    End If
    
    
    UserForm2.ListBox1.ListIndex = -1
 
    UserForm2.Show
    
End Sub
 
 
上記もできる。
でも、配列のまま渡したい。
 
************************************
その他の方法として、タグ(tag)が使えるが、少し変則的。
 
Private Sub UserForm_Initialize()
    ' 配列を初期化
    Dim MyArray() As Variant
    ReDim MyArray(1 To 3)
    MyArray(1) = "Apple"
    MyArray(2) = "Banana"
    MyArray(3) = "Cherry"
    
    ' 配列をTagプロパティに設定
    Me.Tag = Join(MyArray, ";") ' セミコロンは適切な区切り文字を選んでください
End Sub
 
 
Private Sub UserForm_Initialize()
 
PassedArray = UserForm1.Tag
 
Debug.Print PassedArray
 
End Sub
 
 
これで、配列を渡せるけど、tagは文字列しかダメらしい。上記例だと、
Apple;Banana;Cherry
という文字列をまた、配列に戻してやる必要がある。(しかも1次元配列だし。)
 
******************************************
で、他の方法を探すと、ネットで「ほげーむわーく」様のブログに、
【エクセルVBA】ユーザーフォーム間で配列を受け渡す方法
というのがあった。
(感謝いたします。紹介記事に不都合があれば即削除します。)
 
そこでは、一度、標準モジュールを介して配列を送っていました。
 
送り側のユーザーフォームで配列を設定する
送り側ユーザーフォームからプロシージャに配列を渡す
受け側のユーザーフォームから配列が設定されたプロシージャを読み出す
 
Private変数を活用し、標準モジュールのプロシージャに配列と受けと送りの役割(sub とfunction)をそれぞれ持たせ、配列をリレーさせています。
(実際の記事は直接お読み下さい。)
 
*****************************************
で、上記のプログラムを見た時、これ、「プロパティ」でできるのかもと思って、書いたのが下記です。
 
 
'標準モジュール(標準モジュールは抜粋)
Private marray送受 As Variant
 
'let
Public Property Let array送受(parray送受 As Variant)
     marray送受 = parray送受
End Property
 
'get
Public Property Get array送受() As Variant
     array送受 = marray送受
End Property
 
 
' UserForm1のコードモジュール
Option Explicit
Dim MyArray() As Variant
 
 
Private Sub CommandButton1_Click()
 
    array送受 = MyArray
 
    UserForm2.Show
End Sub
 
Private Sub UserForm_Initialize()
    ' 配列を初期化
    
    ReDim MyArray(1 To 3)
    MyArray(1) = "Apple"
    MyArray(2) = "Banana"
    MyArray(3) = "Cherry"
 
End Sub
 
 
' UserForm2のコードモジュール
Option Explicit
 
Private Sub UserForm_Initialize()
   
    Dim chkary() As Variant
 
    chkary = array送受
    Stop
 
End Sub
 
 
*****************************************
で、プロパティをクラスに入れたのが下記です。
 
'クラス「配列送受」
Private marray送受 As Variant
 
 
'let
Public Property Let array送受(parray送受 As Variant)
     marray送受 = parray送受
End Property
 
'get
Public Property Get array送受() As Variant
     array送受 = marray送受
End Property
 
 
'標準モジュール
Option Explicit
Dim v配列送受 As 配列送受    'これなら取れる
 
Sub フォーム1_open()
 
    UserForm1.Show
End Sub
 
 
Sub 受け(myary As Variant)   
    Set v配列送受 = New 配列送受
 
    v配列送受.array送受 = myary
 
End Sub
 
 
Function 渡し() As Variant
    渡し = v配列送受.array送受
 
End Function
 
 
' UserForm1のコードモジュール
Option Explicit
Dim MyArray() As Variant
 
 
Private Sub CommandButton1_Click()
 
    Call 受け(MyArray)
    
    UserForm2.Show
End Sub
 
Private Sub UserForm_Initialize()
    ' 配列を初期化
    
    ReDim MyArray(1 To 3)
    MyArray(1) = "Apple"
    MyArray(2) = "Banana"
    MyArray(3) = "Cherry"
    
End Sub
 
 
***************************************************以下メモ
標準モジュールを経由するので、いまいちか?
エクセルVBAのuserform_initialize()に引数が渡せないのが痛い?
userform_initializeの中に、init()などサブルーチンを用意したとしても、
やはり配列の受け渡しには、工夫が必要か(考えがぐるぐる元に戻る・・・)。
 
 
本当は、
userform2.配列2 =userform1.配列1
とか、したかったのですが、それはできないようです。
(どっちもユーザーフォーム立った状態ならできるかなと思ったのですが・・・)
 
 
 
で、今回は、やはりシートから読み込むシンプルな形にするかなと(考えがぐるぐる元に戻る・・・)。
 

エクセルVBAクラス備忘録(rowsなどコレクション(複数形)) を考える

20240510エクセルVBAクラス備忘録(rowsなどコレクション(複数形)) を考える


<前書き>
エクセルVBAオブジェクト指向、初心者です。
VBA自体は、以前から使用していました。
オブジェクト指向というものがあることは、知っていましたが、ネットなどを見てもよく分からず、挫折した過去あり。
<以上前書き>

(素人なので間違っているかもしれません。汗。)

 

今回は、コードは書いていません。
ここ最近、クラス、オブジェクト指向をやってきて、思ったこととしたことのまとめ?備忘録(まとまっていない)です。


オブジェクト指向で、何気なく書いていたエクセルVBAコードを見直す。
「rows(rows.count),row」のイミ


今まで、下記のようなコード
worksheets("データ")
とか
cells(1,1)
とか何気なく書いていたが、

なぜ、複数形(worksheets、cells)なのか、分かっていなかった。(ギモンにも思っていなかった。汗)

 

でも、複数形なのは、「コレクション」だからであって、
worksheets("データ")
なら、
数あるworksheetを束ねているworksheetsコレクションの中から、名前が"データ"のワークシートという意味だった。


うまく書けないが、データという名前のワークシートでなく(worksheet(データ)でなく)、
ワークシートコレクションの中の「データ」という名前のワークシート。worksheets("データ")。

だから、
worksheets(1)
は、worksheetsコレクションのインデックス1番目という意味。


「rows(rows.count),row」は、
最初のrowsは、rowsコレクション。
その引数が、
rows.count。
これは、rowsコレクションのcountプロパティで、行の最終行が取れる。
で、
rows(最終行)
というオブジェクトを指すことになり、
最後に
rows(最終行).row
でrowsコレクションのrowプロパティで、最終行数を指す。

 

今更ですが。汗

 

エクセルVBAクラス備忘録(クラスの統合(簡易なまとめ方・考え仮))

20240506エクセルVBAクラス備忘録(クラスの統合(簡易なまとめ方・考え仮))


<前書き>
エクセルVBAオブジェクト指向、初心者です。
VBA自体は、以前から使用していました。
オブジェクト指向というものがあることは、知っていましたが、ネットなどを見てもよく分からず、挫折した過去あり。
<以上前書き>

(素人なので間違っているかもしれません。汗。)

今回は、コードは書いていません。
ここ最近、クラス、オブジェクト指向をやってきて、思ったこととしたことの?備忘録(未確認・未確定メモ)です。
(素人なので間違っているかも。汗)

 

<最近やっていること>
昔、作成した手続き型のVBAコードをクラスを意識して少しずつ直しています。(リファクタリング
それがある程度できたら、別の機能を新たに実装しようかと思っています。
その備忘録です。自分用なので読まなくてもいいです。日記です。汗


昨日、
【クラスのまとめ方は難しい(仮)】
として、備忘録を書きました。

そこには、ざっくり言うと「クラスのまとめ方」で、「機能をまとめるのはどうしよう・・・」という疑問を書きました。
(追記にも少し書いたが、まとめ方の方針をどうしようかと考えていた。)

 

結局、自分の過去の手続き型のコードのいろんな箇所で、機能面で、似たようなコード(しかし微妙に違うもの)が多い。
最終的に、キレイにすることは可能かもしれないが、作成中や改修中には難しいか・・・。

 

<簡易的なまとめ方>
「クラスをまとめて、メソッドで分ける。」

////////////////////////////////////////

〇クラスA
 ・メソッド1(手続き型のsabやfunctionを移植)
 ・メソッド2
 ・メソッド3

///////////////////////////////////////

これならメソッドごとに、引数の数や戻り値が違っても問題ない。
(前回の例と同様のまとめ方です。)

/////////////////////////////////

dim vクラスA as クラスA
set vクラスA = new クラスA
vクラスA.メソッド1 引数1、


set vクラスA = new クラスA
vクラスA.メソッド2 引数1、引数2、引数3

//////////////////////////////////////

みたいな感じ。

 

「簡易的」と書いたのは、このメソッド同士を見比べると、変数の宣言など重複しているものがあったりするからです。
それらは、「いずれ」はきれいにしたい。
(しかし、例えば、引数として用いる変数のスコープが異なったりする場合、変にまとめると、メイン文などに影響が出る。)


注:
オブジェクト指向、クラスとか、言いながら、ここで書いているのは、クラスでなくても、標準モジュールに書いた「functionをまとめる作業」とあまり変わらない。
(強いて言えば、エクセルVBAクラスは、メソッドがあるから、無理やりまとめやすいということかな・・・。)
↑これが正しい理解かは、分かりません。何となくやってたら感じたことです。汗。

 

 

エクセルVBAクラス備忘録(クラスの統合・整理(孫クラス的なのを追加))仮・問題点メモ

20240505エクセルVBAクラス備忘録(クラスの統合・整理(孫クラス的なのを追加))仮・問題点メモ


<前書き>
エクセルVBAオブジェクト指向、初心者です。
VBA自体は、以前から使用していました。
オブジェクト指向というものがあることは、知っていましたが、ネットなどを見てもよく分からず、挫折した過去あり。
<以上前書き>

 

(素人なので間違っているかもしれません。汗。)

今回は、コードは書いていません。
ここ最近、クラス、オブジェクト指向をやってきて、思ったこととしたことの?備忘録(未確認・未確定メモ)です。
(素人なので間違っているかも。汗)

 

<最近やっていること>
昔、作成した「手続き型のVBAコード」をクラスを意識して少しずつ直しています。(リファクタリング
それがある程度できたら、別の機能を新たに実装しようかと思っています。
その備忘録です。自分用なので読まなくてもいいです。日記です。汗


【クラスのまとめ方は難しい(仮)】
今回、クラス「氏名_make、用務地_make、用務内容_make」などクラスを作った。
「氏名_make」などの命名は、日本語と英語で「何をどうするか」を表したもの。
(自分のオリジナルでこれでいけるのか、考え中)
インスタンス化する時に、この名前でなく、別のインスタンス名を付ければいいか。
例:dim v氏名リスト as 氏名_make。・・・ややこしいか・・・?)


で、上記の各クラスだが、内容は、シートの表を読み込むメソッドだけ。(今現在)
中身は「リスト配列」というメソッド(読み込み列の関係で少し引数の数が違うなどだが類似)なので、
1個のクラスにまとめること可能。クラス「配列_make」とかで、まとめられるなと思う。

やるとしたら、


////////////////////////////////
<元の例>
dim v氏名 as 氏名_make
set v氏名 = new 氏名_make
v氏名.リスト配列 "sheet_simei",1,2,8

 

dim v用務地 as 用務地_make
set v用務地 = new 用務地_make
v用務地.リスト配列 "sheet_youmuti",2,3,4,5,10,12


みたいな感じで書いていたのをクラス「配列_make」とかで、まとめて・・・
///////////////////////////////
<仮の例>
dim v氏名 as 配列_make
set v氏名 = new 配列_make
v氏名.配列_make1 "sheet_simei",1,2,8


dim v用務地 as 配列_make
set v用務地 = new 配列_make
v用務地.配列_make2 "sheet_youmuti",2,3,4,5,10,12

だと、どうだろう。
(クラス「氏名_make、用務地_make、用務内容_make」は不要になる。)

 

配列のまとめ方としては分かりやすくなるが、
この先、クラス「氏名_make、用務地_make、用務内容_make」で、独自のメソッドなどが出てくるかもしれない。

そう考えると、クラス「氏名_make、用務地_make、用務内容_make」はそれぞれ残し、
その中で、さらに子(孫)クラスとして、クラス「配列_make」を呼ぶ方がいい気がする。

 

●親モジュール
 ●子クラス「氏名_make」
  ●孫クラス「配列_make」
   (この場合、子クラス「氏名_make」は、孫クラス「配列_make」を呼ぶだけ)


この、問題点を考える・・・。
親子孫クラスまで階層ができると、またややこしいし、クラス同士の関係がややこしくなる・・・。
(孫クラスとして呼び出す場合以外、他の階層で呼び出す場合もありうる。)

というか、この関係構築・把握に慣れていく(しかない)のがオブジェクト指向なのか???
(目的(何を)手段(どうするか)の分け方・くくり方をしっかりしておく必要があるか)


=======================
で、ここまでの仮結論(方針)
この先やることが多いので、(自分にとっては)ここはひとまずこのままで、このリファクタリング(?)は、
内容を見極め、もう少し先でやるようにしようかと思う。


オブジェクト指向は、作っては壊しの作業ばかりなのかな・・・。汗。
というか、それぞれの必要な要件の詳細を先に決めておけばできるのだろうけど。素人なのでできない(見通せない)。

=======================

 

で、終わろうかと思ったが、仮で、上の改修をやってみた。
それなりに出来たが、このまとめた孫クラス「配列_make」は今回は実装しないことに。
(子クラス時点で、できていればそれでいいかと。)
特に、現時点では問題ないが、以下に気になった点を羅列しておきます。

 

<改修方法>
●親モジュール
  →→→①子クラスへのメソッドのコードを改修(必要なら)
 ●子クラス「氏名_make」 
  →→→①親クラスからの呼び出しメソッドを改修(必要なら)
  →→→②子クラスのメソッドを孫クラスへ卸す(孫クラスで要改修)
  →→→③孫クラス呼び出し用のコードを新設
     (親コードに書いていたのを卸して改修
  →→→④ここでも孫クラスインスタンス化(戻り値必要)
  ●孫クラス「配列_make」
  →→→①子クラスから卸したのコードを改修(ただ動くだけならあまり直さないでいいかも)
     (↑寄せ集めでなくきちんとしたいなら、private変数など直す)

+++++++++++++++++++++++

・上記のように、引っ越すには、当該「配列_make」だけでなく上位のコード、下位のコードも直す必要あり。
(というか、下にコードを卸す感じ。)
・階層を変えるため、クラス名も分かりやすく命名し直しか・・・も。
(また、子クラスへ引き渡しが多くなると、インスタンス化した変数がきちんと消えていくのか気になる。)
(ローカルウィンドウだと(なんとなく)、消えているようだ。不要になればきちんと消せばいいのだけれど。)
・中間の子クラスでは、孫クラスのインスタンス化が必要。
(また、中間の子クラス同志では、リスト配列_makeのような、「氏名_make、用務地_make、用務内容_make」でかなり似た孫クラスインスタンス化のコードを書く)
・孫クラスも、各メソッドの変数が寄せ集めただけではバラバラで整理し直した方がいい。


結局、コード作成途中で、「配列を作る」という①機能ごとで、「下請けクラス」(孫クラス:クラス「配列リスト」)を作ればいいと思ったが、
上の「氏名_make、用務地_make、用務内容_make」では、②目的ごとにクラスを分けることをしている。

 

今回のクラス「配列リスト」には、1次元配列や、2次元配列、シートを読み込む際、必要な列の指定や、前列を読み込むcurrentregionを使った各種メソッドができたが、
これを洗練、整理して、実装した方がいいのだろう。(コードも減るし。)しかし、中には、面倒なpublic変数が1個含まれたりしている。
(昔、どうしても解決できなかったpublic変数・・・。)


文書作成でいう「推敲」が必要だろうか。

 

ただ、今後の見通しがまだまだ立たないため、「氏名_make、用務地_make、用務内容_make」個々のクラス内単独に処理していた方が、
「考えるスコープ」が少なくて済むかも・・・。

 

どうしようか。慣れれば、改修した方の方がいいか。

 

その他:
命名について
クラスを追加したりすると、目的や機能が少し変わり、命名など直す必要に迫られる・・・。「日本語名詞+英語動詞」でいいかなと思ったが、
下位にメソッドを持つクラスを追加すると、例えば、英語動詞部分(_make)が、ジャマで分かりにくくなる。

 

クラスのまとめ方について
「クラスの中では、どんな方法で、処理されていても、問題ない」という考えを、拡大解釈して、クラスの中は、「書き散らかし放題」ではいけないのだろう。
それでは、「手続き型で、スパゲッティ状態」になっているのと同じか。

 

クラス同志も一見して、分かりやすいように、配置できないかな・・・プロジェクトブラウザ様。

 

追記:クラス「配列リスト」について

中身は、ぐちゃぐちゃでも、「使おうかな」とも思っています。

全体を作ってから、個別部分は直してもいいかなという気もあります。悩む。

(せめて命名だけは、分かりやすくできないかな。)

 

エクセルVBAクラス備忘録(変数(配列))スコープ(クラス・function仕様)

20240504エクセルVBAクラス備忘録(変数(配列))スコープ(クラス・function仕様)


<前書き>
エクセルVBAオブジェクト指向、初心者です。
VBA自体は、以前から使用していました。
オブジェクト指向というものがあることは、知っていましたが、ネットなどを見てもよく分からず、挫折した過去あり。
<以上前書き>

(素人なので間違っているかもしれません。汗。)

 

今回は、コードは書いていません。
ここ最近、クラス、オブジェクト指向をやってきて、思ったこととしたことの?備忘録(未確認・未確定メモ)です。
(素人なので間違っているかも。汗)


●標準モジュールで、宣言したpublic変数
例えば、ユーザーフォームでも使えるが、そのユーザーフォームで、クラスを生成し、そのクラス内で、当該public変数は使える。
(メソッドとして、クラス内にfunction関数を置き、そこで当該変数(配列など)を作ることは可能)・・・まあ、当たり前か。
しかし、そのクラス内で作った内容は、ユーザーフォームに戻った時、「そのままでは」、入っていない。・・・クラス内に封じ込め???
で、function関数なら、ユーザーフォームに変数(戻り値)を返せるので、返すと使える。・・・public変数なのになんか不思議。

 

●ユーザーフォームで、宣言したdim変数
上記同様で、ユーザーフォームで、クラスを生成し、そのクラス内で、当該dim変数は使える。
(メソッドとして、クラス内にfunction関数を置き、そこで当該変数(配列など)を作ることは可能)・・・まあ、当たり前か。
しかし、そのクラス内で作った内容は、ユーザーフォームに戻った時、「そのままでは」、入っていない。・・・クラス内に封じ込め???
で、function関数なら、ユーザーフォームに変数(戻り値)を返せるので、返すと使える。・・・ユーザーフォームで、作ったdim変数なのになんか不思議。


プロパティ(ゲッター)で出すのと同じ感じか・・・。
そんな仕様なのかな。

 

その他(感謝)
VBAのプロジェクトエクスプローラーで、ユーザーフォームのコードを開くとき、当該ユーザーフォームを指定して「シフト+エンター」で、
(オブジェクト表示でなく、)いきなりコードを開ける。(便利)
youtube「ひとりごとワンダーランド」様のユーザーフォーム上のテキストの動的配置の動画に紹介してありました。

 

エクセルVBAクラス備忘録(作りかけのモジュールメモ)

20240428エクセルVBAクラス備忘録(作りかけのモジュールメモ)

 

<前書き>
エクセルVBAオブジェクト指向、初心者です。
VBA自体は、以前から使用していました。
オブジェクト指向というものがあることは、知っていましたが、ネットなどを見てもよく分からず、挫折した過去あり。
<以上前書き>

(素人なので間違っているかもしれません。汗。)

 

今回は、コードは書いていません。
ここ最近、クラス、オブジェクト指向をやってきて、思ったこととしたことのまとめ?備忘録(まとまっていない)です。

 

〇どういうことをオブジェクト指向ですればいいのか?
個人的には、過去に作った仕事で使うマクロ(手続き型)が、時代とともに仕事のやり方が変わり、古くなってきたので、
この際、オブジェクト指向で、最近の仕事に合わせて作り直そうかと思っています。

 

〇少しオブジェクト指向(クラス)を勉強し始めると、
「あ、こんなことも、オブジェクトなら、いちいち書かなくていいかも」とか
思ったりします。

例えば、今まで、ユーザーフォームで言えば、
○○=textbox1.value
とか書いていたものも、
textbox全部をまとめて処理すれば、(for eachとかで)、
いっぱいコード書かなくていいし、

例えば、今まで、シートで言えば、
□□ = cells(1,i).value
として、列全部を変数に入れていたのも、
列の表題をコレクションとかでリストにして、処理すれば、
いっぱいコード書かなくていいし、
とか、思ったりもします。

でも、そうすると、抽象化というか、ぱっと見何の処理をしているのか、分かりにくくなるし、
そもそも、そんな方法で、VBAの達人の皆様は作成しているのかとか思ったりもします。

オブジェクト指向は、廃れたとかいう記事も見るし・・・どうしようかなという気にもなる。)

 

〇「y-moride.com」様のホームページ、(感謝)
https://y-moride.com/vba/collection-how-to-use.html
【[VBA]Collectionを知り、使い方を知る】
には、データの列の表題をコレクションで取り込み、その標題を使って、処理を行っています。
(コレクションに入れてしまえば、行数・列数は使わない。)
これを見て、こういうやり方でも行けるんだなと思いました。
自分もシートの表の並びを無視して、標題で処理する方法を少し書いていたので。

 

〇自分には、今現在、クラスの「武器」が少ない。
自分の扱えるクラス関係の武器は、「コレクション」だけという感じ。「ディクショナリ」も使えるかもしれないが、
「FileSystemObject」などは、「使ったことあったっけ?」という記憶。
(こんな風につかえるのかなと手探り状態・・・)

 

〇今現在のオブジェクト指向のコードは、手続き型には劣るし、ピボットテーブルやパワークエリでもシート関数でもできることを再現する程度。

 

〇以下、作っては、放棄?のクラスのコード達。(一部)


自分用メモ
①ユーザーフォーム関係
・外部ファイル処理(クラス)
・職員クラス、職員リストクラスを作成
・ユーザーフォームのコンボボックスのデータ(職員リストクラスのデータを利用)

②ワークシート関係
・シートに名前をつけて、セルデータ、セルデータコレクションクラスを作成
・上記、データ取得
・取得したデータをデータベースシートに書き込むため、データベースシートの表題をEnumにして、
該当するセルデータリストの値を、データシート列に書き込む

③ユーザーフォーム関係(まだやり始め)
・ユーザーフォームのコントロールを回し(for each文)、テキストボックスの値を取得

 

まあ、こうしてみると全部の工程が線としてつながれば、形になるのか・・・。


ただし、過去に手続き型で、やった細かい制御まで、再び新たに組み込むことになるが、かなり面倒(不具合があると、いろんなところでサブルーチンを仕込んだ記憶アリ・・・)。
(他人に使ってもらわないと分からないこともあるし。)

要件定義とか仕様とかどう書くのか(書けない。)

 

・・・時代に合わせた新しい作り方・使い方までは、程遠そうです。(しかも2本作り直しを予定)(もうこれらも時代遅れなんでしょうが。)


整理しながら、やるしかないですね。笑

 

エクセルVBAクラス備忘録 コレクションひな形

20240427 エクセルVBAクラス備忘録 コレクションひな形

 

<前書き>
エクセルVBAオブジェクト指向、初心者です。
VBA自体は、以前から使用していました。
オブジェクト指向というものがあることは、知っていましたが、ネットなどを見てもよく分からず、挫折した過去あり。
<以上前書き>

(素人なので間違っているかもしれません。汗。)


今回、collectionのひな形をいくつか書いておきます。
ここに書いたのは、簡単なもので、実際はもっとややこしくしないと実装できないと思います。
(これらでも手こずりましたが。汗)

(実践的なのは、次回以降にメモ程度に書こうかな。)

(注:最初の方のサンプルはクラスではありません。最後にクラスを使ったサンプルを書きました。)


////////////////////////////////////////
Sub コレクションひな形1()

    Dim 果物 As New Collection

    果物.Add "りんご"
    果物.Add "みかん"
    果物.Add "桃"
    果物.Add "メロン"
  
    Stop
    
End Sub
////////////////////////////////////////
Sub コレクションひな形1_2()

    Dim 果物 As New Collection

    果物.Add Item:="りんご"
    果物.Add Item:="みかん"
    果物.Add Item:="桃"
    果物.Add Item:="メロン"
  
    Stop
    
End Sub
////////////////////////////////////////
-----------------------------------------------
上記2つは同じ意味です。アイテムだけ、コレクションに入れている。
個々のアイテムの取り出し方は、
    Debug.Print 果物(1)
    Debug.Print 果物.Item(1)
でもいいです。

-----------------------------------------------
////////////////////////////////////////
Sub コレクションひな形2()

    Dim ヒト型 As New Collection

    ヒト型.Add "山田"
    ヒト型.Add "太郎"
    ヒト型.Add "男"
    ヒト型.Add "30"
    
    Stop
    
End Sub

////////////////////////////////////////
Sub コレクションひな形2_2()

    Dim ヒト型 As New Collection

    ヒト型.Add Item:="山田", Key:="姓"  'keyは一意であること
    ヒト型.Add Item:="太郎", Key:="名"
    ヒト型.Add Item:="男", Key:="性別"
    ヒト型.Add Item:="30", Key:="年齢"
    
    
    Debug.Print ヒト型(2)     'インデックスを使う
    Debug.Print ヒト型("名")  'keyを使う
    
    Dim hito As Variant
    For Each hito In ヒト型
        
        Debug.Print hito     'コレクションヒト型を回す
    
    Next hito
    
    Stop
    
End Sub
////////////////////////////////////////
--------------------------------------------------
上記も、果物とほぼ同じサンプルです。
keyを使ってます。keyは一意であることが条件です。
keyを使えば、
    Debug.Print ヒト型("名")  
で、取り出しが可能です。

全アイテムを取り出したければ、
For Each hito In ヒト型
で、可能です。

ちなみに、一部、keyを使わなかった場合、どうなるのかやってみましたが、特に問題ないです。
------------------------------------------------
//////////////////////////////////////////////
Sub コレクションひな形2_2_参考()    '////////////一部にkeyなくても問題ないか(問題ない)

    Dim ヒト型 As New Collection

    ヒト型.Add Item:="山田", Key:="姓"
    ヒト型.Add Item:="太郎"              'keyなし////////問題ない
    ヒト型.Add Item:="男", Key:="性別"
    ヒト型.Add Item:="30", Key:="年齢"
    
    
    Debug.Print ヒト型(2)     'インデックスを使う
'    Debug.Print ヒト型("名")  'keyを使う   'keyなし////////コメントアウトすれば問題ない
    
    Dim hito As Variant
    For Each hito In ヒト型
        
        Debug.Print hito     'コレクションヒト型を回す
    
    Next hito
    
    Stop
    
End Sub
//////////////////////////////////////////////
--------------------------------------------
で、ここまで来て思うのは、「山田太郎さん 男 30歳」1人を集めたってしょうがないんだよ。
山田太郎さん 男 30歳」なら1データでいいじゃん。
「佐藤花子さん 女 31歳」とか、複数人のデータの集まりがほしいんだよ・・・、となると思います。
で、このコレクションの上位のコレクション「ヒトビト型」を考える必要が出てきます。


また、変な(間違った)考え方ですが、
最初のひな形「果物」の場合、
    果物.Add "りんご"
    果物.Add "みかん"
    果物.Add "桃"
    果物.Add "メロン"
keyを加えても、
    果物.Add "りんご","赤"
    果物.Add "みかん","オレンジ"
    果物.Add "桃","ピンク"
    果物.Add "メロン","緑"
これじゃ、「数量」とか、「産地」とか何もいれられないじゃないか??となってしまいます。
ここでは書きませんが、果物の場合は、リンゴなどの各アイテムの下位のコレクションを考えたりする必要があると思います。
(上記の例の"赤"等の色は、keyではなく、下位のコレクションに書くべきでしょうね??。汗)

-----------------------------------------------
//////////////////////////////////////////////
Sub コレクションひな形3_2()

    Dim ヒトビト型 As New Collection


        Dim ヒト型 As Collection
        Set ヒト型 = New Collection
    
        ヒト型.Add Item:="山田", Key:="姓"  'keyは一意であること
        ヒト型.Add Item:="太郎", Key:="名"
        ヒト型.Add Item:="男", Key:="性別"
        ヒト型.Add Item:="30", Key:="年齢"
    
    ヒトビト型.Add Item:=ヒト型
    
        Set ヒト型 = New Collection
    
        ヒト型.Add Item:="佐藤", Key:="姓"  'keyは一意であること
        ヒト型.Add Item:="花子", Key:="名"
        ヒト型.Add Item:="女", Key:="性別"
        ヒト型.Add Item:="31", Key:="年齢"
    
    ヒトビト型.Add Item:=ヒト型
    
    

    Debug.Print ヒトビト型(2).Item(1)
    Debug.Print ヒトビト型(2).Item("年齢")    'ヒトビト型のItem = ヒト型(コレクション)
    
End Sub
/////////////////////////////////////////////////
-----------------------------------------------
上記のコードで、「ヒト型」2人分を、「ヒトビト型」に入れました。
下記のコードでは、「ヒトビト型」のkeyに「ヒト型」の姓を入れました。
(この方が少し実用的かと。)
-----------------------------------------------
/////////////////////////////////////////////////
Sub コレクションひな形3_2_参考()    'ヒトビト型にkeyを持たせる

    Dim ヒトビト型 As New Collection


        Dim ヒト型 As Collection
        Set ヒト型 = New Collection
    
        ヒト型.Add Item:="山田", Key:="姓"  'keyは一意であること
        ヒト型.Add Item:="太郎", Key:="名"
        ヒト型.Add Item:="男", Key:="性別"
        ヒト型.Add Item:="30", Key:="年齢"
    


'    ヒトビト型.Add Item:=ヒト型, Key:="山田"    'これOK
    ヒトビト型.Add Item:=ヒト型, Key:=ヒト型("姓")  'これもOK
      
      
        Set ヒト型 = New Collection
    
        ヒト型.Add Item:="佐藤", Key:="姓"  'keyは一意であること
        ヒト型.Add Item:="花子", Key:="名"
        ヒト型.Add Item:="女", Key:="性別"
        ヒト型.Add Item:="31", Key:="年齢"

'    ヒトビト型.Add Item:=ヒト型, Key:="佐藤"     'これOK
    ヒトビト型.Add Item:=ヒト型, Key:=ヒト型("姓")  'これもOK
    
    
    Debug.Print ヒトビト型(1).Item(1)
    Debug.Print ヒトビト型("山田").Item("名")
    Debug.Print ヒトビト型("山田").Item("年齢")
    Debug.Print ヒトビト型(2).Item(1)
    Debug.Print ヒトビト型(2).Item("年齢")    'ヒトビト型のItem = ヒト型(コレクション)
    Debug.Print ヒトビト型("佐藤").Item("年齢")
    
End Sub
//////////////////////////////////////////////
で、ここまで来ると、「「ヒト型」は「コレクション」より、「クラス」にした方がいいのでは?」という気になります。

'クラス「ヒト型」
Option Explicit
'宣言
Private m姓 As String
'宣言
Private m名 As String
'宣言
Private m性別 As String
'宣言
Private m年齢 As Long


'let
Public Property Let 姓(p姓 As String)
     m姓 = p姓
End Property

'get
Public Property Get 姓() As String
     姓 = m姓
End Property

 


'let
Public Property Let 名(p名 As String)
     m名 = p名
End Property

'get
Public Property Get 名() As String
     名 = m名
End Property

 


'let
Public Property Let 性別(p性別 As String)
     m性別 = p性別
End Property

'get
Public Property Get 性別() As String
     性別 = m性別
End Property

 

'let
Public Property Let 年齢(p年齢 As Long)
     m年齢 = p年齢
End Property

'get
Public Property Get 年齢() As Long
     年齢 = m年齢
End Property

//////////////////////////////////////////////
Function TEST_ヒト型1_3()
    Dim ヒトビト型 As New Collection  'このコレクションはクラスではない

    Dim vヒト型 As ヒト型
    Set vヒト型 = New ヒト型

    vヒト型.姓 = "山田"
    vヒト型.名 = "太郎"
    vヒト型.性別 = "男"
    vヒト型.年齢 = "30"
    

    ヒトビト型.Add vヒト型      'keyは付けていない

    
    Set vヒト型 = New ヒト型
    vヒト型.姓 = "佐藤"
    vヒト型.名 = "花子"
    vヒト型.性別 = "女"
    vヒト型.年齢 = "31"
    

    ヒトビト型.Add vヒト型    'newしないとコレクション上書きされてしまう
    
    
'コレクションの全部取り出し分からない
    'ボツ
'    Debug.Print ヒトビト型(1).item1.姓
'    Debug.Print ヒトビト型(1).item1(1)
'    Debug.Print ヒトビト型(1).Item(1)
'    Debug.Print ヒトビト型(1).Item(1).姓
'    Debug.Print ヒトビト型.Item(1).Item(1).姓
    Debug.Print ヒトビト型.Item(1).姓      '正解
 
    
    For Each vヒト型 In ヒトビト型

        Debug.Print "vヒト型_data"  
        Debug.Print vヒト型.姓
        Debug.Print vヒト型.年齢
   
        
    Next vヒト型
    Stop
End Function
/////////////////////////////////////
-----------------------------------
上記では、「ヒト型」をクラス、「ヒトビト型」をコレクションにしました。
For Each文で、コレクション「ヒトビト型」を回せば、各「ヒト型」のデータを確認できます。

-----------------------------------
最後に、「ヒトビト型」もクラスにします。
初心者なので、「いつも隣にIT」様(感謝いたします)
のHPを参考に、順番に「クラスに寄せて」いきます。
3段階あります。

-----------------------------------
/////////////////////////////////第一段階
クラス「ヒト型」は変更なし
////////////////////////////////
'クラス「ヒトビト型」
Option Explicit

Public Items As Collection

//////////////////////////////// 
Function TEST_ヒト型3_1()


 'クラスにした
    Dim vヒトビト型 As New ヒトビト型
    Set vヒトビト型 = New ヒトビト型
    Set vヒトビト型.Items = New Collection

    Dim vヒト型 As ヒト型
    Set vヒト型 = New ヒト型

    vヒト型.姓 = "山田"
    vヒト型.名 = "太郎"
    vヒト型.性別 = "男"
    vヒト型.年齢 = "30"
    

    vヒトビト型.Items.Add vヒト型      'keyは付けていない

    
    Set vヒト型 = New ヒト型
    vヒト型.姓 = "佐藤"
    vヒト型.名 = "花子"
    vヒト型.性別 = "女"
    vヒト型.年齢 = "31"
    

    vヒトビト型.Items.Add vヒト型    'newしないとコレクション上書きされてしまう
    

    
    For Each vヒト型 In vヒトビト型.Items  'コレクションであるItemsを回す

        Debug.Print "vヒト型_data"  '動く
        Debug.Print vヒト型.姓
        Debug.Print vヒト型.年齢
   
        
    Next vヒト型

Stop
End Function
////////////////////////////////////
////////////////////////////////////第二段階
クラス「ヒト型」は変更なし
////////////////////////////////
'クラス「ヒトビト型」

Option Explicit


Public Items As Collection


Private Sub Class_Initialize()
    Set Items = New Collection
End Sub
////////////////////////////////
Function TEST_ヒト型3_2()  'class_initialize


 'クラスにした
    Dim vヒトビト型 As New ヒトビト型
    Set vヒトビト型 = New ヒトビト型
'    Set vヒトビト型.Items = New Collection  '不要

    Dim vヒト型 As ヒト型
    Set vヒト型 = New ヒト型

    vヒト型.姓 = "山田"
    vヒト型.名 = "太郎"
    vヒト型.性別 = "男"
    vヒト型.年齢 = "30"

    vヒトビト型.Items.Add vヒト型      'keyは付けていない

    
    Set vヒト型 = New ヒト型
    vヒト型.姓 = "佐藤"
    vヒト型.名 = "花子"
    vヒト型.性別 = "女"
    vヒト型.年齢 = "31"
    

    vヒトビト型.Items.Add vヒト型    'newしないとコレクション上書きされてしまう
    

    
    For Each vヒト型 In vヒトビト型.Items  'コレクションであるItemsを回す

        Debug.Print "vヒト型_data"  '動く
        Debug.Print vヒト型.姓
        Debug.Print vヒト型.年齢
   
        
    Next vヒト型

Stop
End Function
//////////////////////////////
////////////////////////////////////第三段階
クラス「ヒト型」は変更なし
////////////////////////////////
'クラス「ヒトビト型」
Option Explicit


Public Items As Collection


Private Sub Class_Initialize()
    Set Items = New Collection
    
    
    Dim vヒト型 As ヒト型
    Set vヒト型 = New ヒト型

    vヒト型.姓 = "山田"
    vヒト型.名 = "太郎"
    vヒト型.性別 = "男"
    vヒト型.年齢 = "30"
    

    Items.Add vヒト型      'keyは付けていない

    
    Set vヒト型 = New ヒト型
    vヒト型.姓 = "佐藤"
    vヒト型.名 = "花子"
    vヒト型.性別 = "女"
    vヒト型.年齢 = "31"
    

    Items.Add vヒト型    'newしないとコレクション上書きされてしまう
    

    
    For Each vヒト型 In Items  'コレクションであるItemsを回す

        Debug.Print "vヒト型_data"  '動く
        Debug.Print vヒト型.姓
        Debug.Print vヒト型.年齢
   
        
    Next vヒト型
    
    
End Sub
////////////////////////////////////
Function TEST_ヒト型3_3()  'class_initializeの2


 'クラスにした
    Dim vヒトビト型 As New ヒトビト型
    Set vヒトビト型 = New ヒトビト型

Stop
End Function
//////////////////////////////////
---------------------------------
上記の感じです。
あ~、ヒト型の個別具体データまで、ヒトビト型に入れたのは、入れすぎかも。

でも、一旦これで終了します。

データの取り出し方や入れ方ですが、for each文が便利ですが、クラスについては、すこし気になります。(クラスのプロパティの指定方法を変数で回したいが無理?)

 

'foreach文の使いどころ・・・(使えるところ、使えないところ) 考え中
①コレクションのitemsに入れる
②コレクションのitemを回す(取り出し)
③クラスのプロパティに入れる(上位コレクションを回して個々に入れる。個々にプロパティを設定しないとできない?まとめて入れられない?)
④クラスのプロパティを回す。上位コレクションを回して個々に取り出し。まとめて取り出すことはできない?(プロパティを変数にしたりとか考えたけどうまくできない)