Access (VBA)

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

 
(Windows 11 Home : Access 2019)
テキストボックス内で改行する
投稿日時: 24/06/12 12:02:40
投稿者: mmiwa

教えて下さい
フォームAAA上にあるテキストボックスに文字列が記載されています
テキストボックス内のカーソルのある場所に変数bufに格納された文字列を挿入した後
文字列の最後へカーソルを移動した上で改行したいのですが
改行の方法が分かりません
vbCrLfなどを記載してみましたが期待した結果になりません
どのようにコード記載したら良いのでしょうか
よろしくお願いいたします
 
With [Forms]![AAA]![テキスト1]
     
    .Text = Left(.Text, .SelStart) & buf & Mid(.Text, .SelStart + 1)
     
    .SelStart = Len(.value) & vbCrLf
     
End With

回答
投稿日時: 24/06/12 12:49:52
投稿者: Suzu

SelStart は テキストボックス内のテキストの挿入位置 を示します。
 
TextBox.SelStart プロパティ (Access)
https://learn.microsoft.com/ja-jp/office/vba/api/access.textbox.selstart
 
そのプロパティーに

引用:
.SelStart = Len(.value) & vbCrLf

としています
 
vbCrLf は Text の方に入れるべき値ではありませんか?

回答
投稿日時: 24/06/12 13:00:36
投稿者: abec

selstartは文字の位置を数値で入れるので、そこでvbcrlfを入れてはいけません。
.textでvbcrlfを追加し、あとselstartの前に SetFocus でフォーカスを移動して置く必要があります。

回答
投稿日時: 24/06/12 13:41:00
投稿者: sk

引用:
フォームAAA上にあるテキストボックスに文字列が記載されています
テキストボックス内のカーソルのある場所に変数bufに格納された文字列を挿入した後
文字列の最後へカーソルを移動した上で改行したい

その処理をどのコントロールのどのイベントで
実行しようとされているのでしょうか。

回答
投稿日時: 24/06/12 22:54:19
投稿者: hatena
投稿者のウェブサイトに移動

mmiwa さんの引用:

テキストボックス内のカーソルのある場所に変数bufに格納された文字列を挿入した後
文字列の最後へカーソルを移動した上で改行したいのですが
改行の方法が分かりません

 
カーソルのある場所文字列を挿入するなら、SelTextに代入すればいいでしょう。
 
文字列の最後へカーソルを移動した上で改行は、カーソルを最後に移動させた後、SelTextにvbCrLfを代入すればいいでしょう。
 
Me!テキスト1   
    .SelText = buf 'カーソル位置にBufを挿入     
    .SelStart = Len(.Text) 'カーソルを最終位置に移動
    .SelText = vbCrLf     'カーソル位置に改行を代入
End With

 
と、ここまで回答を書いて、サンプルを作成して動作確認したら、
 
    .SelText = vbCrLf     'カーソル位置に改行を代入

がうまく動作しない。改行が挿入されずにカーソルが先頭に移動してしまう。
Accessのテキストボックスは改行コード(vbCrLf)の扱いが特殊なようです。
 
仕方がないので、SendKeys で改行してみました。
With Me!テキスト1     
    .SelText = buf 'カーソル位置にBufを挿入
    .SelStart = Len(.Text) 'カーソルを最終位置に移動
    SendKeys "^{ENTER}"
End With

 
ちなみに、.SelText や .SelStart はフォーカスがないと取得できないので、
コマンドボタンのクリック時なとではコマンドボタンにフォーカス移動してしまうので、実行することはできません。
サンプルではラベルのクリック時で実行しました。ラベルはフォーカス移動しないので、動作させることができます。
 

投稿日時: 24/06/17 09:03:15
投稿者: mmiwa

Suzuさん、abecさん、skさん、hatenaさん
返信が遅くなり大変申し訳ありません お詫び申し上げます
アドバイスありがとうございました
蒼々たる方々にアドバイスいただき恐縮です
 

引用:

vbCrLf は Text の方に入れるべき値ではありませんか?

  
確かにそうですね
.Text = Left(.Text, .SelStart) & buf & Mid(.Text, .SelStart + 1) & vbCrLf
としてみましたが、結果は同じでした
  
引用:

selstartの前に SetFocus でフォーカスを移動して置く必要があります

  
ありがとうございます
SetFocus加えましたが、結果は同じでした
  
引用:

