【会員アンケートご協力のお願い】抽選で計5名様に役立つ書籍をプレゼント!

Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(指定なし : 指定なし)
『SpinButton』に値が上手く入らない??
投稿日時: 24/10/15 13:02:45
投稿者: けけちゃま

いつもお世話になっております。
VBAフォームモジュールを書いております。
 
【フォームの説明】
TextBox1・・・任意の時間をいれたい「hh:mm」の形で。(初期設定では、システム時間をいれたい)
SpinButton1・・・hhをスピンボタンをつかって1時間ごとに動かしたい
 
私が作成したコードです↓
 

Private Sub SpinButton1_Change()

        TextBox1.Text = Format(SpinButton1.Value, "hh:mm")

       

End Sub

 

 

Private Sub TextBox1_AfterUpdate()

           

        If TextBox1.Text = "" Then

            Exit Sub

        End If

    SpinButton1.Value = CDate(TextBox1.Text)
       

        

            

End Sub

 

Private Sub TextBox1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

 

         If TextBox1.Text <> "" Then

            If Not IsDate(TextBox1.Text) Then

                MsgBox "時間を入力(hh:mm)してください。"

                Cancel = True

                TextBox1.Text = Format(Time, "hh:mm")

               

            End If

           

        End If

End Sub

 

 

'【初期設定】

Private Sub UserForm_Initialize()

        TextBox1.Text = Format(SpinButton1.Value, "hh:mm")

       

        SpinButton1.Min = CDate("00:00:00")

        SpinButton1.Max = CDate("23:59:59")

        SpinButton1.Value = Time

       

        TextBox1.Text = Format(SpinButton1.Value, "hh:mm")

       

        

End Sub

 
私の想定では、これを実行すると、
「Valueプロパティを設定できません」となります。
 
 
最初にTextBox1.Textには、システム時間「hh:mm」の形式で表示されるはずだったのですが、
どうも Initialize の SpinButton1.Value = Time の値が「1」なんです。
 
申し訳ございません。
SpinButton1.Min をどう設定したら起動するか、
どなたかお分かりになられましたら教えていただけますでしょうか・・。
 

投稿日時: 24/10/15 13:04:34
投稿者: けけちゃま

文章訂正です。
 
私の想定では、これを実行すると、
最初にTextBox1.Textには、システム時間「hh:mm」の形式で表示されるはずだったのですが、
「Valueプロパティを設定できません」となります。
  
  
 
どうも Initialize の SpinButton1.Value = Time の値が「1」なんです。
申し訳ございません。
SpinButton1.Min をどう設定したら起動するか、
どなたかお分かりになられましたら教えていただけますでしょうか・・。

回答
投稿日時: 24/10/15 16:17:53
投稿者: mattuwan44

スピンボタンのValueプロパティには整数しか設定できません。
そして、日付型の時間は24時間を1とした小数となります。
なので、例えば時刻を分に直して扱うとかの処理が必要になります。
  
Option Explicit
 
Private Sub UserForm_Initialize()
    Dim t As Double: t = Time
    Me.TextBox1.Text = Format(t, "hh:mm")
    With Me.SpinButton1
        .Min = 0
        .Max = 23 * 60
        .SmallChange = 60
        .Value = DateDiff("n", 0, t)
    End With
End Sub
 
Private Sub SpinButton1_Change()
    Me.TextBox1.Text = Format(TimeSerial(0, Me.SpinButton1.Value, 0), "hh:mm")
End Sub
  
こんな感じになると思います。
(MinとMaxはこれでいいか不安。。。)

投稿日時: 24/10/15 22:11:47
投稿者: けけちゃま

mattuwan44様
 
ありがとうございます。
とても考えがつかない処理方法でした。
思わず実行して嗚咽がでました。
DateDiffって本当によくできているだなぁと思いました。
 
実は、
SpinButton2・・・mmをスピンボタンをつかって1分ごとに動かしたい
 
