Excel VBA 質問スレッド №2001 (解決済)

vbaの配列とユーザーフォーム

投稿者 : ara     投稿日時 : 2024/08/25(Sun) 13:30:11     OS : Windows 10     EXCEL : Excel 2013
基本的な質問ですみません。


レジソフトを作りたい。
① 例えば1から10の商品があり、エクセルシートに商品名、商品単価が記録されている。
② ユーザーフォームに商品ボタンを10つくり、①のデータをボタンに商品名と単価が表示される。
③ ボタンが押されたら多次元配列にその回数を保存。マイナスボタンもつくり、押されたら0になるまで減らす。すべて配列で保存。
④ 商品単価✖押された回数の計算もし、多次元配列に保存



知りたい知識
①と②はできた。
③の多次元配列をユーザーフォーム上に書けばいいのか? 標準モジュールに書けばいいのか? それとも クラスに書けばいいのか?が分からない。
 2回目のボタンを押したり、別のボタンが押されると前に保存した配列のデータが消えてしまう。
④も③と同様。

つまり、配列の宣言の場所、配列の操作をどこに書けばいいのか? (※ 標準モジュールに書く? ユーザーフォーム上に書く? クラスモジュールに書く?ということが理解できていない。また、どのような書き方がいいのか? シンプルな書き方などデータの受け渡しなどの知識が乏しいです。
アドバイスよろしくお願いいたします。
  

スポンサーリンク
[返信 1] Re : vbaの配列とユーザーフォーム
投稿者 : higeru     投稿日時 : 2024/08/25(Sun) 17:25:51
ユーザーフォームというかフォームモジュールのプロシージャだけで読み書きするならフォームモジュールに書けばよいし、標準モジュールのプロシージャでも使うなら標準モジュールで Public 宣言すればよいです。クラスモジュールはあり得ません。

ただ最終的にその配列の値はどうするの?ということを考えたら、配列でなくてシートを使ってもよいかと。配列を使って処理を高速化するということでもなく、シートであればどこからでも読み書きできますし。 

[返信 2] Re : vbaの配列とユーザーフォーム
投稿者 : てらてら     投稿日時 : 2024/08/25(Sun) 19:15:45
こんにちは。

>③の多次元配列をユーザーフォーム上に書けばいいのか? 標準モジュールに書けばいいのか? それとも クラスに書けばいいのか?が分からない。
> 2回目のボタンを押したり、別のボタンが押されると前に保存した配列のデータが消えてしまう。

>つまり、配列の宣言の場所、配列の操作をどこに書けばいいのか?


 配列に限らず変数のスコープ(適用範囲)は、書く場所によって異なります。
 配列データが消えてしまうのは、プロシージャ内で配列を宣言した事によるものでしょう。
 
 つまり、どのモジュールに書くかではなく、プロシージャの外側で宣言しなければ永続的に配列の値を保持できません。

 もし、ユーザーフォーム上で処理で値を保持したいのであれば、ユーザーフォームモジュール(UserForm1など)の、プロシージャの外で宣言して
 UserForm_Initialize() などで初期化するのがセオリーでしょう。
 以下はユーザーフォームにテキストボックスとスピンボタンを配置した例です。
 (この場合ユーザーフォームが閉じたら arr は消滅します。)
 参考にしてみてください。

'///// UserForm1 モジュールに記述する //////
Dim arr(1, 2) As Variant    ’ここに宣言する

Private Sub SpinButton1_SpinUp()
    Dim c As Long
    c = CInt(Me.TextBox1.Value)
    c = c + 1
    arr(0, 2) = c
    Me.TextBox1.Value = c
    Stop

End Sub

Private Sub UserForm_Initialize()
    arr(0, 0) = "ペン"
    arr(0, 1) = 100
    arr(0, 2) = 0
    arr(1, 0) = "消しゴム"
    arr(1, 1) = 200
    arr(1, 2) = 0
    
    Me.TextBox1.Text = arr(0, 2)
    Me.TextBox2.Text = arr(1, 2)
End Sub


 
 もちろん、他のやり方でも配列がインスタンス化されていればOKです。

[返信 3] Re : vbaの配列とユーザーフォーム
投稿者 : ara     投稿日時 : 2024/08/25(Sun) 22:20:10
■[返信 2] てらてらさん(2024-08-25 19:15:45)の記事
> こんにちは。

> >③の多次元配列をユーザーフォーム上に書けばいいのか? 標準モジュールに書けばいいのか? それとも クラスに書けばいいのか?が分からない。
> > 2回目のボタンを押したり、別のボタンが押されると前に保存した配列のデータが消えてしまう。

> >つまり、配列の宣言の場所、配列の操作をどこに書けばいいのか?


>  配列に限らず変数のスコープ(適用範囲)は、書く場所によって異なります。
>  配列データが消えてしまうのは、プロシージャ内で配列を宣言した事によるものでしょう。
>  
>  つまり、どのモジュールに書くかではなく、プロシージャの外側で宣言しなければ永続的に配列の値を保持できません。

>  もし、ユーザーフォーム上で処理で値を保持したいのであれば、ユーザーフォームモジュール(UserForm1など)の、プロシージャの外で宣言して
>  UserForm_Initialize() などで初期化するのがセオリーでしょう。
>  以下はユーザーフォームにテキストボックスとスピンボタンを配置した例です。
>  (この場合ユーザーフォームが閉じたら arr は消滅します。)
>  参考にしてみてください。

> '///// UserForm1 モジュールに記述する //////
> Dim arr(1, 2) As Variant ’ここに宣言する

> Private Sub SpinButton1_SpinUp()
> Dim c As Long
> c = CInt(Me.TextBox1.Value)
> c = c + 1
> arr(0, 2) = c
> Me.TextBox1.Value = c
> Stop

> End Sub

> Private Sub UserForm_Initialize()
> arr(0, 0) = "ペン"
> arr(0, 1) = 100
> arr(0, 2) = 0
> arr(1, 0) = "消しゴム"
> arr(1, 1) = 200
> arr(1, 2) = 0

> Me.TextBox1.Text = arr(0, 2)
> Me.TextBox2.Text = arr(1, 2)
> End Sub


>  
>  もちろん、他のやり方でも配列がインスタンス化されていればOKです。

分かりやすい解説ありがとうございます。

自分なりに書いてみました。が駄目でした。

クラスモジュール↓

Private WithEvents Btn As MSForms.CommandButton
'ボタンの数字を格納する変数を宣言
Private Index As String


Public Sub NewClass(ByVal c As MSForms.CommandButton, _
ByVal i As String)
    'いわゆるコンストラクタ処理

    '引数のコマンドボタンを変数に格納
    Set Btn = c
    'コマンドボタンの数字を変数に格納
    Index = i
    
End Sub

Private Sub Btn_Click()
    'frmCalcフォームのTextBox1に、
    '変数Indexの中身を表示する
    
'ここにボタン0-17が押された時に押された回数を配列に保存したい
    
End Sub

ユーザーフォームモジュール↓
Option Explicit

Dim arry(10, 10) As String

Private NumBtn(0 To 17) As New Class1


Private Sub UserForm_Initialize()
    'インスタンスの生成

    Dim i As Integer
    Dim y As Integer
      
    For i = 0 To 17
        y = Cells(1 + i, 2)
        NumBtn(i).NewClass Controls("b" & i), y
    Next

End Sub

標準モジュール↓
Sub a()
   
frmCalc.Show 0

For kei = 0 To 17
    frmCalc.Controls("b" & kei).Caption = Cells(kei + 1, 1)
Next

End Sub

こんな感じで書いてみましたが、できませんでした。何がいけないのか? どの知識が足りないのか教えて下さい。

[返信 4] Re : vbaの配列とユーザーフォーム
投稿者 : ara     投稿日時 : 2024/08/25(Sun) 22:26:25
■[返信 1] higeruさん(2024-08-25 17:25:51)の記事
> ユーザーフォームというかフォームモジュールのプロシージャだけで読み書きするならフォームモジュールに書けばよいし、標準モジュールのプロシージャでも使うなら標準モジュールで Public 宣言すればよいです。クラスモジュールはあり得ません。

> ただ最終的にその配列の値はどうするの?ということを考えたら、配列でなくてシートを使ってもよいかと。配列を使って処理を高速化するということでもなく、シートであればどこからでも読み書きできますし。

ありがとうございます。配列はパブリックで宣言できないと思ってました。変数で試したらできたんですが、配列は記述方法が間違ってたのかもしれません。
シート使えばいいだけなんですよね。でも使わずにやってみたくなりました。 

[返信 5] Re : vbaの配列とユーザーフォーム
投稿者 : てらてら     投稿日時 : 2024/08/26(Mon) 07:05:37
>こんな感じで書いてみましたが、できませんでした。何がいけないのか? どの知識が足りないのか教えて下さい。

何が出来ないのかを書くようにしてください。

ユーザーフォームに何を配置しているのか不明ですが、コードを見た感じですと、以下の部分がおかしいと思います。

>Private NumBtn(0 To 17) As New Class1

プロシージャの外では宣言のみにしましょう。
インスタンス化は、UserForm_Initialize()内で行います。

Private Sub UserForm_Initialize()
    'インスタンスの生成

    Dim i As Integer
    Dim y As Integer
      
    For i = 0 To 17
        Set NumBtn(i) = New Class1
        y = Cells(1 + i, 2)
        NumBtn(i).NewClass Controls("b" & i), y
    Next
End Sub




ユーザーフォームをShowした後の処理(5行目以下)は、ユーザーフォームモジュールの方に書くべきでしょう。

Sub a()
   
    frmCalc.Show 0
    
    For kei = 0 To 17
        frmCalc.Controls("b" & kei).Caption = Cells(kei + 1, 1)
    Next

End Sub


その他は検証できませんでした。
とりあえず、今時間が無いので答えられるのは以上です。

[返信 6] Re : vbaの配列とユーザーフォーム
投稿者 : higeru     投稿日時 : 2024/08/26(Mon) 11:07:29
 先のコメントに書いた「最終的にその配列の値はどうするの?」が不明ですが、標準モジュールに書くのが間違いないかと思います。
 10個(0-17だと18個??)のコマンドボタンをクリックした時の処理を共通化したいのだとうと推測しますが、残念ながら全然できていません。

 以下はとりあえず1次元配列に、ボタン(コードを簡素化するために CommandButton0~CommandButton9 であることを想定)をクリックした回数を格納・表示するようにしたサンプルです。

●標準モジュール

Public NumBtn(4) As Long

Sub ShowUserForm()
    Erase NumBtn
    UserForm1.Show
End Sub

Public Sub SetValueNumBtn(ctrl As MSForms.CommandButton)
    Dim btnNo As Long: btnNo = Val(Right(ctrl.Name, 1))
    NumBtn(btnNo) = NumBtn(btnNo) + 1
    MsgBox "NumBtn(" & btnNo & ")" & " = " & NumBtn(btnNo)
End Sub

●フォームモジュール

Private colEvent As New Collection

Private Sub UserForm_Initialize()
    Call setControlEvent("CommandButton#")
End Sub

Private Sub setControlEvent(ByVal ctrl_name As String)
    Dim clsEv As New clsEvent
    Dim ctrl As Control
    For Each ctrl In Me.Controls
        If ctrl.Name Like ctrl_name Then
            Set clsEv = New clsEvent
            clsEv.SetCtrl ctrl
            colEvent.Add clsEv
        End If
    Next
End Sub

●クラスモジュール(クラス名 clsEvent)

Private WithEvents Btn As MSForms.CommandButton

Public Sub SetCtrl(new_ctrl As MSForms.CommandButton)
    Set Btn = new_ctrl
End Sub

Private Sub Btn_Click()
    Call SetValueNumBtn(Btn)
End Sub

[返信 7] Re : vbaの配列とユーザーフォーム
投稿者 : higeru     投稿日時 : 2024/08/26(Mon) 11:12:28
標準モジュールの配列のPublic宣言で

Public NumBtn(4) As Long

となってましたが(どりあえず4個で動作確認してたので)、4 → 10 にしてください。

蛇足ながらコマンドボタンの名前を CommandButton0~CommandButton9 としているのは、処理を共通化するボタンを "CommandButton#" で判定しているためです。

[返信 8] Re : vbaの配列とユーザーフォーム
投稿者 : ara     投稿日時 : 2024/08/26(Mon) 14:10:36
■[返信 7] higeruさん(2024-08-26 11:12:28)の記事
> 標準モジュールの配列のPublic宣言で

> Public NumBtn(4) As Long

> となってましたが(どりあえず4個で動作確認してたので)、4 → 10 にしてください。

> 蛇足ながらコマンドボタンの名前を CommandButton0~CommandButton9 としているのは、処理を共通化するボタンを "CommandButton#" で判定しているためです。

『最終的にその配列の値はどうするの?』

①商品名、単価をエクセルシートに事前入力し

②注文を確認して

③支払い金額、注文内容を表示させたいと考えています。エクセルシートとユーザーフォームに表示したいと考えております。

②で商品ボタンを押すと注文個数が把握され、単価を考慮して支払い金額を表示が最終的な処理の目標です。

今日から出張で、返信が遅くなると思います。
丁寧にご指導してくださり、ありがとうございます。
まだ、いただいたコードの確認をしてないので、時間をかけて確認したいと思います。

[返信 9] Re : vbaの配列とユーザーフォーム
投稿者 : ara     投稿日時 : 2024/09/03(Tue) 20:06:13
■[返信 6] higeruさん(2024-08-26 11:07:29)の記事
>  先のコメントに書いた「最終的にその配列の値はどうするの?」が不明ですが、標準モジュールに書くのが間違いないかと思います。
>  10個(0-17だと18個??)のコマンドボタンをクリックした時の処理を共通化したいのだとうと推測しますが、残念ながら全然できていません。

>  以下はとりあえず1次元配列に、ボタン(コードを簡素化するために CommandButton0~CommandButton9 であることを想定)をクリックした回数を格納・表示するようにしたサンプルです。

> ●標準モジュール

> Public NumBtn(4) As Long

> Sub ShowUserForm()
> Erase NumBtn
> UserForm1.Show
> End Sub

> Public Sub SetValueNumBtn(ctrl As MSForms.CommandButton)
> Dim btnNo As Long: btnNo = Val(Right(ctrl.Name, 1))
> NumBtn(btnNo) = NumBtn(btnNo) + 1
> MsgBox "NumBtn(" & btnNo & ")" & " = " & NumBtn(btnNo)
> End Sub

> ●フォームモジュール

> Private colEvent As New Collection

> Private Sub UserForm_Initialize()
> Call setControlEvent("CommandButton#")
> End Sub

> Private Sub setControlEvent(ByVal ctrl_name As String)
> Dim clsEv As New clsEvent
> Dim ctrl As Control
> For Each ctrl In Me.Controls
> If ctrl.Name Like ctrl_name Then
> Set clsEv = New clsEvent
> clsEv.SetCtrl ctrl
> colEvent.Add clsEv
> End If
> Next
> End Sub

> ●クラスモジュール(クラス名 clsEvent)

> Private WithEvents Btn As MSForms.CommandButton

> Public Sub SetCtrl(new_ctrl As MSForms.CommandButton)
> Set Btn = new_ctrl
> End Sub

> Private Sub Btn_Click()
> Call SetValueNumBtn(Btn)
> End Sub
↓以下の部分でエラーが出てしまい確認できませんでした。すみません。私のどこかがおかしいと思うのですが。。。。

Private Sub setControlEvent(ByVal ctrl_name As String)
Dim clsEv As New clsEvent

[返信 10] Re : vbaの配列とユーザーフォーム
投稿者 : higeru     投稿日時 : 2024/09/04(Wed) 09:43:08
■[返信 9] araさん(2024-09-03 20:06:13)の記事

> ↓以下の部分でエラーが出てしまい確認できませんでした。すみません。私のどこかがおかしいと思うのですが。。。。

> Private Sub setControlEvent(ByVal ctrl_name As String)
> Dim clsEv As New clsEvent

 クラス名が "clsEvent" になっていないのかと。

[返信 11] Re : vbaの配列とユーザーフォーム
投稿者 : ara     投稿日時 : 2024/09/14(Sat) 15:36:47
みなさま、たくさんのアドバイスありがとうございました。完璧にできたわけではありませんが、知りたいことを教えていただいたので一度解決済にしたいと思います。ありがとうございました。

当掲示板について
  • Excel VBA に関する掲示板です。Excel VBA に関する質問や疑問、それに対する解決方法など気軽に投稿してください。
  • 記事内ではHTMLのタグは使用できません。
  • 記事は一度投稿すると修正できません。内容を訂正したい場合は返信で対応してください。
  • Sub〜End Sub、Function〜End Function は自動的にプログラムコードとみなし、枠で囲って見やすくします。
  • Excel VBA とは関係ないことや、他人が不快に思うようなことなど、管理人が適当でないと判断した記事は削除する場合があります。
スポンサーリンク
返信入力フォーム
お 名 前  :
内  容   :

ステータス  :

認証コード  : キャプチャ画像 




( 処理日時 : 2025-07-05 08:57:49 )
タイトルとURLをコピーしました