どのコントロールのどのイベントで

  
リストボックスをダブルクリックすることで選択した項目をbufへ格納
bufをテキストボックスのカーソルの位置へ転記します
続いてカーソルをテキストボックス内の文字列の最後へ移動させ
改行を入れたいのです
  
引用:

SendKeys "^{ENTER}"

  
ありがとうございます
これまでSendKeysに良い印象がなかったので
選択肢としていませんでした
残念ながら、試してみましたが、結果は同じでした

回答
投稿日時: 24/06/17 10:50:54
投稿者: sk

引用:
リストボックスをダブルクリックすることで選択した項目をbufへ格納
bufをテキストボックスのカーソルの位置へ転記します

次のどちらの操作を想定されているのかが不明瞭です。
 
・リストボックスをダブルクリックしてから
 テキストボックスの任意の位置にフォーカスを移動させようとしている。
 
・テキストボックスの任意の位置にフォーカスがある状態から
 リストボックスをダブルクリックしようとしている。
 
仮に前者である場合、テキストボックスへのフォーカスの移動は
マウスクリックだけでなく Tab や Enter などのキークリックによって
行なわれる可能性があります。
その際にカーソルがどこに移動するかは、クライアントの設定における
[フィールドの移動時の動作]オプションに左右され、場合によっては
テキストボックスの値全体が範囲選択された状態となり得ますが、
もしそうなったら値全体を buf の値で上書きしてしまうのでしょうか。
 
また後者である場合、例えばそのフォームが開かれてから
一度もテキストボックスにフォーカスに移ったことがない状況で
リストボックスがダブルクリックされることがあり得るのであれば、
その際の挙動はどのようになさるつもりなのでしょうか。

投稿日時: 24/06/17 22:50:35
投稿者: mmiwa

skさん
ありがとうございます
 

引用:

・リストボックスをダブルクリックしてから
 テキストボックスの任意の位置にフォーカスを移動させようとしている。
  
・テキストボックスの任意の位置にフォーカスがある状態から
 リストボックスをダブルクリックしようとしている。

後者です
 
引用:

また後者である場合、例えばそのフォームが開かれてから
一度もテキストボックスにフォーカスに移ったことがない状況で
リストボックスがダブルクリックされることがあり得るのであれば、
その際の挙動はどのようになさるつもりなのでしょうか。

そのフォームが開かれたときに
一旦テキストボックスへフォーカスを移動しています

回答
投稿日時: 24/06/18 03:30:14
投稿者: hatena
投稿者のウェブサイトに移動

mmiwa さんの引用:

引用:

SendKeys "^{ENTER}"

  
ありがとうございます
これまでSendKeysに良い印象がなかったので
選択肢としていませんでした
残念ながら、試してみましたが、結果は同じでした

 
私の回答で既にふれてますが、SelStartはフォーカスがないテキストボックスではエラーになり使えません。
ラベルのクリック時で実行するように回答してますが、ラベルのクリック時で試してみてダメだったのですか。
 
リストボックスのダプルクリック時で実行したいのなら下記のような問題が発生します。
 
リストボックスをダブルクリックすると、フォーカスがリストボックスに移動します。
その時点でSelStartはリセットされます。
SetFocusでフォーカスをテキストボックスに戻してもSelStartは0(クライアントの設定によっては最後の位置)になります。
 
テキストボックスのフォーカス移動時にSelStartとSelLengthを格納しておいて、SetFocus後に移動前の状態に戻すという対処法が必要になります。
ということで、コード化してみました。
 
Option Compare Database
Option Explicit
Dim preSelStart As Long
Dim preSelLength As Long

Private Sub テキスト1_Change()
    With Me.テキスト1
        Debug.Print "Change: ", .SelStart, .SelLength
    End With
End Sub

Private Sub テキスト1_Exit(Cancel As Integer)
    With Me.テキスト1
        preSelStart = .SelStart
        preSelLength = .SelLength
    End With
End Sub

Private Sub リスト2_DblClick(Cancel As Integer)
    Dim buf As String
    buf = Me.リスト2.Value
    With Me.テキスト1
        .SetFocus
        .SelStart = preSelStart
        .SelLength = preSelLength
        .SelText = buf
        .SelStart = Len(.Text)
        SendKeys "^{ENTER}"
    End With
End Sub

 
これでサンプルで実験してい見るとうまく行く時とうまく行かないときがあります。
 
Accessのコントロールは裏でいろいろやっているようで複雑です。
いろいろ試してみましたが、もう力尽きたので寝ます。