とも思っており、応用して下記をかいてみたんですが、多分秒に直すとSpinButtonキャパオーバーなのかまた止まってしまいました。
もう、こういうときってSpindownとかのコード使うしかないとかですかね・・
重ね重ね申し訳ございませんが、この点なにかお知恵をいただくことは可能でしょうか・・
 
 
 
 


Option Explicit

Dim t As Double

 

Private Sub SpinButton1_Change()

        TextBox1.Text = Format(TimeSerial(0, SpinButton1.Value, 0), "hh:mm")

       

        

End Sub

 

 

Private Sub SpinButton2_AfterUpdate()

 

        If TextBox1.Text = "" Then

            Exit Sub

        End If

 

        SpinButton2.Value = DateDiff("s", 0, TextBox1.Text)

End Sub

 

Private Sub SpinButton2_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

            If TextBox1.Text <> "" And Not IsDate(TextBox1.Text) Then

                    MsgBox "時間を入力(hh:mm)してください。"

                    Cancel = True

                    TextBox1.Text = Format(t, "hh:mm")

            End If

 

End Sub

 

Private Sub SpinButton2_Change()

        TextBox1.Text = Format(TimeSerial(0, 0, SpinButton2.Value), "hh:mm")

 

End Sub

 

Private Sub TextBox1_AfterUpdate()

 

        If TextBox1.Text = "" Then

            Exit Sub

        End If

 

        SpinButton1.Value = DateDiff("n", 0, TextBox1.Text)

       

End Sub

 

Private Sub TextBox1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

            If TextBox1.Text <> "" And Not IsDate(TextBox1.Text) Then

                    MsgBox "時間を入力(hh:mm)してください。"

                    Cancel = True

                    TextBox1.Text = Format(t, "hh:mm")

            End If

           

End Sub

 

 

'【初期設定】

Private Sub UserForm_Initialize()

       

        t = Time

        TextBox1.Text = Format(t, "hh:mm")

       

        SpinButton1.Min = 0

        SpinButton1.Max = 23 * 60   '最大23時となり、分に換算すると23*60=1,380分(そして60ずつ増加させる)

        SpinButton1.SmallChange = 60

        SpinButton1.Value = DateDiff("n", 0, t)

       

        SpinButton2.Min = 0

        SpinButton2.Max = 59 * 60 '最大59分となり、秒に換算すると59*60=3,540秒(そして60ずつ増加させる)

        SpinButton2.SmallChange = 60

        SpinButton2.Value = DateDiff("s", 0, t)

回答
投稿日時: 24/10/16 08:16:52
投稿者: mattuwan44

難しく考えなくていいですよ。
テキストボックスに時間に読める文字列を作るだけです。
スピンボタンを2つに分けるなら、より簡単です。
 

Option Explicit

Private Sub UserForm_Initialize()
    Dim t As Double: t = Time
    
    With Me.SpinButton1
        .Min = 0
        .Max = 23
        .SmallChange = 1
        .Value = Hour(t)
    End With
    With Me.SpinButton2
        .Min = 0
        .Max = 59
        .SmallChange = 1
        .Value = Minute(t)
    End With
    
    Me.TextBox1.Text = GetText
End Sub

Private Sub SpinButton1_Change()
    Me.TextBox1.Text = GetText
End Sub

Private Sub SpinButton2_Change()
    Me.TextBox1.Text = GetText
End Sub

Private Function GetText() As String
    GetText = Me.SpinButton1.Value & ":" & Me.SpinButton2.Value
End Function

 
(余分な空白行は読むのに下に長くなって頻繁にスクロールすることになるので、
読みにくくないですか?)

回答
投稿日時: 24/10/16 08:19:29
投稿者: mattuwan44

あ、分のところ、2桁に固定した方がよみやすいですかね?
お好きなように文字列を作ってください。

回答
投稿日時: 24/10/16 14:30:58
投稿者: mattuwan44

