[[20230426122733]] 『表示シートの最後尾(右端)を知る方法』(知りたがり) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『表示シートの最後尾(右端)を知る方法』(知りたがり)

左端にWorksheets("Master")があり、そこから右へ複数のシートがあり
そのシートの不特定数が非表示になっているブックがあります。
そのブックの最後尾(右端)にMasterシートをコピーしようとすると、
実際の最後尾が非表示になっているときは表示シートの右端にコピーされるため
コピーしたシートがWorksheets(Worksheets.Count)ではSetできないので

Public Function シート最後尾(ByVal wb As Workbook) As Long

    '非表示シートを検索し、表示シートの最後尾を返す
    Dim ws As Worksheet
    Dim Flag As Boolean
    Dim 最後尾 As Long

    Flag = False
    For Each ws In wb.Worksheets
        Select Case ws.Visible
            Case xlSheetVisible
                If Flag Then
                    Flag = False
                    最後尾 = 0
                End If
            Case xlSheetHidden, xlSheetVeryHidden
                If 最後尾 = 0 Then
                    Flag = True
                    最後尾 = ws.Index - 1
                End If
        End Select
    Next ws

    シート最後尾 = 最後尾

End Function

のようなプロシージャで表示シートの最後尾を取得しています。

もっと簡単に取得できる方法等はないでしょうか?

< 使用 Excel:Microsoft365、使用 OS:Windows10 >


 シートのコピー直後であれば
 ActiveSheet
 でコピーしたシートをとれないだろうか?
(ねむねむ) 2023/04/26(水) 13:14:20

発想を変えて一旦、末尾シートを表示状態にして、挿入が終わったら元に戻すのはどうでしょうか?
    Sub 実験01()
        Stop 'ブレークポイントの代わり
        Dim tmp As Boolean

        With Worksheets(Worksheets.Count)
            tmp = .Visible
            .Visible = True
            Worksheets("Master").Copy after:=Worksheets(.Index)
            .Visible = tmp
        End With

        MsgBox "コピー挿入したシートは【" & Worksheets(Worksheets.Count).Name & "】です"
    End Sub

(もこな2) 2023/04/26(水) 15:33:32


 お勉強に参加します^^;

 Sub Sample()

    Dim str As String
    Dim x As Long
    str = Worksheets(Worksheets.Count).Name
    x = Worksheets(Worksheets.Count).Index

    '' 最後尾(右端)にMasterシートをコピー
    ActiveSheet.Copy after:=Sheets(x) '' ← 特定シートは、Sheet12
    Debug.Print str, x

    '**************************************************************
    Rem : 処理前の[ Debug.Print ] 結果
    Rem : 左から並び順はグチャグチャ
    Rem : 左から [ Master , 6, 7, 8, 9,10, 2, 3, 4 ,5, 11, 12 ]
    Rem : 非表示は、[ 8, 3, 5, 11, 12 ]
    Rem : Debug.Print 結果: [ Sheet12 ,   12 ]

    Rem : 実行すると( 処理後 )
    Rem : Sheet13 が作成され、シート名は、(Master(2))となる
    '**************************************************************
'    ※注意
'    ActiveSheet.Copy after:=Sheets(x)と書くと他のBOOKが
'    セレクトされていると... あッ失敗!! やらかす人多し(笑)
'   webで、良く紹介されるパターンなので

 End Sub

 (もこな2)さんのように書いた方が無難ですな
   Worksheets("Master").Copy After:=Sheets(x)

(あみな) 2023/04/26(水) 16:24:49


皆さん回答ありがとうございます。

ねむねむさん>
確かにActiveSheetだと直後ならいけますね。
ただ出来るだけActive○○は使いたくないんですよね。

もこな2さん>
そのやり方だときちんと最後尾に作成されますね。
非表示のままコピーするとAfter:=.Worksheets(.Worksheets.count)なのに
何故か表示シートと非表示シートの間に作成されますからね・・・

あみなさん>
それだとシート名はSheet 13でもIndexは13にならなくないですか?
それがSetできなかった理由なんですよね・・・

