Excel VBA質問箱 IV

当質問箱は、有志のボランティア精神のおかげで成り立っています。
問題が解決したら、必ずお礼をしましょうね。
本サイトの基本方針をまとめました。こちら をご一読ください。

投稿種別の選択が必要です。ご注意ください。
迷惑投稿防止のため、URLの入力を制限しています。ご了承ください。


9092 / 13644 ツリー ←次へ | 前へ→

【29331】TextBoxの入力値制限 年寄り 05/10/1(土) 9:58 質問[未読]
【29332】Re:TextBoxの入力値制限 ichinose 05/10/1(土) 10:52 発言[未読]
【29333】Re:TextBoxの入力値制限 年寄り 05/10/1(土) 11:13 発言[未読]
【29339】Re:TextBoxの入力値制限 ichinose 05/10/1(土) 19:39 発言[未読]
【29340】Re:TextBoxの入力値制限 ichinose 05/10/1(土) 20:34 発言[未読]
【29392】Re:TextBoxの入力値制限 年寄り 05/10/4(火) 12:04 お礼[未読]

【29331】TextBoxの入力値制限
質問  年寄り E-MAILWEB  - 05/10/1(土) 9:58 -

引用なし
パスワード
   TextBox1の入力値を日付に限定したい。日付は以下の3通りを許可したい。他の入力がされたらエラーメッセージを出す。
04/10/1
2004/10/1
平成16年10月1日

Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)

End Sub

にどのように記述したらよいでしょうか?どこか参考になるところを教えてもらえたらうれしい。

【29332】Re:TextBoxの入力値制限
発言  ichinose  - 05/10/1(土) 10:52 -

引用なし
パスワード
   ▼年寄り さん:
おはようございます。

>TextBox1の入力値を日付に限定したい。日付は以下の3通りを許可したい。他の入力がされたらエラーメッセージを出す。
>04/10/1
>2004/10/1
>平成16年10月1日
厳密にやれば出来そうですけど・・・、面倒なので、
日付に変換できるか否かを調べて出来るデータのみを
ある形式で表示するに仕様を変更されたら如何ですか?
例は、日付として認識できるデータは、YYYY/M/D形式で表示する例です。

Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
  With TextBox1
   If IsDate(.Text) Then
     .Text = Format(.Text, "yyyy/m/d")
   Else
     MsgBox "no good"
     .SelStart = 0
     .SelLength = Len(.Text)
     Cancel = True
     End If
   End With
End Sub

とっかかりとして、参考にして下さい。

それと・・・、
Exitイベントは、便利にみえますが、仕様によっては、問題も多々あります。
色々試してから、お使いください。

【29333】Re:TextBoxの入力値制限
発言  年寄り E-MAILWEB  - 05/10/1(土) 11:13 -

引用なし
パスワード
   ▼ichinose さん:
ありがとうございます。

>日付に変換できるか否かを調べて出来るデータのみを
確かにそうですね。先に入力された値が日付に変換されるかどうかを調べればよいのですね。そうすればこのように一つの形式だけチェックすればよい。

>Exitイベントは、便利にみえますが、仕様によっては、問題も多々あります。
この辺のことをもう少し具体的に教えていただけたら。またそれに対する対応策を。

【29339】Re:TextBoxの入力値制限
発言  ichinose  - 05/10/1(土) 19:39 -

引用なし
パスワード
   ▼年寄り さん:
こんばんは。
>>Exitイベントは、便利にみえますが、仕様によっては、問題も多々あります。
>この辺のことをもう少し具体的に教えていただけたら。またそれに対する対応策を。
これについて言い出すと大変なことになってしまうのですが・・・。

Exitイベントを取り上げて申し上げましたが、
Excelのユーザーフォームで一般のアプリのような画面操作を
行おうと思うとサポートされているイベントでは
「帯に短し、襷に長し」だなあ というのが私の正直な感想です。

アルゴリズムを駆使して(複雑な事をして)やりたいことを行うか、

サポートされている機能で可能な仕様にするか・・・。

それはともかく

>Exitイベントは、便利にみえますが、仕様によっては、問題も多々あります

の事情として私が思ったことは、

 1いくつかのバグがあること。
  角田さんサイトから、
  http://www.h3.dion.ne.jp/~sakatsu/Excel_Tips03.htm
  こんなの始めとして、角田さんのサイトに他にも「油断のならないシリーズ?」
  がありますから、これも参考にして下さい。
  他にも細かい???はありますよ!!

 2本家VBとの格差なのかコントロール配列がないこと
  似たようなことは出来ますけど、完全ではないこと。
  これも角田さんの
  http://www.vbalab.net/vbaqa/c-board.cgi?cmd=one;no=51;id=FAQ
  これを参考にして下さい。

 3Exitイベントでは物足りない・・・
  VBにはExitイベントではなく、Lostfocusイベントというのが
  あるのですが、Exitイベントはこれほど使いやすくないこと。

バグの長期放置は論外です(MS社の怠慢に他なりません)から、置いといて、

2について

例えば、Userform1に