Option Explicit

Private Sub UserForm_Initialize()
    With Me.SpinButton1
        .Min = 0
        .Max = 23
        .SmallChange = 1
    End With
    With Me.SpinButton2
        .Min = 0
        .Max = 59
        .SmallChange = 1
    End With
    
    GetTime
End Sub

Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
    Dim s As Variant
    Dim d As Date
    
    s = Trim(Me.TextBox1.Text)
    
    If IsDate(s) Then
        d = CDate(s)
        If d < 1 Then
            GetTime d
            Exit Sub
        End If
    End If
    
    If Len(s) = 0 Then
        GetTime
    Else
        With Me.TextBox1
            .SelStart = 0
            .SelLength = Len(.Text)
        End With
        Cancel = True
        
        MsgBox "時刻を入力してください。(24:00未満)"
    End If
End Sub

Private Sub SpinButton1_Change()
    Me.TextBox1.Text = GetText
End Sub

Private Sub SpinButton2_Change()
    Me.TextBox1.Text = GetText
End Sub

Private Function GetText() As String
    GetText = Me.SpinButton1.Value & ":" & Format(Me.SpinButton2.Value, "00")
End Function

Private Function GetTime(Optional ByVal t As Double = 9999)
    If t >= 1 Then t = Time
    Me.SpinButton1.Value = Hour(t)
    Me.SpinButton2.Value = Minute(t)
End Function

 
テキストボックスに手入力も考えてみました。
が、あんまりうまくないです^^;
何時間でも考えてしまうので、これくらいで^^;
 
あ、スピンボタンよりスクロールバーの方が使い勝手が良いかも知れません。
(スライドバーは使えなくなったのかなぁ。。。。)

投稿日時: 24/10/16 21:59:00
投稿者: けけちゃま

mattuwan44様
昨晩からずっとこのことで頭を動かし、朝閃いては試行錯誤繰り返し、テキスト手入力も含め書いてみたんですが・・・!!!
実行時の時間が21:55として、手入力で15:00にすると、15:55になるんです。
でも、だいぶヒント(ほぼ答え)頂いたので、引き続き考えてみます。
 
十分です!こんな他人の質問に、貴重な時間を使ってまで考えてくださりありがとうございます泣
(色々メモ入れながら作っているので改行が多くなりがちで見づらくて申し訳ございません恥)
 

Option Explicit

Dim t As Double

 

 

Private Sub TextBox1_AfterUpdate()

        If TextBox1.Text = "" Then

            Exit Sub

        End If

       

            SpinButton1.Value = Hour(TextBox1.Text)

            SpinButton2.Value = Minute(TextBox1.Text)

           

End Sub

 

Private Sub TextBox1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

            If TextBox1.Text <> "" And Not IsDate(TextBox1.Text) Then

                    MsgBox "時間を入力(hh:mm)してください。"

                    Cancel = True

                    TextBox1.Text = Format(t, "hh:mm")

            End If

           

End Sub

 

Private Sub UserForm_Initialize()

     t = Time

   

    With Me.SpinButton1

        .Min = 0

        .Max = 23

        .SmallChange = 1

        .Value = Hour(t)

 

       

    End With

    With Me.SpinButton2

        .Min = 0

        .Max = 59

        .SmallChange = 1

        .Value = Minute(t)

    End With

   

    Me.TextBox1.Text = GetText

End Sub

 

Private Sub SpinButton1_Change()

    Me.TextBox1.Text = Format(CDate(GetText), "hh:mm")

   

End Sub

 

Private Sub SpinButton2_Change()

    Me.TextBox1.Text = Format(CDate(GetText), "hh:mm")

End Sub

 

Private Function GetText() As String

    GetText = Me.SpinButton1.Value & ":" & Me.SpinButton2.Value

End Function

回答
投稿日時: 24/10/17 08:00:50
投稿者: mattuwan44

            SpinButton1.Value = Hour(TextBox1.Text)

            SpinButton2.Value = Minute(TextBox1.Text)

 