回答
投稿日時: 24/06/18 11:58:47
投稿者: sk

引用:
・テキストボックスの任意の位置にフォーカスがある状態から
 リストボックスをダブルクリックしようとしている。

引用:
そのフォームが開かれたときに
一旦テキストボックスへフォーカスを移動しています

(フォームモジュール)
-------------------------------------------------------------
Option Compare Database
Option Explicit
 
Private varSelStart As Variant
Private varSelLength As Variant
 
Private Sub Form_Load()
     
    Dim strControlSource As String
     
    With Me![テキスト1]
        strControlSource = "=GetSelectConditions([" & .Name & "])"
        .OnKeyUp = strControlSource
        .OnMouseUp = strControlSource
        .OnUndo = strControlSource
        Me.OnUndo = strControlSource
    End With
 
End Sub
 
Private Sub Form_Current()
     
    Me![テキスト1].SetFocus
    GetSelectConditions Me![テキスト1]
 
End Sub
 
Private Sub リストボックス名_DblClick(Cancel As Integer)
On Error GoTo Err_リストボックス名_DblClick
         
    Const conNoPreviousControl = 2483
         
    If Nz(Me![リストボックス名].Value, "") = "" Then
        Exit Sub
    End If
         
    Dim varBuf As Variant
     
    varBuf = Me![リストボックス名].Value & vbCrLf
         
    Dim ctlPrevious As Access.Control
        
    Set ctlPrevious = Screen.PreviousControl
     
    Me.Painting = False
     
    With ctlPrevious
        If (.Name = "テキスト1") And (IsEmpty(varSelStart) = False) Then
            .Value = Left(.Value, varSelStart) & _
                     varBuf & _
                     Mid(.Value, varSelStart + varSelLength + 1)
            .SetFocus
            .SelStart = varSelStart + Len(varBuf)
            .SelLength = 0
            GetSelectConditions ctlPrevious
        End If
    End With
     
Exit_リストボックス名_DblClick:
     
    Set ctlPrevious = Nothing
     
    Me.Painting = True
     
    Exit Sub
 
Err_リストボックス名_DblClick:
     
    Dim strErrTitle As String
    Dim strErrMsg As String
     
    Select Case Err.Number
        Case 0, conNoPreviousControl
         
        Case Else
            strErrTitle = "実行時エラー(" & Me.Name & ".リストボックス名_DblClick)"
            strErrMsg = Err.Number & ": " & Err.Description
             
            Debug.Print strErrTitle
            Debug.Print strErrMsg
             
            MsgBox strErrMsg, vbCritical, strErrTitle
             
    End Select
         
    Resume Exit_リストボックス名_DblClick
End Sub
 
Private Function GetSelectConditions(TextBox As Access.TextBox)
     
    With TextBox
        If Me.ActiveControl.Name = .Name Then
            varSelStart = .SelStart
            varSelLength = .SelLength
        Else
            varSelStart = Empty
            varSelLength = Empty
        End If
    End With
 
End Function
-------------------------------------------------------------
 
とりあえず、以上のようなコードを記述した上で
動作テストを行なってみて下さい。

回答
投稿日時: 24/06/18 13:46:55
投稿者: hatena
投稿者のウェブサイトに移動

skさんのコードの動作確認してみました。
 

mmiwa さんの引用:
テキストボックス内のカーソルのある場所に変数bufに格納された文字列を挿入した後
文字列の最後へカーソルを移動した上で改行したいのですが
改行の方法が分かりません

 
うまく動作しているようですが、
文字列の最後へカーソルを移動した上で改行の部分を私は、「テキストボックスの文字列の最後に移動して改行」と解釈していましたが、
skさんの動作をみて、「挿入した文字列の最後に移動して改行」という意味とも解釈できますね。
skさんのコードは後者の解釈ですね。
 
mmiwaさん、ご希望はどちらなんでしょうか。

回答
投稿日時: 24/06/18 16:10:42
投稿者: sk

hatena さんの引用:
テキストボックスの文字列の最後に移動して改行

hatena さんの引用:
挿入した文字列の最後に移動して改行

本件に関して、改行文字の挿入位置の違いはさほど重要ではないため、
私自身はあまり問題視していません。
 
単にリストボックスのダブルクリックが繰り返し行なわれた際に
後者のような動きをした方が、「文字列の挿入/上書き」と
「カーソル位置の移動」が正しく実行されたかどうかを確認しやすいからです。
 