Textbox1〜Textbox6までの6個のテキストボックスを配置して下さい。
この6個のテキストボックスで全て同じように日付が入力されたか否かの
チェックを行う事を考えます。
Userform1のモジュールに
'==================================================================
Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
  With TextBox1
   If IsDate(.Text) Then
     .Text = Format(.Text, "yyyy/m/d")
   Else
     MsgBox "no good"
     .SelStart = 0
     .SelLength = Len(.Text)
     Cancel = True
     End If
   End With
End Sub
'==================================================================
Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean)
  With TextBox2
   If IsDate(.Text) Then
     .Text = Format(.Text, "yyyy/m/d")
   Else
     MsgBox "no good"
     .SelStart = 0
     .SelLength = Len(.Text)
     Cancel = True
     End If
   End With
End Sub






とTextbox1〜Textbox6まで似たようなExitイベントプロシジャーを記述しなければ
なりません。
6個ならまだ我慢できますか?でもこれが10とか20だったら???


これを以下のようにする事が出来ます。
クラスモジュール(Class1)に(VBEにて「挿入」---「クラスモジュール」で作成)

'====================================================
Public WithEvents txt As MSForms.TextBox
'====================================================================
Private Sub txt_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
  If KeyCode = 13 Or KeyCode = 9 Then
   With txt
     If IsDate(.Text) Then
      .Text = Format(.Text, "yyyy/m/d")
     Else
      MsgBox "no good"
      .SelStart = 0
      .SelLength = Len(.Text)
      KeyCode = 0
      End If
     End With
   End If
End Sub

'Userform1のモジュールに
'=====================================
Private txt_cls(1 To 6) As New Class1
Private Sub UserForm_Initialize()
  For g0 = 1 To 6
   With txt_cls(g0)
     Set .txt = Controls("textbox" & g0)
     End With
   Next g0
End Sub


クラスは初めてだとちょっと難しいですが、とりあえず
上記の通り操作してください。

こうすると、日付チェックのコードは一回で済みますし、
イベントプロシジャーもいくつも記述する必要がありません。

しかし、ご覧の通り、使用しているのはKeyDownイベントです。
このような記述方法がExitイベントでは出来ません。

これが不満のひとつです。

【29340】Re:TextBoxの入力値制限
発言  ichinose  - 05/10/1(土) 20:34 -

引用なし
パスワード
   > 3Exitイベントでは物足りない・・・
>  VBにはExitイベントではなく、Lostfocusイベントというのが
>  あるのですが、Exitイベントはこれほど使いやすくないこと。
3について例題を作成したのですが、
仕様が悪かったのかExitイベントの悪い箇所が目立たない・・・。
後述しますが、「そんなにたいした事では無い」なんて思われるかもしれませんが、
でも私は厄介だと思っています。

では、例題の仕様から・・・。

ユーザーフォームで足し算の問題を出題し、ユーザーに回答してもらうプログラムを
考えます。

Userform1に

ラベルコントロールを8個配置して下さい。
  名前は、自動作成される Label1〜Label8
  問題を表示するために使います

テキストボックスを2個配置して下さい。
  名前は自動作成される Textbox1とTextbox2
  回答入力用に使います。

コマンドボタンを2個配置して下さい。
  名前は自動作成される Commandbutton1とCommandbutton2
  Commandbutton1----次の問題を表示
  Commandbutton2----プログラムの終了 


ではコードです。

標準モジュールに
'================================================================
Sub main()
  UserForm1.Show
End Sub


Userform1のモジュールに
'=================================================================
Private seikai(1 To 2) As Boolean
'=================================================================
Private Sub UserForm_Initialize()
  Dim lbl_lt As Variant
  Dim lbl_ht As Variant
  Dim lbl_wd As Variant
  Dim btn_lt As Variant
  Dim btn_cap As Variant
  Dim txt_ht As Variant
  lbl_lt = Array(36, 90, 126, 180, 36, 90, 126, 180)
  lbl_ht = Array(54, 54, 54, 54, 90, 90, 90, 90)
  lbl_wd = Array(54, 36, 54, 36, 54, 36, 54, 36)
  txt_ht = Array(54, 90)
  btn_lt = Array(48, 174)
  btn_cap = Array("次の問題", "終了")
  With Me
   .Caption = "足し算問題"
   .Height = 263
   .Width = 350
   End With
  For g0 = 1 To 8
   With Controls("label" & g0)
    .Caption = ""
    .BackColor = &HFFFFFF
    .SpecialEffect = fmSpecialEffectSunken
    .Font.Size = 16
    .TextAlign = fmTextAlignRight
    .Left = lbl_lt(g0 - 1)
    .Top = lbl_ht(g0 - 1)
    .Height = 20.5
    .Width = lbl_wd(g0 - 1)
    End With
   Next g0
  For g0 = 1 To 2
   With Controls("textbox" & g0)
    .Text = ""
    .Font.Size = 16
    .TextAlign = fmTextAlignRight
    .Left = 216
    .Top = txt_ht(g0 - 1)
    .Height = 20.5
    .Width = 66
    End With
   Next g0
  For g0 = 1 To 2
   With Controls("commandbutton" & g0)
    .TabStop = False
    .Caption = btn_cap(g0 - 1)
    .Font.Size = 16
    .Left = btn_lt(g0 - 1)
    .Top = 180
    .Height = 30
    .Width = 90
    End With
   Next g0
  Call disp_ques
