できるようにしてみた
アンドゥ(元に戻す)回数の上限は3に指定してみた
セルの値を使ってテキストボックスを作成するマクロを
アンドゥ・リドゥできるようにしてテストしているところ
最初は下半分は無視して、上半分だけ見たほうがわかりやすい
下半分がアンドゥ・リドゥ用のマクロがあるブックでバックアップ用になる裏方
上半分のBook1とBook3が普通のブック
同じブックでも異なるブックに対してもアンドゥ・リドゥできている
アンドゥ・リドゥの動作の考え方としては単純で
マクロを実行する直前にバックアップ用のブックにシートをコピーしておいて
アンドゥ・リドゥの時にはバックアップ用から元のブックに戻しているだけ
だから前回のをちょっと変えるだけだから1日でできそう!(意気揚々)
3日かかった!!!
ダウンロード先
ファイル名:マクロで行った処理をマクロでアンドゥ・リドゥ_シートをコピペ編.xlsm
使い方
ユーザーフォームのボタンに実行したいマクロを書いたり指定したりするときは
例えばCommandButton1のクリックイベントにかくと
Private Sub CommandButton1_Click()
’選択セルに0から10までのランダムな数値を記入するマクロ
Dim r As Range
Randomize
For Each r In Selection
r.
Value = Int((10 - 0 + 1) * Rnd(10) - 0)
Next
End Sub
こんなかんじになると思う
これをアンドゥ・リドゥできるようにするには
Private Sub CommandButton1_Click()
’選択セルに0から10までのランダムな数値を記入するマクロ
Call myBefore前処理(ActiveSheet)
Dim r As Range
Randomize
For Each r In Selection
r.
Value = Int((10 - 0 + 1) * Rnd(10) - 0)
Next
Call myAfter後処理
End Sub
こんなふうに赤字のところを書き加える
Call myBefore前処理(ActiveSheet)
このActiveSheetはどのシートを保存するかの指定だから
ActiveSheet以外のシートをアンドゥ・リドゥの対象にしたいときは
そのシートを指定する
要は
マクロの前にCall myBefore前処理(ActiveSheet)
マクロの後にCall myAfter後処理
を書けばいいだけ
あとは
1行目にある
Const wsCount As Long = 3
この数値3がアンドゥ・リドゥできる回数の上限になる
これを超えると古いものに上書きしていく
ユーザーフォームを表示するときは
UserFormUndoRedo.Show vbModeless
vbModelessを付けてシートやセルをクリックできるように表示する
これは標準モジュールに書く
Sub uf3Show()
UserFormUndoRedo.Show vbModeless
End Sub
標準モジュールに書いてある
これを実行してユーザーフォームを表示する
ユーザーフォームを表示したところ
上にある数値は左が元に戻せる回数で右がやり直しできる回数
選択セルに乱数を記入するマクロが登録されたボタンを押す
テキストボックスボタンを押す
このボタンには選択範囲の値を記入したテキストボックスを
作成するマクロが登録してある
テキストボックスが作成されて元に戻せる回数が増えている
元に戻すボタンを押すと
元に戻すの回数は1減って、やり直しが1回増える
やり直しボタンを押すと
元に戻す回数が1増えて、やり直し回数が1減った
元に戻すボタンを2回押すと
ここから乱数ボタンを押すと
やり直し回数がリセットされて0になる
対応できないマクロ、エラーになるような状況
例えば
- ブックやシートを作成したり削除するマクロ
- 対象にするシートは1枚だけなので複数のシートに変更を加えるマクロ
- エクセル以外のファイルに何かするマクロ
- マクロで行った処理をマクロでアンドゥ・リドゥ_シートをコピペ編.xlsmにシート名がSave1とかSave21みたいなSaveと数字の組み合わせのシートがあるとユーザーフォームが表示できなくなる
- シート名:午後のアンドゥ・リドゥの左側はバックアップ用のシートなので、左側にシートを作成したり、左側のシートを削除したり変更したりすると正しく動作しなくなる
これ以外にもあると思うけど、こんなところ
対応できるマクロ、対応できそうなマクロ
- 1枚のシートの中で収まっているマクロ
- セルの書式や値を変更したりとかのマクロ
できるかどうか試す前にはファイルをバックアップをしてからの方がいい
ユーザーフォームのデザイン画面
このCommandButton1に普通にマクロを登録した状態が
↓
こんなかんじで
これをアンドゥ・リドゥ対応にするには
↓
こう
最小値から最大値の間の整数を選択セルに記入するマクロ
Const wsCount As Long = 3
Dim undoCount As Long
Dim redoCount As Long
Dim SaveSlot As Long
Dim ima As mySousa
Dim mae As mySousa
Private Enum mySousa
nasi = 0
Sitei = 1
iUndo = 2
iRedo = 3
End Enum
Dim mySheets() As Worksheet
Dim SheetName() As String
Dim SheetIndex() As Long
Dim BookName() As String
Private Sub UserForm_Initialize()
Application.ScreenUpdating = False
Dim ActSh As Worksheet: Set ActSh = ActiveSheet
mae = nasi
undoCount = 0
redoCount = 0
SaveSlot = 0
ReDim SheetName(wsCount - 1)
ReDim SheetIndex(wsCount - 1)
ReDim BookName(wsCount - 1)
Call urLabelラベル表示更新
ReDim mySheets(wsCount - 1)
Dim WS As Worksheet
Set WS = ThisWorkbook.Sheets.Add(Before:=ThisWorkbook.Sheets(1))
WS.Name = "Save" & 0
Set mySheets(0) = WS
For i = 1 To wsCount - 1
Set WS = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(i))
WS.Name = "Save" & i
Set mySheets(i) = WS
Next
Me.CommandButton1.SetFocus
Application.ScreenUpdating = True
ActSh.Activate
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
On Error GoTo myError
Application.DisplayAlerts = False
oii = mySheets
For i = 0 To wsCount - 1
ore = mySheets(i).Name
mySheets(i).Delete
Next
Application.DisplayAlerts = True
Exit Sub
myError:
Application.DisplayAlerts = True
MsgBox Err.Description
End Sub
Private Sub CommandButton1_Click()
Call myBefore前処理(ActiveSheet)
Call myRand選択範囲に乱数(10, 0)
Call myAfter後処理
End Sub
Private Sub CommandButton2_Click()
Call myBefore前処理(ActiveSheet)
Call セルの値でテキストボックスランダム色
Call myAfter後処理
End Sub
Private Sub CommandButtonRedo_Click()
If redoCount = 0 Then Exit Sub
ima = iRedo
Call GetSaveSlot保存場所の決定
Call UandRアンドゥリドゥの処理
undoCount = undoCount + 1
redoCount = redoCount - 1
Call urLabelラベル表示更新
mae = iRedo
End Sub
Private Sub CommandButtonUndo_Click()
If undoCount = 0 Then Exit Sub
ima = iUndo
Call GetSaveSlot保存場所の決定
Call UandRアンドゥリドゥの処理
undoCount = undoCount - 1
redoCount = redoCount + 1
Call urLabelラベル表示更新
mae = iUndo
End Sub
Sub urLabelラベル表示更新()
Me.LabelRedoCount = redoCount
Me.LabelUndoCount = undoCount
End Sub
Sub GetSaveSlot保存場所の決定()
If ima = iUndo And mae = iUndo Then
If SaveSlot = 0 Then
SaveSlot = wsCount - 1
Else
SaveSlot = SaveSlot - 1
End If
ElseIf ima = iUndo Or mae = iUndo Then
ElseIf mae = nasi Then
SaveSlot = 0
ElseIf SaveSlot < wsCount - 1 Then
SaveSlot = SaveSlot + 1
ElseIf SaveSlot >= wsCount - 1 Then
SaveSlot = 0
End If
End Sub
Sub myBefore前処理(myWS As Worksheet)
On Error GoTo myError
Application.DisplayAlerts = False
ima = Sitei
Dim newSheet As Worksheet
Call GetSaveSlot保存場所の決定
SheetName(SaveSlot) = myWS.Name
SheetIndex(SaveSlot) = myWS.Index
BookName(SaveSlot) = myWS.Parent.Name
ThisWorkbook.Sheets(SaveSlot + 1).Delete
myWS.Copy Before:=ThisWorkbook.Sheets(SaveSlot + 1)
Set mySheets(SaveSlot) = ThisWorkbook.Sheets(SaveSlot + 1)
myWS.Activate
Application.DisplayAlerts = True
Exit Sub
myError:
Application.DisplayAlerts = True
MsgBox "何かのエラー発生" & Err.Description
End Sub
Sub myAfter後処理()
If undoCount < wsCount Then
undoCount = undoCount + 1
End If
redoCount = 0
Call urLabelラベル表示更新
mae = Sitei
End Sub
Private Sub UandRアンドゥリドゥの処理()
Dim Sc As Long
Dim motoBook As Workbook
Dim myIndex As Long: myIndex = SheetIndex(SaveSlot)
Dim motoSheet As Worksheet
Set motoBook = Workbooks(BookName(SaveSlot))
Set motoSheet = motoBook.Sheets(myIndex)
Application.ScreenUpdating = False
If motoBook.Name <> ThisWorkbook.Name Then
motoSheet.Move Before:=ThisWorkbook.Sheets(SaveSlot + 1)
If myIndex > motoBook.Sheets.Count Then
Sc = motoBook.Sheets.Count
mySheets(SaveSlot).Move After:=motoBook.Sheets(Sc)
Else
mySheets(SaveSlot).Move Before:=motoBook.Sheets(myIndex)
End If
Else
On Error GoTo myError
Application.DisplayAlerts = False
motoSheet.Copy Before:=ThisWorkbook.Sheets(SaveSlot + 1)
motoSheet.Delete
Application.DisplayAlerts = True
If myIndex >= motoBook.Sheets.Count Then
Sc = motoBook.Sheets.Count
mySheets(SaveSlot).Move After:=motoBook.Sheets(Sc)
Else
mySheets(SaveSlot).Move Before:=motoBook.Sheets(myIndex)
End If
End If
motoBook.Sheets(myIndex).Name = SheetName(SaveSlot)
Set mySheets(SaveSlot) = ThisWorkbook.Sheets(SaveSlot + 1)
Application.ScreenUpdating = True
Exit Sub
myError:
Application.DisplayAlerts = True
End Sub
Sub myRand選択範囲に乱数(最大値 As Long, 最小値 As Long)
Dim r As Range
Randomize
For Each r In Selection
r.Value = Int((最大値 - 最小値 + 1) * Rnd(最大値) - 最小値)
Next
End Sub
Sub セルの値でテキストボックスランダム色()
Dim c As Range
Dim myRng As Range
Dim myTxt As String
Dim myShar As Shape
Randomize
Set myRng = Selection
For Each c In myRng
myTxt = c.Value
If myTxt <> "" Then
Set myShar = ActiveSheet.Shapes.AddShape( _
msoShapeRoundedRectangle, c.Left, c.Top, c.Width, c.Height)
With myShar.Fill
.Visible = msoTrue
.ForeColor.RGB = Int((16777215 - 0 + 1) * Rnd(16777215) - 0)
End With
myShar.Line.Visible = msoFalse
With myShar.TextFrame
.Characters.Text = myTxt
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
.AutoSize = False
End With
With myShar.TextEffect
.FontName = c.Font.Name
.FontSize = c.Font.Size
End With
End If
Next
End Sub
シートのブック間の移動やコピーのマクロって書いたことなかった
変数に入れたシートを移動させると変数から飛び出して変数が空になるとか
予想外だったw
今まではエラーが出ても無視して突き進む
On Error Resume Next
こればかり書いていたけど
シートを削除しようとするときに出るダイアログボックスを非表示にする
Application.DisplayAlerts = False
これが放置されたままになる状況があって、それはかなり良くないと思って
エラーの対処も少し書いてみた
参照したところは
Office TANAKA -
Excel VBA Tips[エラーに負けない]
続きは翌日