abec さんの引用:
あとselstartの前に SetFocus でフォーカスを移動して置く必要があります。

hatena さんの引用:
.SelText や .SelStart はフォーカスがないと取得できないので、
コマンドボタンのクリック時なとではコマンドボタンにフォーカス移動してしまうので、実行することはできません。

abec さんと hatena さんが回答された通り、テキストボックスの
SelStart プロパティおよび SelLength プロパティは、
そのテキストボックスがアクティブである状態でしか
参照することが出来ません。
リストボックスの DblClick イベントが発生した時点では
既に[テキスト1]からフォーカスが失われているため、
その後で参照しようとしても手遅れです。
 
したがって、[テキスト1]からフォーカスが失われるまでの間、
そのカーソル位置とテキスト選択範囲が変化する都度、
これらのプロパティの値を正確に捕捉、保持しておく必要があります。
それを具体的にどのイベントで、どのような方法によって実現するかが、
本件において最も重要な問題です。
 
hatena さんの引用:
Private Sub テキスト1_Exit(Cancel As Integer)
    With Me.テキスト1
        preSelStart = .SelStart
        preSelLength = .SelLength
    End With
End Sub

hatena さんの引用:
これでサンプルで実験してい見るとうまく行く時とうまく行かないときがあります。

hatena さんが挙げられたサンプルでは Exit イベント、
つまりフォーカスを喪失しようとしている時に
SelStart プロパティと SelLength プロパティを
取得なさろうとされているわけですが、お気付きの通り
そのイベントではこれらのプロパティの値を正確に
取得できないことがあります。
 
また、そのフォームが非連結フォームではなく連結フォームであり、
かつ[テキスト1]が連結テキストボックスであった場合、
フォームのカレントレコードが他のレコードに移動されれば
[テキスト1]の値は移動後のレコードの連結フィールドの値に変わり、
テキストの選択状態はリセットされます。
フォームのアンドゥ、または[テキスト1]のアンドゥが
実行された場合も同様です。
 
以上の点を踏まえて、[テキスト1]がアクティブであり、かつ
[テキスト1]のテキストの選択状態の変化を促す操作
行なわれた時に発生し得るそれぞれのイベントにおいて
SelStart プロパティと SelLength プロパティを取得するようにし、
かつリストボックスにフォーカスが移る直前にアクティブだった
コントロールが[テキスト1]である場合のみ、これらのプロパティが
指し示すカーソル位置/選択範囲に対して「文字列の挿入/上書き」
および「カーソル位置の移動」を実行するようにしています。
直前にアクティブだったコントロールが[テキスト1]ではなければ、
この処理は実行されません。
 
もし対象となるのが([テキスト1]だけに限らず)「リストボックスに
フォーカスが移る直前にアクティブだったテキストボックス」全て
であり、
それらに対して同様の処理を実行しようとなさっているのであれば、
クラスモジュールを作成してより適切な形で処理を共通化させた方がよいでしょう。

回答
投稿日時: 24/06/18 17:18:44
投稿者: sk

sk さんの引用:
フォームのアンドゥ、または[テキスト1]のアンドゥが
実行された場合も同様です。

連結フォームのアンドゥが実行された場合については、
一旦[テキスト1]にフォーカスを移した状態で
GetSelectConditions を呼び出した方が安定しそうなので、
次のように訂正します。
 
(フォームモジュール)
-------------------------------------------------------------
Option Compare Database
Option Explicit
  
Private varSelStart As Variant
Private varSelLength As Variant
  
Private Sub Form_Load()
      
    Dim strControlSource As String
      
    With Me![テキスト1]
        strControlSource = "=GetSelectConditions([" & .Name & "])"
        .OnKeyUp = strControlSource
        .OnMouseUp = strControlSource
        .OnUndo = strControlSource
        'Me.OnUndo = strControlSource
    End With
  
End Sub
  
Private Sub Form_Current()
      
    Me![テキスト1].SetFocus
    GetSelectConditions Me![テキスト1]
  
End Sub
 
Private Sub Form_Undo(Cancel As Integer)
 
    Me![テキスト1].SetFocus
    GetSelectConditions Me![テキスト1]
 