よく見たら自分の作成したプロシージャも表示と非表示の間に挟まってますね。
後ろから探しても同じになりそうですし、もこな2さんのアイデアが一番目的に近い気がしますね。
(知りたがり) 2023/04/26(水) 17:43:24


 Setする事が目的なのであれば、
 明らかにIndexを把握できる場所にCopyして、
 変数にSetしてから最後尾にMoveしても良いのかもですね。

    Dim s As Worksheet
    Sheets("Master").Copy Sheets(1)
    Set s = Sheets(1)
    Debug.Print s.Index; s.Name
    s.Move after:=Sheets(Sheets.Count)
    Debug.Print s.Index; s.Name

 まあ「最後尾にMove」と言ったところで、
 CopyでもMoveでも非表示シートが無視されちゃう事に変わりはないので
 あんまり状況は変わらないのかも知れませんけど... ^^;

(白茶) 2023/04/26(水) 20:24:18


 コピーだけ出来たら良いかと勘違いしました (o_ _)o ペコ

 Set もなんですね( ; ; )

 新たな作戦を、検討したのですが…無謀でしたw

 Sub Sample() '' これはエラーになりますw
    Dim ws As Worksheet
    Set ws = Worksheets.Add
    Debug.Print ws.Name
    Worksheets("Master").Copy Worksheets(ws.Name)
 End Sub
(あみな) 2023/04/26(水) 20:38:16

 これはいけるような

 Sub Sample()
    Dim ws As Worksheet
    Set ws = Worksheets.Add
    Worksheets("Master").Range("A1").Copy _
    Destination:=Worksheets(ws.Name).Range("A1")
    Debug.Print ws.Name
    MsgBox ws.[A1]
 End Sub

 ※今日は、もう遅いので寝ます...皆様、おやすみなさい。
(あみな) 2023/04/26(水) 23:12:01

回答ありがとうございます。

白茶さん>
仕様としか言いようがありませんが、なぜ非表示は無視されてしまうんですかね・・・
xlSheetVeryHiddenの時だけならまだわかる気もするのですが。
行列の非表示のときのCountのように表示部分だけのCountにもなりませんしね。
よくわからない仕様です。

あみなさん>
色々試してもらってありがとうございます。
(知りたがり) 2023/04/27(木) 07:33:03


 私、2016ですが、
 >実際の最後尾が非表示になっているときは表示シートの右端にコピーされる
 この現象再現しないです。

 ちゃんと最後に追加されますが、私なにか勘違いしてますか?
(´・ω・`) 2023/04/27(木) 08:36:25

 (´・ω・`)さん再現しないのか〜

 私、2021ですが、
 >実際の最後尾が非表示になっているときは表示シートの右端にコピーされる
 この現象になる。

 ↓これダメ

 Sub 実験02()
    With Worksheets(Worksheets.Count)
        Worksheets("Master").Copy after:=Worksheets(.Index)
    End With
 End Sub

 今も、(白茶)さんの 2023/04/26(水) 20:24:18
 マクロでしたけど...オモイッキリなる

(あみな) 2023/04/27(木) 09:23:41


 基本、(もこな)おね〜さまの方法が、優勝ですね。(きっと)

 BOOKのシート構成を、下記のイメージとして挑戦しました。					
 これなら、完成度90% ( 自己評価 )					

 ・左から、[ Master、注文、在庫、リスト ]のシート全4枚として					
 ・最後尾の[ リスト ]シートは、非表示になっている。(基本)					

 Sub Sample()					
    Dim ws As Worksheet					
    Set ws = Worksheets.Add					
    Worksheets("リスト").Visible = True					
    Worksheets("Master").[A1:E10].Copy _					
    Destination:=Worksheets(ws.Name).[A1:E10]				
    ws.Move after:=Sheets(Sheets.Count)					
    ws.Name = "発注書"  ''( 新規シート名は仮の話です )					
    Worksheets("リスト").Visible = False					
    MsgBox ws.Name & "へコピーしました"					
 End Sub					

 ※ 流れ					

 ・[ リスト ]を Visible = True して					
 ・ Worksheets("Master")をコピー					
 ・[ Move after ] 君で、最後尾に移動させて					
 ・新規のシートに名前を[ 発注書 ]と付けて					
 ・[ リスト ] Visible = False して終了					
 ・既にSet 済なので、お好きにどうぞってイメージ					

(あみな) 2023/04/27(木) 09:37:26


 ごめんなさい
 私が勘違いしていました
 シートの追加(Add)ではなくコピーでした
 たしかにそうなります
