メインコンテンツへスキップ
見出し画像
Photo bymuoland

セル結合している複数行にデータをコピペするマクロ【Excel VBA】

    AIの話ばかりだと病みそうなのでたまには違った話題を。

    仕事柄、方眼紙Excelでセル結合しまくりのファイルを取り扱うことが多いです。

    もうセル結合なんて世界の害悪だからこの宇宙から無くせよ!と思うのですが、存在しているからには対策を立てて効率化したいものです。

    そこで作成したのが結合セルの結合は崩さずに突破してデータを 貼り付けるExcelマクロとなります。
    意外と便利で重宝しております。


    1.仕組み

    ※コピー元のセルも結合セルだった場合を想定した設計となっています。もちろん単体セルでも使用可能。
    ※HA列より右側の列を使用しているExcelではその範囲の値が消えてしまうので注意。
    ※コピー元はセル以外にもテキストファイルにテキストでも一応可能。

    ①対象のセル範囲をコピー
    ②貼付先の結合セルを選択
    ③マクロを実行。以下内部処理
     1.おそらく未使用であろうものすごい右端のHA1セルにコピー元の内容を値貼り付け。
     2.貼り付けた先の範囲は結合が解除された状態。この状態の範囲とデータ数を取得
     3.貼付先の結合セルを選択。2.の範囲をループ処理して、データ値が存在するセルの値のみ貼付先に代入
     4.2.の範囲の値を消去


    2.コード

    VBAのコードはこちらとなります。

    '# 結合セルを崩さずに貼りつける機能
      '┗※貼り付け対象のファイルが共有ブックかつ印刷範囲外が
      '   シート保護されいる場合は操作不可
     '┗※コピー範囲の限度列数は48列
    '# このマクロ本体以外に「GetCB」Functionが必要
    
    Sub 結合セル突破()
    
    '# 変数宣言
        Dim i        As Long
        Dim cnt      As Long
        Dim str      As String
        Dim oSheet   As String
        Dim r        As Range
        Dim oRange   As Range
        Dim pRange   As Range
    
    '# 貼付先がセル結合の場合
        If ActiveCell.MergeCells Then
       
        '# セルコピーモードの場合
            If Application.CutCopyMode = xlCopy Then
    
            '# 画面固定および確認メッセージ非表示
                Application.DisplayAlerts = False
                Application.ScreenUpdating = False
            
            '# 貼付先と貼付先シート名を変数に格納
                Set pRange = Selection
                oSheet = ActiveSheet.Name
                
                '# 一時貼付先にコピー元を値貼り付け
                  '┗Excel2003の限界列がIV列。そこから48行遡ったHA列に設定
                    Range("HA1").Select
                    Selection.PasteSpecial xlPasteValues
                '# 貼付した範囲を取得(あとでその範囲を消去するため)
                    Set oRange = Selection
                '# 貼付した範囲をコピー。もとの貼り付け先にセル移動
                    Selection.Copy
                    Sheets(oSheet).Select
                    pRange.Select
                '# 貼付した範囲をコピー。もとの貼り付け先にセル移動
                    GoSub skip2
    
                    oRange.ClearContents
                    
                    Sheets(oSheet).Select
    
            '# 画面固定および確認メッセージ非表示の解除
                Application.DisplayAlerts = True
                Application.ScreenUpdating = True
          
        '# セルコピーでない場合はクリップボードの内容を取得して貼付
            Else
                GoSub skip1
            End If
     
    '# 貼付先がセル結合ではない場合
        Else
            On Error Resume Next
            '# セルコピーモードの場合は値貼付。
            If Application.CutCopyMode = xlCopy Then
                Selection.PasteSpecial xlPasteValues
            '# セルコピーでない場合はクリップボードの内容を取得して貼付
            Else
                GoSub skip1
            End If
        End If
    
    Exit Sub
    
    '# 結合セルに値を貼りつけるためのサブルーチン
    skip2:
            
            '# 一時貼付先のセル範囲の値を本来の貼り付け先に反映
            cnt = 0
            For Each r In oRange
              
            '# セルが空欄もしくは改行コードのみの場合はスキップ
                If r.Value = "" Then
                ElseIf r.Value = vbLf Then
                Else
                    ActiveCell.Select
                    If cnt <> 0 Then
                    '# コピーが横方向の場合(前周の列番号と今回の番号が異なる場合)に貼付先セル移動
                        If cCnt <> r.Column Then
                            ActiveCell.Offset(0, 1).Select
                    '# コピーが縦方向の場合に貼付先セル移動
                        Else
                            ActiveCell.Offset(1, 0).Select
                        End If
                    End If
                    '# 値を反映
                    Selection.Value = r.Value
                    cnt = cnt + 1
                    cCnt = r.Column
                End If
    
            Next
            '# 本来の貼り付け先にセルを戻す
            pRange.Select
            Return
            
    Exit Sub
    
    '# クリップボードの内容を取得して貼りつけるためのサブルーチン
    skip1:
    
    '# クリップボードの値を変数に格納
        Call GetCB(str)
    '# クリップボード内の改行コードの数をカウント
        cnt = Len(str) - Len(WorksheetFunction.Substitute(str, vbLf, ""))
        
    '# セルが空欄もしくは改行コードのみの場合はスキップ
        If str = "" Then
        ElseIf str = vbLf Then
    '# 改行コードがない場合はそのまま反映
        ElseIf cnt = 0 Then
            
            Selection.Value = WorksheetFunction.Clean(str)
    
    '# 改行コードが有る場合は分割して各セルに反映
      '┗一つのセルに格納する分岐も考えたがあまり用途がなさそうなので除外
        Else
            For i = 0 To cnt
                Selection.Offset(i, 0).Value = WorksheetFunction.Clean(Split(str, vbLf)(i))
            Next
        End If
        Return
    
    Exit Sub
    
    ERR1:
        
    End Sub
     
    
    
    
    '# クリップボードから文字列を取得
    Public Sub GetCB(ByRef str As String)
      With CreateObject("Forms.TextBox.1")
        .MultiLine = True
        If .CanPaste = True Then .Paste
        str = .Text
      End With
    End Sub

    3.実行イメージ

    画像
    データ数と結合セル数が同じであれば、
    結合セル数が異なるもの同士でも実行可能な想定

    4.おすすめ実行方法

    キー操作で実行できるようマクロの機能をキーに割り当てての使用をおすすめします。
    僕は「ctrl」 + 「Win」キーに割り当てています。

    割り当て用(ON / OFF)

    'マクロを「ctrl」 + 「Win」キーに割り当て
    Sub mON()
        
        Application.OnKey "^{91}", "結合セル突破"
    
    End Sub
    
    '「ctrl」 + 「Win」キーの割り当てを解除
    Sub mOFF()
        
        Application.OnKey "^{91}"
    
    End Sub

    5.まとめ

    大量処理の場合は本格的にVBAで処理したほうが良さそうですが、10回以内くらいのコピペ作業の時は結構便利です。

    どうでも良いですが、作ったのが結構前のためかコードの書き方が今見ると気持ち悪いです。動きますけどね(笑)


    前回の記事


    この記事が参加している募集

     
     
    本当のお仕事は底辺の非正規労働者。東京都在住。現実逃避のためchatGPTと一緒に無駄に真面目な開発ごっこをしています。効率化とタスク管理を好む一方で非効率でも自動で動くカッコよさげな仕組みも好き。実はITスキル低め。趣味はAbemaの報道リアリティショー『アベマプライム』視聴。

    あなたへのおすすめ