End Sub
'=================================================================
Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
  With TextBox1
   If (Not IsNumeric(.Text)) And .Text <> "" Then
     MsgBox "答えは数字です"
     .SelStart = 0
     .SelLength = Len(.Text)
     Cancel = True
   ElseIf .Text <> "" Then
     If Val(Label1.Caption) + Val(Label3.Caption) = Val(.Text) Then
      seikai(1) = True
     Else
      MsgBox "不正解"
      .SelStart = 0
      .SelLength = Len(.Text)
      Cancel = True
      End If
     End If
   End With
End Sub
'=================================================================
Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean)
  With TextBox2
   If (Not IsNumeric(.Text)) And .Text <> "" Then
     MsgBox "答えは数字です"
     .SelStart = 0
     .SelLength = Len(.Text)
     Cancel = True
   ElseIf .Text <> "" Then
     If Val(Label5.Caption) + Val(Label7.Caption) = Val(.Text) Then
      seikai(2) = True
     Else
      MsgBox "不正解"
      .SelStart = 0
      .SelLength = Len(.Text)
      Cancel = True
      End If
     End If
   End With
End Sub
'=================================================================
Private Sub CommandButton1_Click()
  For g0 = 1 To 2
   If seikai(g0) = False Then
    MsgBox "正解するまで次の問題にはいけません"
    With Controls("textbox" & g0)
      .SelStart = 0
      .SelLength = Len(.Text)
      .SetFocus
      End With
    Exit Sub
    End If
   Next g0
  Call disp_ques
End Sub
'=================================================================
Private Sub CommandButton2_Click()
  Unload Me
End Sub
'=================================================================
Private Sub disp_ques() '問題表示
  For g0 = 1 To 7 Step 2
   Controls("label" & g0).Caption = _
     Int(Rnd() * 10000) + 1
   Next g0
  For g0 = 2 To 8 Step 4
   With Controls("label" & g0)
     .TextAlign = fmTextAlignCenter
     .Caption = "+"
     End With
   With Controls("label" & g0 + 2)
     .TextAlign = fmTextAlignCenter
     .Caption = "="
     End With
   Next g0
  For g0 = 1 To 2
   seikai(g0) = False
   Controls("textbox" & g0).Text = ""
   Next g0
End Sub


ちょっと例題に懲りすぎてしまったかな??
因みにこのプログラム正解の記述がないと「次の問題」ボタンを押しても
エラーになります。

ここで問題の個所は、
  一度に出題される問題数は2問ですが、たとえば

 8715+563= という問題に対して 不正解である50と入力した後、

プログラムの終了ボタンであるCommandbutton2をクリックしたとき
終了できない事なのです。
(これだけの事のために例題が長すぎました)

一般のアプリでは終了するんですよ、こういう場合・・・。

Exitイベントでは、フォーカスが次に移るコントロールが何なのか
Exitイベント内ではわからないという問題があります。

よって、私は、Keydownイベントを使用することが多いのですが、
これはこれでコードの記述が面倒なところもあります。

結論の割に記述が長すぎました。反省。

よって、Excelのユーザーフォームで一般のアプリのような画面操作を
行おうと思うとサポートされているイベントでは
「帯に短し、襷に長し」だなあ というのが私の正直な感想です。

【29392】Re:TextBoxの入力値制限
お礼  年寄り E-MAILWEB  - 05/10/4(火) 12:04 -

引用なし
パスワード
   ▼ichinose さん:
お礼が遅くなりました。何しろ検証することが余りにも多かったものですから。特に目安箱に釘付けとなりました。皆さんの献身的な精神で、たくさんの有益な情報が含まれていることに感銘しました。

>Excelのユーザーフォームで一般のアプリのような画面操作を
>行おうと思うとサポートされているイベントでは
>「帯に短し、襷に長し」だなあ というのが私の正直な感想です。
この辺の事情十分感じ取れました。

>とTextbox1〜Textbox6まで似たようなExitイベントプロシジャーを記述しなければ
>なりません。
>6個ならまだ我慢できますか?でもこれが10とか20だったら???
>
>
>これを以下のようにする事が出来ます。
>クラスモジュール(Class1)に(VBEにて「挿入」---「クラスモジュール」で作成)
>しかし、ご覧の通り、使用しているのはKeyDownイベントです。
>このような記述方法がExitイベントでは出来ません。
実はこれと同じことが生じており、次なる質問にこれを取り上げようと考えていた次第です。おかげさまでこちらもすっきりと解決しました。

最後に今回の私のプログラミングにおいて、項目を変えてもう一つだけ質問を許してください。

9092 / 13644 ツリー ←次へ | 前へ→
ページ:  ┃  記事番号:
2610219
(SS)C-BOARD v3.8 is Free