End Sub

 
Private Sub リストボックス名_DblClick(Cancel As Integer)
On Error GoTo Err_リストボックス名_DblClick
          
    Const conNoPreviousControl = 2483
          
    If Nz(Me![リストボックス名].Value, "") = "" Then
        Exit Sub
    End If
          
    Dim varBuf As Variant
      
    varBuf = Me![リストボックス名].Value & vbCrLf
          
    Dim ctlPrevious As Access.Control
         
    Set ctlPrevious = Screen.PreviousControl
      
    Me.Painting = False
      
    With ctlPrevious
        If (.Name = "テキスト1") And (IsEmpty(varSelStart) = False) Then
            .Value = Left(.Value, varSelStart) & _
                     varBuf & _
                     Mid(.Value, varSelStart + varSelLength + 1)
            .SetFocus
            .SelStart = varSelStart + Len(varBuf)
            .SelLength = 0
            GetSelectConditions ctlPrevious
        End If
    End With
      
Exit_リストボックス名_DblClick:
      
    Set ctlPrevious = Nothing
      
    Me.Painting = True
      
    Exit Sub
  
Err_リストボックス名_DblClick:
      
    Dim strErrTitle As String
    Dim strErrMsg As String
      
    Select Case Err.Number
        Case 0, conNoPreviousControl
          
        Case Else
            strErrTitle = "実行時エラー(" & Me.Name & ".リストボックス名_DblClick)"
            strErrMsg = Err.Number & ": " & Err.Description
              
            Debug.Print strErrTitle
            Debug.Print strErrMsg
              
            MsgBox strErrMsg, vbCritical, strErrTitle
              
    End Select
          
    Resume Exit_リストボックス名_DblClick
End Sub
  
Private Function GetSelectConditions(TextBox As Access.TextBox)
      
    With TextBox
        If Me.ActiveControl.Name = .Name Then
            varSelStart = .SelStart
            varSelLength = .SelLength
        Else
            varSelStart = Empty
            varSelLength = Empty
        End If
    End With
  
End Function
-------------------------------------------------------------

投稿日時: 24/06/19 18:22:42
投稿者: mmiwa

hatebaさん skさん
ありがとうございました
 
非常に勉強になりました
お二人のやり取り、鳥肌が立つような感動を覚えました
 

引用:

文字列の最後へカーソルを移動した上で改行の部分を私は、「テキストボックスの文字列の最後に移動して改行」と解釈していましたが、
skさんの動作をみて、「挿入した文字列の最後に移動して改行」という意味とも解釈できますね。
skさんのコードは後者の解釈ですね。
  
mmiwaさん、ご希望はどちらなんでしょうか。

 
曖昧な記述で申し訳ありませんでした
僕の希望は前者でした
skさんのコードだと「挿入した文字列の最後に移動して改行」になるので
僕の希望とは異なります
 
 
引用:

以上の点を踏まえて、[テキスト1]がアクティブであり、かつ
[テキスト1]のテキストの選択状態の変化を促す操作が
行なわれた時に発生し得るそれぞれのイベントにおいて
SelStart プロパティと SelLength プロパティを取得するようにし、
かつリストボックスにフォーカスが移る直前にアクティブだった
コントロールが[テキスト1]である場合のみ、これらのプロパティが
指し示すカーソル位置/選択範囲に対して「文字列の挿入/上書き」
および「カーソル位置の移動」を実行するようにしています。

 
なるほど
「発生しうるあらゆるイベントに対する対応」が必要とは認識していたつもりですが
まだまだ勉強不足だなあと痛感しました
 
 
「テキストボックスの文字列の最後に移動して改行」については
skさんのコードを下記のように変更して
希望通りの動きとなりました
 
 
    With ctlPrevious
     
        If (.Name = "テキスト0") And (IsEmpty(varSelStart) = False) Then
         
            Dim varBuf2 As Variant
     
            varBuf2 = Left(.Value, varSelStart) & _
                      varBuf & _
                      Mid(.Value, varSelStart + varSelLength + 1)
 
            If Right(varBuf2, 2) = vbCrLf Then
                varBuf2 = varBuf2
            Else
                varBuf2 = varBuf2 & vbCrLf
            End If
 
            .Value = varBuf2
            .SetFocus
            .SelStart = Len(.Value)
             
            GetSelectConditions ctlPrevious
        End If
    End With
 
 
結局
SendKeys "^{ENTER}"
は避けてしまいました
せっかくコードを上げていただいたのにhatenaさん申し訳ありません
これを機会に SendKeys "^{ENTER}" も使ってみます
 
アドバイスいただいた皆様
本当にありがとうございました
今後ともよろしくお願いいたします