↑ちらっと見ただけですが、この辺とか、変数に受けておいてたほうがよいかもです。
テキストボックスを順次書き換えて行ったりするので、
 
いろいろいじってて、
あえて手入力を用意する必要もないかと思いました。
スピンボタン長押しで変更で十分かなぁと思ったりしました。

回答
投稿日時: 24/10/17 20:41:46
投稿者: mattuwan44

Option Explicit

Private Sub UserForm_Initialize()
    With Me.SpinButton1
        .Min = 0
        .Max = 23
        .SmallChange = 1
    End With
    With Me.SpinButton2
        .Min = 0
        .Max = 59
        .SmallChange = 1
    End With
    
    Dim d As Date: d = Time
    Me.TextBox1.Text = Format(d, "hh:nn")
    SetSpinValue d
End Sub

Private Sub TextBox1_Enter()
    With Me.TextBox1
        .IMEMode = fmIMEModeDisable
    End With
End Sub

Private Sub TextBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
    If Chr(KeyAscii) Like "[!0-9:]" Then
        KeyAscii = 0
    End If
End Sub

Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
    Dim d As Variant
    
    With Me.TextBox1
        d = .Text
        If IsDate(d) Then
            d = CDate(d)
            If d < 1 Then
                .Text = Format(d, "hh:nn")
                SetSpinValue d
                Exit Sub
            End If
        End If
        
        .SelStart = 0
        .SelLength = Len(.Text)
    End With
    
    Beep
    Cancel = True
End Sub

Private Sub SetSpinValue(ByVal d As Date)
    Application.EnableEvents = False
    Me.SpinButton1.Value = Hour(d)
    Me.SpinButton2.Value = Minute(d)
    Application.EnableEvents = True
End Sub

Private Sub SpinButton1_Change()
    SetSpin2Text
End Sub

Private Sub SpinButton2_Change()
    SetSpin2Text
End Sub

Private Sub SetSpin2Text()
    Me.TextBox1.Text = Format(Me.SpinButton1.Value, "00") _
                        & ":" & Format(Me.SpinButton2.Value, "00")
End Sub

 
時間が出来ると気になって考えてしまいますね^^;
こんな感じでいかがでしょう?
やっぱテキストボックスに手入力だとどうしてもミスタイプを制御しきれないので、
スピンボタンのみで入力がよいように思います。
あと、メッセージボックスを表示すると、
時刻でない文字を入力しかけて×ボタンで閉じようとしたときに、
メッセージが表示されて美しくないです。
なにか方法があるかも知れませんが、
本当にメッセージが必要かどうか疑問なので、
なくてもよいかと思いました。
 
>実行時の時間が21:55として、手入力で15:00にすると、15:55になるんです
 
少しデバッグしてみましたが、やはり、
            SpinButton1.Value = Hour(TextBox1.Text)
            SpinButton2.Value = Minute(TextBox1.Text)

ここがまずいです。
 
dim t as date
t = time
SpinButton1.Value = Hour(t)
SpinButton2.Value = Minute(t)

というような形で、変数を用いないと変なことになります。
 

投稿日時: 24/10/17 21:28:59
投稿者: けけちゃま

 mattuwan441様
 
ぎゃー!ありがとうございます。
19:30頃のコメントみて(20:41のコメントない状態)、キッチン立ちながらあーでもないこーでもない考えており、
一つの自分なりの結論ででかかったのが・・・
 
@テキストボックス1に時間をいれたい。
Aスピンボタン1でhhを変動させたい。
Bスピンボタン2でmmを変動させたい。
 
という私の願望は…
 
【私的結論1】@Aもしくは@Bであれば  mattuwan44様が1回目にいただいた方法で可能。
<コード↓>
 

Option Explicit
Dim t As Double