(´・ω・`) 2023/04/27(木) 10:06:16

 よし来た、↓下なら

  Sub Sample()
    Dim ws As Worksheet
    Set ws = Sheets.Add(after:=Sheets(Sheets.Count))
    ws.Name = "発注書"
    Worksheets("Master").[A1:E10].Copy _
    Destination:=Worksheets(ws.Name).[A1:E10]
    MsgBox ws.Name & "へコピーしました"
 End Sub

 Worksheets("リスト").Visible = True
 Worksheets("リスト").Visible = False
 ws.Move after:=Sheets(Sheets.Count)

 が省ける
(あみな) 2023/04/27(木) 10:07:56

返答ありがとうございます。

(´・ω・`)さん>
コピーですね。
Addなら普通に最後尾に追加されるのがまた腹立たしい感が・・・

あみなさん>
Addのあとに様式コピーだと印刷関係の設定はコピーされないんですよね。
なので私はAddよりコピーを推します。
(つまりもこな2さんのが一番良いかと)

今回のはもともとの様式が左端(Worksheets(1))に原紙シートがあったのでこのような質問になりましたが、
こういう原紙シートからコピーシートを作成していくようなマクロがある場合
原紙シートを最後尾にしてBefore:=原紙シートにした方がスマートなのかな、って思いました。
※今更ですが。
(知りたがり) 2023/04/27(木) 12:44:58


 >確かにActiveSheetだと直後ならいけますね。
 >ただ出来るだけActive○○は使いたくないんですよね。

 何か論理的とは思えないのだが・・

 こんなにすったもんだする位なら ActiveSheetが最適じゃないの?

(半平太) 2023/04/27(木) 13:50:27


 >Addのあとに様式コピーだと印刷関係の設定はコピーされないんですよね。

 印刷関係の設定ってどんなのかしら?
 う〜ん、ちょっと良くわからないです。

 原紙一発コピーってことなのか?
 私には、されたいコピー内容までわかりかねます。

 もこなさんのコードに、Set入れてイメージで
 良いのでされたい事を、教えてください。

 そしたら、こちらも勉強になりますので
 Ser後に、どんなマクロ内容の事をされるのか
 興味があります。

(あみな) 2023/04/27(木) 14:31:08


返答ありがとうございます。

半平太さん>
あの時点ではSetの問題しか考えてなかったのであんな回答になりましたが、
もこな2さんの案で完全に最後尾にシートがコピーされるのでその案が1番かなと思いました。
だから個人的には解決?はしています。
Activesheetを使いたくないのはコード組み替えとかした時に
見た目どのシートか分かりづらいからですね。

あみなさん>
印刷関係の設定と言うのは印刷範囲だったりヘッダー・フッターとかの設定ですね。
コピー後にコード上で設定してもいいですが、しなくていいならそちらのが楽ですから…
>もこなさんのコードに、Set入れてイメージで
> 良いのでされたい事を、教えてください。
今自宅でPCにさわれないのでコードは明日になりますが、
やりたい事は別ブックのデータの月締めのときに
履歴ブックの書式や印刷設定してある原紙シートをコピー作成して
配列でデータ転記しシート名を変えるといったものです。
その後印刷してファイルに閉じてます。
こんな説明で分かりますかね?
実際のコードを見たらSet要らないやん!とか言われそうな気がしますが…

(知りたがり) 2023/04/27(木) 23:37:17


 うーむ、今まで
 Worksheets(Worksheets.Count)の後ろにコピーすれば、その後は
 Worksheets(Worksheets.Count)はコピー後のシートになるので「凄く分かり易い」。
 Activesheetを使うなんて、ド素人のコードだね。

 皆、そう思っていたハズなんだが、
 非表示のシートがあった場合は「逆じゃね?」って話ですよね。

 堅牢であるべき実務コードでは、もこな2さんの様な慎重なコードを書いているんですかね?
 まさか、非表示シートなんて想定しなくていいよ、なんて前提でいいとも思えないのだが。。
 (実務家にお聞きしたいものです)

(半平太) 2023/04/28(金) 10:05:06


確かマクロの記録だと「最終シートが非表示でないときに最後尾にコピーする」以外は全部「Before:=」になる(うろおぼえ)ので、「コピーの基本はビフォアなんだな〜」と思いこんだ結果、
(知りたがり) 2023/04/27(木) 12:44:58 にあるような
「原紙シートを最後尾にしてBefore:=原紙シート」方式ばっかり使ってる
結果としてこういう問題にあたったことがないので、思い込みであれ方針は間違ってなかったのかもしれない

原紙シートが左端にあるほうが一般的なのかな?
確かに手動でやるならそっちのほうが便利かもしれないけど
(ufj) 2023/04/28(金) 11:05:56


 元にするシート1枚だけのブックをどこかに保存しておいて

 Dim ws As Worksheet
 Set ws = Sheets.Add(after:=Worksheets(Worksheets.Count), Type:="D:\Master.xlsx")
 
 とかするのが一番簡単な感じですね

 type引数には、xltxじゃなくてxlsxでもいいみたいです
