• ベストアンサー

変数の扱いについて

こんにちは。お世話になります。 ユーザーフォームモジュールで宣言した変数を標準モジュールのサブプロシージャ内で使うには どのようにしたらよいのでしょうか? ====== ユーザーフォームモジュール ======= Dim MyData1 as Long Dim MyData2 as Long Private Sub CommandButton1_Click() For MyData1 = 1 To 10 MyData2 = MyData1 * 2 Call test1 Call test2 Next MyData1 End Sub ====== 標準モジュール ======= Sub test1() Worksheets("Sheet1").Range("A" & MyData1).value = MyData2 * MyData2 End Sub Sub test2() Worksheets("Sheet2").Range("A" & MyData1).value = MyData2 + MyData2 End Sub For~Nextは標準モジュール内ではなくユーザーフォームモジュール内に作りたいのです。 おわかりの方よろしくお願いしますm(_ _)m

  • suffre
  • お礼率76% (2013/2633)

質問者が選んだベストアンサー

  • ベストアンサー
  • Wendy02
  • ベストアンサー率57% (3570/6232)
回答No.8

taocat様、suffre様 あまり、VBAの一般論まで発展してしまうと、私自身混乱してしまいます。(^^; 参照渡しで、サブルーチン側のTest1に、引数のMyData1を変更するコードが入っているなら理解できるのですが、引数のMyData1を変更していくなら、逆に、明示的なByVal キーワードは無意味になってしまいます。 実際の設計では、私は、従属的なサブルーチンはローカルに、それも、Private にして、同じモジュールの中に入れています。 そういう点で、今回、#5 のStudyVBAさんが、ご指摘のように、確かに、ByVal キーワードを使ったほうが正解だったかもしれませんが、もともと、値渡し・参照渡しの キーワードをサブルーチン側につけるつけないというよりも、標準的には、参照渡しになっていまから、値渡しにすべきかは、コードの設計の趣旨の問題だと思います。 逆の見方をすると、メイン側から渡された引数が、値渡しのキーワードをつけないサブルーチン側で、引数の値を変更して、メインに戻ってきても、それが保持されているということなら、変数としての役割も、またシステム全体のコードとしての論理も適っていることだと思います。 何か、とっても難しい書き方になっていますが、つまり、キャッチボールをしていて、投げられた玉を、相手が、ボールに細工をして返してきても、そのまま受け取って、また使うのが参照渡しです。相手がボールを別のにすりかえても、ボールの大きさ(変数型)は変えられないので、使い続けます。 投げられたボールを、相手が細工して返してきたボールは受け取らず、次に、別の自分のボールを使うのが、値渡しっていうことかな?

その他の回答 (7)

  • taocat
  • ベストアンサー率61% (191/310)
回答No.7

再度のこんにちは。 suffreさん、こんにちは。 Wendy02さん、いつも丁寧なレスありがとうございます。 suffreさんのNo.6、Wendy02さんへのお礼のコメントが気になりましたので再度の書きこみです。 ---------------------------------------------- >でも、今回の場合は、 > For MyData1 = 1 To 10 >となっていますから、参照渡ししようとしても出来ないと思います。 そうかあ!Callで読んできて変数の値が変更されていてもNextでForに戻ればMyData1とMyData2は値が代入されてしまうので参照渡しの意味はなくなるということですね。 ----------------------------------------------- それは変数MyData2にだけ言えることです。先の回答にも書きましたが、変数MyData1については上記のことは言えません。 もし参照渡しをしていて、Test1側でMyData1が55に変更されたとしたら、Mainに戻っていったとき For MyData1=1 to 10 MyData1は55になってますので、このFor文は抜けてしますわけです。 それより危険なのは、Mydata1が、1~9までに変更された場合です。 上記For文は無限ループになります。 試しに、Test1側で、 MyData1を  2 に変更 MyData1を 55 に変更 これでテストしてみてください。ご理解いただけると思います。 尚,コメントの意味を取り違えていましたら、VBA勉強中ということでご容赦願います。

  • Wendy02
  • ベストアンサー率57% (3570/6232)
回答No.6

StudyVBA様へ 私も、そんなキャリアがあるわけではありませんが、一応、私のハンドルがあったので、付け足しておきます。 >今回の場合はその危険はないでしょうが、引数を使う場合、値渡し、参照渡しに常に気を配るようにするべきだと思います。 # Sub test1(ByVal MyData1 As Long, ByVal MyData2 As Long) 後で、その使い方を忘れてしまったりするような場合に、特定の引数の渡し方を、サブルーチン側に「明示的に」入れておいたほうがよいでしょうね。 ふつうは、引数を渡す側のコードの問題だから、あまり、私は、ユーザー定義関数のように重要視していません。(間違っているのかもしれませんが) でも、今回の場合は、  For MyData1 = 1 To 10 となっていますから、参照渡ししようとしても出来ないと思います。

suffre
質問者

お礼

>でも、今回の場合は、 > For MyData1 = 1 To 10 >となっていますから、参照渡ししようとしても出来ないと思います。 そうかあ!Callで読んできて変数の値が変更されていてもNextでForに戻ればMyData1とMyData2は値が代入されてしまうので参照渡しの意味はなくなるということですね。 これは頭使いますね・・・勉強になりました!

  • StudyVBA
  • ベストアンサー率0% (0/3)
回答No.5

こんにちは。 まだ回答するレベルではないのですが、序に頭に入れておいた方がいいと思いましたので一言。 サブルーチンTest1,2側ですが、少なくともMyData1はキーワードByValで値渡しにすべきではないでしょうか。 ByValなしだと参照渡し(ByRef)になりますので、サブルーチンTest1,2側で、変数Mydata1,MyData2の値を変えてしまうことができるので思わぬ結果を招くことになります。 今回の場合はその危険はないでしょうが、引数を使う場合、値渡し、参照渡しに常に気を配るようにするべきだと思います。 ---------------------------------------------- Sub test1(ByVal MyData1 As Long, ByVal MyData2 As Long) End Sub ---------------------------------------------- Wendy02さん、これ間違いないですよね?・(^^;;;  

suffre
質問者

お礼

StudyVBAさんこんにちは! 参照渡しと値渡しがあるんですね。これも大事典で調べてみました。 図解つきでとてもわかりやすかったです。 ByValだと標準モジュールで変更された変数の値はユーザーフォームモジュールには戻されないということですね。 これも勉強になりました。ありがとうございます!

  • Wendy02
  • ベストアンサー率57% (3570/6232)
回答No.4

suffreさん、こんにちは。 変数を共通で使う場合は、Public ステートメントを使ってもよいのですが、コードをみる限りでは、変数の保持の必要性がありません。実際に、システムとして作っていくと、Public スコープの変数は、意外に取り扱いが面倒なのです。本当に、Publicレベルなのか、という使い分けには慎重にならざるを得ない時があります。今時点の正解は、Publicステートメントであっても、どこか、記憶の片隅に入れておいてください。 Private Sub CommandButton1_Click() Dim MyData1 As Long Dim MyData2 As Long  For MyData1 = 1 To 10   MyData2 = MyData1 * 2   Call test1(MyData1, MyData2)   Call test2(MyData1, MyData2)  Next MyData1 End Sub Sub test1(MyData1 As Long, MyData2 As Long)  Worksheets("Sheet1").Range("A" & MyData1).Value = MyData2 * MyData2 End Sub Sub test2(MyData1 As Long, MyData2 As Long)  Worksheets("Sheet2").Range("A" & MyData1).Value = MyData2 + MyData2 End Sub

suffre
質問者

お礼

Wendy02さんいつもお世話になっています! >実際に、システムとして作っていくと、Public スコープの変数は、意外に取り扱いが面倒なのです。 そうなのですか・・・。私にはまだわからない領域かもしれませんね。Publicの方法もあるということは勉強になりましたが引数渡しのほうがよいのですね。 たとえば変数が増えていくと下記のカッコ内にもどんどん書き込んでいくことになるのでしょうか? Sub test1(MyData1 As Long, MyData2 As Long, MyData3 As Long, MyData4 As Long, MyData5 As Long, ・・・) 現在使いたい変数は4つなのですが、変数が大量になった場合の記述はどうなのかなって思いまして・・・。

回答No.3

#1さんの解答にあるように、引数をつけて呼び出すのがよいと思います。(次のように)標準モジュールにPublic定義をする方法は、別の場所で同じようなものを使ったとき変数が変わってしまう場合があり、私も以前これでエラーを見つけるのに時間がかかりました。できるだけその場所でしか変数が使われないようにする方がミスが少なくなります。 ====== ユーザーフォームモジュール ======= 省略 Call test1(MyData1, MyData2) Call test2(MyData1, MyData2) 省略 End Sub ====== 標準モジュール ======= Sub test1(D1 As Long, D2 As Long) Worksheets("Sheet1").Range("A" & D1).Value = D2 * D2 End Sub Sub test2(D1 As Long, D2 As Long) Worksheets("Sheet2").Range("A" & D1).Value = D2 + D2 End Sub

suffre
質問者

お礼

pascal3141さんお返事ありがとうございます。 引数を使う方法とPublic宣言する方法があるのですね。 Excel大事典で引数を使う方法を探してみると・・・プロシージャの連携というやつですね。 この場合の宣言はユーザーフォームモジュール内でDim宣言でいいのですかね・・・。できなかったらPublicにすればいいかな。

回答No.2

====== ユーザーフォームモジュール ======= Private Sub CommandButton1_Click()  For MyData1 = 1 To 10   MyData2 = MyData1 * 2   Call test1   Call test2  Next MyData1 End Sub ====== 標準モジュール ======= Public MyData1 As Long  'ここにpublicで Public MyData2 As Long  'ここにpublicで Sub test1()  Worksheets("Sheet1").Range("A" & MyData1).value = MyData2 * MyData2 End Sub Sub test2()  Worksheets("Sheet2").Range("A" & MyData1).value = MyData2 + MyData2 End Sub これが簡単です。

suffre
質問者

お礼

tinu2000さん詳しい記述いただきまして感謝です! Public変数は標準モジュールに記述するのですね。 Excel辞書をくまなく読んでみました・・・。 「Privateステートメントで宣言した変数はDimステートメントを使用して宣言した場合と同じで、そのモジュール内でのみ使用できます」 そうだったんですね。私のはユーザーフォームモジュールに書いてしまいました。 またひとつ勉強になりました。

  • osamuy
  • ベストアンサー率42% (1231/2878)
回答No.1

標準モジュール側関数を引数付きにするか、フォーム側の公開したい変数を(dimでなく)publicとして定義するとか。

suffre
質問者

お礼

osamuyさん、お返事ありがとうございます! Publicを使うのですね。Excel辞書を開いてみました・・・。 「すべてのモジュールのすべてのプロシージャで使えます」 これかぁ! Excel VBAを初めてまだ1週間ほどのド素人でただいま勉強中のため目が赤くなるまで辞書読んでますが1000ページは分厚いです・・・。

関連するQ&A

  • ListBoxで表示されたデータの取得方法は

    お世話になります。 標準フォーム から以下のリストボックスを表示して、無事シートの一覧が表示されています。 Private Sub UserForm_Initialize() Dim lastRow As Long Dim myData Worksheets("Sheet1").Range("a1:C35").Value = "" With Worksheets(Sheet) myData = .Range(.Cells(1, 1), .Cells(Rows.Count, 3).End(xlUp)).Value End With With ListBox1 .ColumnCount = 3 .ColumnWidths = "20;70;100" .List = myData End With End Sub このリストボックスにはボタンが二つありボタン1を押したときに標準フォームへ[hinban] という変数にリストボックスでフォーカスしているデータを取り込みたいのですが以下の方法でうまくいきません。 Private Sub CommandButton1_Click() Dim lastRow As Long Dim i As Integer hinban = ListBox1.Column(pvargColumn:=1) End Sub もう一つのボタンは何もせずにリストボックスを閉じたいのですがこれで問題ないですか。 Private Sub CommandButton2_Click() Unload Me End Sub プログラム初心者でインターネットから寄せ集めのプログラムです。 どなたかお力をお貸しください。

  • エクセルVBAでセル範囲のデータをクリップボードに

    セル範囲のデータをテキストとしてクリップボードに取り込みたいのです。 http://okwave.jp/qa/q5650002.html#16327676 の回答ANo2を見て Sub test01() Dim myData As DataObject Dim myCb As Variant Dim x x = "TESTデータです。" Set myData = New DataObject myData.SetText x myCb = myData.GetText myData.PutInClipboard End Sub は出来ました。 そこで、セル範囲A1:B3をクリップボードに貼ろうといろいろやってみました。 一応、下記でできましたが、実際にはもっと広い範囲を取り込みたいので、もっと簡単な方法はないでしょうか? Sub Clip() Dim myStr As String Dim myData As DataObject Dim myCb As Variant Set myData = New DataObject With Sheets(1) myStr = .Range("A1").Value & ":" & .Range("B1").Value & _ vbNewLine & .Range("A2").Value & ":" & .Range("B2").Value & _ vbNewLine & .Range("A3").Value & ":" & .Range("B3").Value End With myData.SetText myStr ', 1 myCb = myData.GetText If MsgBox("データ" & vbNewLine & myCb & " をクリップボードに送りますか? ", vbYesNo + vbQuestion, "確認") = vbNo Then Exit Sub End If myData.PutInClipboard End Sub

  • excel ユーザーフォームでシートごとに転記2

    先日ユーザーフォームへの転記について質問させていただきました。 ご回答いただき、ありがとうございました。 今度はオプションボタンで選択したときに、シートごとに転記する方法を 教えていただけますでしょうか。 ユーザフォーム上で、オプションボタンを選択。 OptionButton1・・・シート1へ転記 OptionButton2・・・シート2へ転記 これをOKボタンを押したときに転記するようにしたいと思っています。 Private Sub OK_Click() Dim CLrow As Long Dim KYrow As Long CLrow = Worksheets("Sheet1").Range("A65536").End(xlUp).Row KYrow = Worksheets("Sheet2").Range("A65536").End(xlUp).Row If OptionButton1.Value = True Then Worksheets("Sheet1").Range("A" & CLrow).Value = .TextBox1.Value ElseIf OptionButton2.Value = True Then Worksheets("Sheet2").Range("A" & KYrow).Value = .TextBox1.Value End With End Sub ここまでやってみたのですが「参照が不正または不完全です」 と出てしまいます。 どなたかご教示願います。 よろしくお願いします。

  • 変数の宣言(s As String)で良い理由

    vbaについてしつもんです。 標準モジュールで Option Explicit Dim s As String Sub test1() Call test2("qqq") End Sub Sub test2(s As String) MsgBox s End Sub としたのですが、もしかしてDim s As Stringって必要ないのでしょうか? あってもなくても動きます。 (s As String)があるからDim s As Stringは不要なのですか? だとしたら、(s As String)に dim や publicをつけなくて良い理由を教えてください。

  • VB6-標準モジュールとの変数の受け渡し

    VB6の初心者です。ごく初歩的な問題でお恥ずかしいのですが困っています。 標準モジュール内にある以下のサブルーチンを行わせるために、 Sub Transform(NumSamples As Long, RealIn() As Double, ImageIn() As Double, RealOut() As Double, ImagOut() As Double, Optional InverseTransform As Boolean = False) (NumSamples、RealIn()、ImageIn()は入力値) (RealOut()、ImagOut()は戻り値) ... ... End Sub フォームモジュール内で以下のようにコールすると、 Private Sub cmdStart_Click() Dim A as Long Dim B(10000) as Double Dim C(10000) as Double Dim D(10000) as Double Dim E(10000) as Double ... (A,B,Cに数値入力) ... Call Transform(A, B(), C(), D(), E(), False) End Sub で実行すると、変数D()に対して「コンパイルエラー:型が一致しません:配列またはユーザ定義型を指定してください」が出ます。なお、配列は10000まで宣言していますが実際には0~4096を使っています。 変数型は合わせているはずなのになぜエラーになるのでしょうか。D(),E()の型宣言をPublicにして標準モジュール内に入れたり、いろいろやってみたつもりですがうまくいきません。 よろしくお願いします。

  • 標準モジュールにpublicで宣言するしかない?

    フォームモジュールと標準モジュールで同じ変数を使って値を行き来したい場合、 標準モジュールにpublicで宣言するしかないのでしょうか? 【フォームモジュール】 Private Sub cmd_コマンド0_Click() test = "aaa" Call 標準モジュールtest End Sub 【標準モジュール】 Public test As String Sub 標準モジュールtest() MsgBox test End Sub でいいのですか?

  • エクセルVBAユーザーフォームの変数の設定方法について

    すいません、エクセルVBAのユーザーフォームの変数の設定方法について質問があります。 1 ユーザーフォームを2つ用意する。 2 それぞれにComboBox1をおく。 3 立ち上げたユーザーフォームについて、UserForm_InitializeでComboBox1に"a"のAddItemを作る。 この、「立ち上げたフォームのComboBox1に"a"のAddItemを作る」 という作業を各々のユーザーフォームに記載するのではなく、標準モジュールでまとめて記載する方法で躓いています。 Public m As String Private Sub UserForm_Initialize() ’フォーム1を立ち上げた場合   m = "UserForm1"   Call Test1(m) End Sub Private Sub UserForm_Initialize() ’フォーム2を立ち上げた場合   m = "UserForm2"   Call Test1(m) End Sub ↓ 標準モジュールに記載 Sub Test1(m As String) VBA.UserForms.Add(m).ComboBox1.AddItem "a" End Sub これだとUserForm_InitializeとTest1の間で無限ループが始まってしまい、うまく進んでくれません。 ヘルプを見ましたが、Add(変数)でユーザーフォームを変数で指定できるということ以上のことは発見できず行き詰っています。  それぞれのフォームに書けばいいだけの話なのかもしれませんが、メンテを考えると出来ればまとめて記述しておきたいと考えています。 解決方法がありましたらどうぞよろしくご教示願います。

  • EXCEL コンボボックスのリスト設定

    リストインデックスが複数ある場合は動くのですが、 インデックスが0 もしくは1個しかない場合は、どのように処理を追加したらいいでしょうか。。 実行時エラー381 Lisプロパティを設定できません。プロパティの配列のインデックスが無効です、と メッセージが出ます。 いろいろ試してるのですがわかりません。 コンボボックスの値は別シートで参照先を指定しています。 ----------- Private Sub ComboBox3_DropButtonClick() Dim lRow As Long Dim i As Long, myCnt As Long Dim myData With Worksheets("部門名") lRow = .Range("O" & Rows.Count).End(xlUp).Row ’O列の最終行を確認 myData = .Range("O2:O" & lRow).Value ’コンボボックスのリストデータ End With With ComboBox3 .ColumnCount = 1 .ColumnWidths = "50" .List = myData End With End Sub

  • VBA リストボックス(複数条件)で検索⇒転記方法

    VBA初心者です。 入門書を読み、コンボボックスを用いる(一つの条件検索)で請求書ツール作成までできたのですが、画像のようにユーザーフォームに複数選択リストを設けると現在のコードですと、エラーになってしまいます。 つきましては、リストボックスで条件を複数選択可能にして、該当データを転記するといったことを行いたいです。大変恐縮ですが、コードをご教示お願い致します。 ↓参考に、現状のコードを下記致します。 (ユーザーフォームのコード) Private Sub btnExit_Click() Unload Me End Sub Private Sub UserForm_Initialize() Dim ListRange As Range Dim temp As Range Dim vYear As Long Dim i As Long With Worksheets("取引先一覧").Range("A1").CurrentRegion Set ListRange = .Resize(.Rows.Count - 1).Offset(1) End With For Each temp In ListRange cmbcompany.AddItem temp.Value Next vYear = Year(Date) cmbYear.AddItem vYear - 1 cmbYear.AddItem vYear cmbYear.AddItem vYear + 1 cmbYear.Value = vYear For i = 1 To 12 cmbMonth.AddItem i Next End Sub Private Sub btnMakeBill_Click() MakeBill cmbcompany.Text, cmbYear.Text, cmbMonth.Text End Sub (標準モジュールのコード) Option Explicit Sub Main() frmMakeBill.Show End Sub Sub MakeBill(ByVal vCompany As String, ByVal vYear As Long, ByVal vMonth As Long) Dim TargetSheet As Worksheet Dim vDate As Date Dim DataRange As Range Dim TargetRange As Range Dim BillBook As Workbook Dim i As Long, vRow As Long Dim vInfo(1 To 2) As String On Error Resume Next Worksheets("請求書Template").Copy After:=Worksheets(Worksheets.Count) If Err.Number <> 0 Then MsgBox "「請求書Template」ワークシートが見つかりません。確認下ください" Exit Sub End If On Error GoTo 0 On Error GoTo ErrHdl Set TargetSheet = Worksheets(Worksheets.Count) Set TargetRange = TargetSheet.Range("A18") i = 1 vRow = 1 With Worksheets("受注データ").Range("A9") Do Until .Cells(i, 1).Value = "" vDate = .Cells(i, 1).Value If .Cells(i, 2).Value = vCompany _ And Year(vDate) = vYear And Month(vDate) = vMonth Then TargetRange.Cells(vRow, 1).Value = .Cells(i, 1).Value '「日付」列 TargetRange.Cells(vRow, 2).Value = .Cells(i, 3).Value '「商品コード」列 TargetRange.Cells(vRow, 3).Value = .Cells(i, 4).Value '「商品名」列 TargetRange.Cells(vRow, 4).Value = .Cells(i, 5).Value '「数量」列 TargetRange.Cells(vRow, 5).Value = .Cells(i, 6).Value '「単価」列 TargetRange.Cells(vRow, 6).Value = .Cells(i, 7).Value '「金額」列 vRow = vRow + 1 End If i = i + 1 Loop TargetSheet.Range("F28").Formula = "=SUM(F18:F27)" '「小計」 TargetSheet.Range("F29").Formula = "=F28 * 0.08" '「消費税額」 TargetSheet.Range("F30").Formula = "=F28 + F29" '「合計金額」 TargetSheet.Range("B6").Formula = "F30" '請求額 vInfo(1) = Date vInfo(2) = vCompany TargetSheet.Range("F2").Value = vInfo(1) '「請求日」 TargetSheet.Range("A6").Value = vInfo(2) '「請求先」 End With Set BillBook = Workbooks.Add TargetSheet.Cells.Copy BillBook.Worksheets(1).Range("A1") Application.DisplayAlerts = False TargetSheet.Delete Application.DisplayAlerts = True Exit Sub ErrHdl: MsgBox "エラーが発生しました。処理を終了します" End Sub

  • ExcelVBAについて

    以上~以下検索についてです。 現在、1文字以上一致で検索し、listboxに検索結果を表示させることができます コードは下記 Private Sub CommandButton1_Click() Dim lastRow As Long Dim myData, myData2(), myno Dim i As Long, j As Long, cn As Long With Workbooks("Master.xlsm").Worksheets("Sheet1") myData = .Range(.Cells(3, 2), .Cells(Rows.Count, 5).End(xlUp)).Value lastRow = .Cells(Rows.Count, 2).End(xlUp).Row End With ReDim myData2(1 To lastRow, 1 To 4) For i = LBound(myData) To UBound(myData) If myData(i, 2) Like "*" & TextBox2.Value & "*" And _ myData(i, 3) Like "*" & TextBox3.Value & "*" And _ myData(i, 4) Like "*" & TextBox4.Value & "*" _ Then cn = cn + 1 myData2(cn, 1) = myData(i, 1) myData2(cn, 2) = myData(i, 2) myData2(cn, 3) = myData(i, 3) myData2(cn, 4) = myData(i, 4) End If Next i If cn = 0 Then MsgBox "検索結果は見つかりませんでした・・・" Else End If With ListBox1 .ColumnCount = 4 .ColumnWidths = "20;40;20;60" End With End Sub そして今回教えていただきたいのが userfoamで 例えば,金額が1000~100000の間のものを検索し、 それに該当するものすべてをリストボックスに表示させることです。 このコードに以上~以下検索を追加するにはどうすればいいでしょうか? 新しい方法、これよりいい方法があればお教えください。 よろしくお願いいたします。

専門家に質問してみよう