Private Sub SpinButton2_Change()      
        TextBox1.Text = Format(TimeSerial(0, SpinButton2.Value, 0), "hh:mm")
End Sub

 

Private Sub TextBox1_AfterUpdate()
        If TextBox1.Text = "" Then
            Exit Sub
        End If

        SpinButton2.Value = DateDiff("n", 0, TextBox1.Text)      
End Sub

Private Sub TextBox1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
            If TextBox1.Text <> "" And Not IsDate(TextBox1.Text) Then
                    MsgBox "時間を入力(hh:mm)してください。"
                    Cancel = True
                    TextBox1.Text = Format(t, "hh:mm")
            End If
End Sub

 
'【初期設定】
Private Sub UserForm_Initialize()      
        t = Time
        TextBox1.Text = Format(t, "hh:mm")
        SpinButton2.Min = 0
        SpinButton2.Max = 23 * 60   '最大23:59となり、分に換算すると23*60=1,380分(そして1ずつ増加させる)
        SpinButton2.SmallChange = 1
        Debug.Print t
        Debug.Print DateDiff("n", 0, t)        
        SpinButton2.Value = DateDiff("n", 0, t)       
End Sub

 
 
 
 
 
【私的結論2】ABであれば mattuwan44様が2回目にいただいた方法で可能。
 
【私的結論3】@ABとなると、ほぼ不可能(多分、できなくはないが、結論1や結論2に比べるとかなりコードも長くなり複雑化してしまう。
 
ということでした。
 
そして20:41のmattuwan44様の回答みて、やはり私の脳みそのキャパを超えた複雑なコードでした爆。(Change Initialize AfterUpdate BeforeUpdate 以外はまだほど未知の領域)。
 
でも、この回答については後程また、じっくり理解に努めますので、本当にありがとうございます…泣
 
また、この3つ結論はあながち間違っていないとわかっただけでも、とても大きな収穫でした!
一旦、これで解決にさせていただきたいと思います。
 
 
そのまえになのですが、あと1個だけ聞いてもいいですか。
質問とはそれてしまうのですが、
mattuwan44様のコードに「Me.」ってあると思うのですが、フォームモジュールの時って、特にMe.がなくても、バグを起こす(別のフォームやエクセルからデータを間違ってとることはない)ことはないと思うので、書かなくてもいいと勝手に思っているのですが、あえて書かれている理由があれば教えていただけますか?!(私の勉強不足・知識不足かもしれませんがTT)

回答
投稿日時: 24/10/18 07:39:31
投稿者: mattuwan44

>特にMe.がなくても、バグを起こす(別のフォームやエクセルからデータを間違ってとることはない)ことはないと思うので、書かなくてもいい
 
特になくてもいいですよー。
書き慣れたら、ない方が気持ち悪いだけです。
 
英語が苦手なんで、
「Me.」まで入れたら入力候補が出て、
単語をなんとなくで覚えてたら、選択できるので、
スペルを思い出せなくて悩んだり調べたりの時間が少なくて済むから、
出来るだけ入力候補がでるようにしてるかなぁ。。。
 

回答
投稿日時: 24/10/18 19:53:01
投稿者: mattuwan44

ああああ。。。
 
テキストボックスも、
時と分と分けたらいいかも。。。
そしたら、手入力でもテンキーだけで完結できるし、スピンボタンと
1対1でリンクさせることが可能になりますね^^

回答
投稿日時: 24/10/19 08:58:47
投稿者: mattuwan44

時間があるようなので、、、
 
ユーザーフォームのデザイン>>
https://drive.google.com/file/d/1xygHbSCczDCjvoHZdA5SvsaPi8rMz6Vp/view?usp=sharing
 
テキストボックス2つとラベルを使っても、
テキストボックス1つのように見せることは可能です。
 
 

Option Explicit

Private Sub UserForm_Initialize()
    Dim t As Date: t = Time
    '※各オブジェクトのプロパティは事前に設定済とする。
    Me.SpinButton1.Value = Hour(t)
    Me.SpinButton2.Value = Minute(t)
End Sub

Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
    Dim s As String: s = Me.TextBox1.Text
    Dim e As Long
    
    'とりあえずスピンボタンに値を設定してみる
    On Error Resume Next
    Application.EnableEvents = False
    Me.SpinButton1.Value = CLng(s)
    Application.EnableEvents = True
    e = Err.Number
    On Error GoTo 0
    
    'エラーならキャンセルをして再入力を促す
    If e <> 0 Then
        With Me.TextBox1
            .SelStart = 0
            .SelLength = Len(s)
        End With
        Cancel = True
    End If
End Sub

Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean)
    Dim s As String: s = Me.TextBox2.Text
    Dim e As Long
    
    On Error Resume Next
    Application.EnableEvents = False
    Me.SpinButton2.Value = CLng(s)
    Application.EnableEvents = True
    e = Err.Number
    On Error GoTo 0
    
    If e <> 0 Then
        With Me.TextBox2
            .SelStart = 0
            .SelLength = Len(s)
        End With
        Cancel = True
    End If