(´・ω・`) 2023/04/28(金) 11:20:16

半平太さん>

>非表示のシートがあった場合は「逆じゃね?」って話ですよね。
After:=Worksheets(Worksheets.Count)なのにWorksheets.Countより
手前にコピーされてしまう不可思議使用によりActivesheetの価値が若干上がった感ありますね。
全てを知り尽くしたプログラマーであれば、どのようなコードかは分かりませんが
最後尾の表示・非表示関係なく全て同じ動作をするように組んでいるのでしょうね。

(´・ω・`)さん>
同じブックに原紙は置かず、不可思議使用に引っかかりそうなコードは組まない・・・
が最善策ですかね。

あみなさん>
取り合えず今実際に組んでいるコードはこんな感じです。
いろいろ突っ込みどころはあるでしょうがご容赦を。

Public Sub 更新()

    '
    Dim wb履歴 As Workbook
    Dim ws原料 As Worksheet, ws履歴 As Worksheet, _
        ws As Worksheet, ws最後尾 As Worksheet
    Dim 読取範囲 As Range, 貼付範囲 As Range, _
        消去範囲(2) As Range, 先月末在庫範囲 As Range, 対象月 As Range

    Dim 転写 As Variant, 在庫 As Variant
    Dim 範囲 As String, シート名 As String
    Dim 最終行 As Long, 行 As Long, msg As Long, 最後尾 As Long
    Dim 表示状態 As Boolean

    '各範囲の設定
    Set ws原料 = ThisWorkbook.Worksheets("原料管理")
    With ws原料
        最終行 = .Cells(.Rows.Count, "A").End(xlUp).Row
        範囲 = "$A$1:" & .Cells(最終行, "BO").Address
        Set 読取範囲 = .Range(範囲)
        Set 消去範囲(1) = .Range("$D$1:$BM$1")
        Set 消去範囲(2) = .Range("$D$4:$BM$" & 最終行)
        Set 先月末在庫範囲 = .Range("$BN$4:$BN$" & 最終行)
        Set 対象月 = .Range("A1")
    End With

    '必要なデータの格納
    ReDim 在庫(最終行 - 4, 0)
    転写 = 読取範囲.Value
    For 行 = 4 To 最終行
        在庫(行 - 4, 0) = 読取範囲(行, 2)
    Next 行
    シート名 = Format(対象月.Value, "yyyy年m月")

    '
    Set wb履歴 = GetBook '←別プロシージャでブック開いてます
    With wb履歴
        '作成しようとするシートと同月がないか検索、あれば質問
        For Each ws In .Worksheets
            If ws.Name = シート名 Then
                msg = MsgBox("作成しようとする月のシートが既に存在します。" & vbLf & _
                             "このまま更新を進めますか?", vbYesNo + vbQuestion)
                If msg = vbNo Then Exit Sub
                シート名 = "重複シート (" & シート名 & ")"
                Exit For
            End If
        Next ws

        '最後尾に"Master"シートをコピー、シート名を当該月にする
        Set ws最後尾 = .Worksheets(.Worksheets.Count)
        表示状態 = ws最後尾.Visible
        ws最後尾.Visible = xlSheetVisible
        .Worksheets("Master").Copy After:=ws最後尾
        Set ws履歴 = .Worksheets(.Worksheets.Count)
        ws最後尾.Visible = 表示状態
        ws履歴.Name = シート名

        'データを貼り付け
        Set 貼付範囲 = ws履歴.Range(範囲)
        貼付範囲.Value = 転写
    End With

    '原料管理シートを初期化
    対象月.Value = DateValue(Year(対象月.Value) & "/" & Month(対象月.Value) + 1 & "/01")
    先月末在庫範囲.Value = 在庫
    消去範囲(1).ClearContents
    消去範囲(2).ClearContents

End Sub

(知りたがり) 2023/04/28(金) 11:29:49


 (知りたがり)さん...ありがとうございます。(o_ _)o. ペコ

 >取り合えず今実際に組んでいるコードはこんな感じです。
 >いろいろ突っ込みどころはあるでしょうがご容赦を。

 突っ込めるほど、知識がありませんが、研究材料に
 させていただきます。