End Sub

Private Sub SpinButton1_Change()
    Me.TextBox1.Text = Format(Me.SpinButton1.Value, "00")
End Sub
Private Sub SpinButton2_Change()
    Me.TextBox2.Text = Format(Me.SpinButton2.Value, "00")
End Sub

 
各コントロールのプロパティは事前にプロパティボックスで
設定してデザインを含め作り込むとよいかと思います。
 
TextBox2のプロパティの設定の例>
BackStyle:0-fmBackStyeleTransparrent ’背景を透明に
Font:サイズ 12  ’老眼にも優しく大きめに
IMEMode:3-fmIMEModeDisable  ’入力モードを半角英数に
Left:42   ’表示位置を微調整
MaxLength:2  '入力文字数制限
specialEffect:0-fmspecialEffectFlat ’見た目をフラットに
Text:00  '初期の文字列(見た目を確認するため入力)
Top:43   ’表示位置を微調整
 
分からない単語は、ヘルプや、Webサイトを調べてみてください。
わたしも丸覚えしているわけではなく、
ヘルプとか見ながら思い出しながら、そして、試しながら、
書いています。
 
同じようなコードが2回づつ出てくるので、
まとめるなら、クラスモジュールの勉強が必要になりますが、
使う人には関係ない話ですので、動作に不満がなければ、
これくらいの感じでもいいかと思います。
のちのちのメンテナンス性を気にするなら、
クラスモジュールの勉強をしてみてください。

投稿日時: 24/10/21 21:48:53
投稿者: けけちゃま

mattuwan44様
 
お返事が遅くなり申し訳ありません><。
(携帯で回答はみていたのですがパソコンが開けない日々が続きました)。
 
 
そしてめちゃくちゃありがとうございます。
 
本当は、時間と分をもともと別にわけていたんです。
一個にしたいという願望があった、今回の質問に繋がったという流れでもあったのですが、テキストボックス2つを合体とは、またたまげた発想に脱帽です。
 
プロパティ例も丁寧に教えていただきありがとうございます。
ただ、私の理解力がまだ完全においついていないので(←まだ前回に押してもらったコードを解読中)、持ち帰ってみます!
 
Meの件もありがとうございます。
そういう使い方もあるのですね!
 
クラスモジュール・・・実はずっと気になっていて(なにこのモジュールって)、調べていたのですがちょっとよくわからなかったです。
 
mattuwan44様のコードにファンクションプロシージャがあったのに感化され、クラスモジュールにはいるまえにファンクションプロシージャの完全取得を目指そうと新たな課題も増えたのでした。
 
このたびは本当にありがとうございます。
解決済みにするともうやりとりがないのが名残惜しいですが、私も精進いたします!
 
(と、いってまた絶対この掲示板に現れると思いますが)