(あみな) 2023/04/28(金) 11:45:22


 Addなら最後尾来るなら、スケープゴート作ってやるのも手ですよね。
    Sub test()
        Dim ws As Worksheet
        With Sheets.Add(after:=Sheets(Sheets.Count))
            Sheets("Sheet1").Copy after:=Sheets(.Index)
            Application.DisplayAlerts = False
            .Delete
            Application.DisplayAlerts = True
        End With
        Set ws = Sheets(Sheets.Count)
        Debug.Print ws.Name
    End Sub

(稲葉) 2023/04/28(金) 16:53:25


稲葉さん>
そういうやり方もありますね。
色々と気付かされて勉強になります。

皆さんありがとうございます。
(知りたがり) 2023/04/28(金) 18:13:42


ufjさん>
コメント見逃していました、すみません。
確かに手動コピーだとbeforeですね。
基本は間に差す仕様なのかもしれませんね。

少数のシートの複製ならば追加は後ろ…って思ってしまうので
原紙は先頭にあった方が見栄えが良さげな感がありますが、
多数のシートの複製であれば、複製した時に見える最後尾の方が
分かりやすいのかもしれませんね。

この様なブックの使い方をしている方々はどっちが多いのかな…と思いました。
(知りたがり) 2023/04/29(土) 23:06:53


>いろいろ突っ込みどころはあるでしょうが〜
ツッコミというか感想ですが、変数を盛りだくさんにしてかえって混乱しないですかね。
あと、一旦配列を変数に入れるのは冗長な気がします。

踏まえて別案。興味があれば、ステップ実行等を行い研究のうえ、必要な部分をご自身のコードに組み込んでください。

 ※1 理解して頂きたいので丸パクリはNGとします。
 ※2 コンパイルエラーにならないことしかチェックしてません。

    Sub 別案()
        Dim 最終行 As Long
        Dim wb履歴 As Workbook
        Dim tmp As Worksheet, dstSH As Worksheet
        Dim シート名 As String
        Dim 表示状態 As Boolean

        With ThisWorkbook.Worksheets("原料管理")
            最終行 = .Cells(.Rows.Count, "A").End(xlUp).Row
            シート名 = Format(.Range("A1").Value, "yyyy年m月")

            Set wb履歴 = Workbooks("hogehoge.xlsx")

            '▼シート重複チェック
            On Error Resume Next
            Set tmp = wb履歴.Worksheets(シート名)
            On Error GoTo 0
            If Not tmp Is Nothing Then
                If MsgBox("作成しようとする月のシートが既に存在します。" & vbLf & "このまま更新を進めますか?", vbYesNo + vbQuestion) = vbNo Then
                    Exit Sub
                Else
                    シート名 = "重複シート (" & シート名 & ")"
                End If
            End If

            '▼シート挿入処理
            表示状態 = wb履歴.Worksheets(wb履歴.Worksheets.Count).Visible
            wb履歴.Worksheets(wb履歴.Worksheets.Count).Visible = xlSheetVisible
            wb履歴.Worksheets("Master").Copy After:=wb履歴.Worksheets(wb履歴.Worksheets.Count)

            '▼転記処理
            Set dstSH = wb履歴.Worksheets(wb履歴.Worksheets.Count)
            dstSH.Previous.Visible = 表示状態
            dstSH.Name = シート名
            dstSH.Range("A1:BO" & 最終行).Value = .Range("A1:BO" & 最終行).Value

            '▼"原料管理"シートのリセット(月次更新?)
            .Range("A1").Value = DateAdd("m", 1, Range("A1").Value) 'A1セルを1か月後に更新
            .Range("BN4:BN" & 最終行).Value = .Range("B4:B" & 最終行).Value
            .Range("D1:BM1,D4:BM" & 最終行).ClearContents
        End With
    End Sub

(もこな2 ) 2023/05/01(月) 19:07:41


もこな2さん>

やはり上手な人が組むとすっきりしますね・・・
DateAddとか知りませんでしたし、勉強になります。

>変数を盛りだくさんにしてかえって混乱しないですかね。
これは自分が今の職場から居なくなった時に
VBAをほとんど知らない方(自分が熟知しているわけではないですが)が見ても
変数を日本語にして入れておけば分かるかな?と思ってやってたんですが、
やっぱり逆に分かりづらいですかね。
マクロの中身を知らないで様式変えたりする人もいるんで・・・

>一旦配列を変数に入れるのは冗長な気がします。
すみません、配列を覚えたてなので使いたくなってしまいました。
(知りたがり) 2023/05/02(火) 07:38:21


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.