I needed a dynamic frozen header row in Excel as the sheet I was working with had Several Tables that were large and were easier to understand if they were located on the same sheet.
But after searching endlessly I could not find a solution as there is no event for scrolling and scrolling doesn't change the active cell.
Thankfully I figured out a work around.
I was able to come up with an acceptable solution for my dilemma after searching for how to Identify the first visible row in the active window running across
MSDN: Identify First Visible Row of Active Window
I was then able to take that code and convert it to a function that could be used in combination with a Timer event that is only activated on the sheet I need the frozen row.
Sheet Code:
Private Sub Worksheet_Activate()
StartFreezePaneTimeRefresh
End Sub
Private Sub Worksheet_Deactivate()
StopFreezePaneTimeRefresh
End Sub
Dynamic Freeze Pane Module Code:
Private RefreshTime
Sub SetFreezePane()
'Check if correct worksheet is active
If ActiveWorkbook.ActiveSheet.Name = "Data" Then
If IdentifyTopVisibleRow < 227 Then
'Check if Frozen Row is the same as the Range to be Copied
If Range("A1") <> Range("AN1") Then
'Copy New Headers for Frozen Row
Range("AN1:BU1").Copy
Range("A1").PasteSpecial xlPasteValues
End If
ElseIf IdentifyTopVisibleRow > 227 Then
'Check if Frozen Row is the same as the Range to be Copied
If Range("A1") <> Range("AN2") Then
'Copy New Headers for Frozen Row
Range("AN2:BU2").Copy
Range("A1").PasteSpecial xlPasteValues
End If
End If
Else
StopFreezePaneTimeRefresh
End If
End Sub
Sub StartFreezePaneTimeRefresh()
Call SetFreezePane
RefreshTime = Now + TimeValue("00:00:01")
Application.OnTime RefreshTime, "StartFreezePaneTimeRefresh"
End Sub
Sub StopFreezePaneTimeRefresh()
On Error Resume Next
Application.OnTime RefreshTime, "StartFreezePaneTimeRefresh", , False
End Sub
Public Function IdentifyTopVisibleRow() As Long
'This code was found on MSDN at
'https://social.msdn.microsoft.com/Forums/en-US/a6cff632-e123-4190-8556-d9f48af8fe9a/identify-first-visible-row-of-scrolled-excel-worksheet?forum=isvvba
Dim lngTopRow As Long ' top row
Dim lngNumRows As Long ' number of visible rows
Dim lngLeftCol As Long ' leftmost column
Dim lngNumCols As Long ' number of visible columns
With ActiveWindow.VisibleRange
lngTopRow = .Row
lngNumRows = .Rows.Count
lngLeftCol = .Column
lngNumCols = .Columns.Count
End With
IdentifyTopVisibleRow = lngTopRow
End Function
The code works by first checking if the correct sheet is active and if it is then it checks the top most visible row every second.
If the top row is Greater or Lesser than the beginning rows of each table it then will check to see if the first header is already set to prevent it from changing the values over and over.
If not it changes the Frozen Row values based upon the users location in the workbook.
Notes:
The change is delayed by 1 second but that is acceptable for what I am doing this for.
The sheet I am using this on is view only as this would constantly shift the focus to the first row if you have an idea on how to set the first row values without changing selection that would make this work great.
Related
I am trying to copy multiple columns from multiple worksheets into a new worksheet in Excel using a VBA Macro.
I have already created the worksheet, and I want to paste specific columns one after another in that worksheet.
I would like to copy from each worksheet all columns including and beyond a certain column, in all worksheets including and from Column F.
I have written a piece of code that selects the appropriate data and loops correctly.
However, i get a "run-time error 1004", when the loop hits a worksheet where I am copying only one column.
I know this is because of the choice of my code. However, I don't know how to solve the problem.
The problem is that my code selects a range to the end of the worksheet when there is only one column being selected. This creates a copied area too big to paste in the new worksheet.
Dim i As Integer
i = 1
Do While i <= Worksheets.Count - 1
Worksheets(i).Select
'Select, Copy and Paste Data
RangeFromF1
Selection.Copy
Worksheets("Combined").Select
Range("X1").Select
Selection.End(xlToLeft).Select
ActiveCell.Offset(0, 1).Select
ActiveSheet.Paste
i = i + 1
Loop
End Sub
Public Sub RangeFromF1()
Range("F1", Range("F1").End(xlDown).End(xlToRight)).Select
End Sub
Instead of going from column F to the right, try going from the last column to the left.
Public Sub RangeFromF1()
Range("F1", Cells(1, Columns.Count).End(xlToLeft).End(xlDown)).Select
End Sub
You might also want to get rid of all the Select stuff.
Sub CopyStuff()
Dim i As Long
i = 1
Do While i <= Worksheets.Count - 1
With Worksheets(i)
.Range("F1", .Cells(1, .Columns.Count).End(xlToLeft).End(xlDown)).Copy
Worksheets("Combined").Cells(1, Columns.Count).End(xlToLeft).Offset(, 1).Paste
i = i + 1
End With
Loop
End Sub
Before coming back to check for your answer noris, I figured out a way, to do as you suggested, with the following code:
Public Sub ReferenceSelection()
Dim startcell As Range
Set startcell = Range("A1").End(xlDown).End(xlToRight)
Range(startcell, ("F1")).Select
End Sub
I have a considerable database in Excel. About half of the column data are entered by hand. One problem has plagued me for some time.
I insert rows by hand.
I copy the formulae by hand. Quite Error prone.
I set up the formats by hand.
The sheet is fairly complex.
I've tried to automate the process, alas always with some bug or another.
Sub InsertRow()
ActiveCell.Offset(1).EntireRow.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromRightOrAbove
ActiveCell.EntireRow.Copy
ActiveCell.Offset(1).EntireRow.PasteSpecial xlPasteFormats
ActiveCell.Offset(1).EntireRow.PasteSpecial xlPasteFormulas
Application.CutCopyMode = False
End Sub
This doesn't seem to copy the formulae. And how would I put default values in the specific columns?
All help is appreciated.
Here's a solution for your problem. Install the code in the code module of the worksheet on which you wish to insert rows. It's one of the pre-existing modules in the workbook, named after the tab. If you want the same action on several worksheets install a version of the procedure in each applicable code module.
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Const TriggerClm As String = "A" ' change to suit
Const FirstDataRow As Long = 2 ' change to suit
Dim Rng As Range
Set Rng = Cells(Rows.Count, TriggerClm).End(xlUp).Offset(1)
With Target
If (.Address = Rng.Address) And (.Row > FirstDataRow) Then
Rows(.Row - 1).Copy ' copies from last used row
' Rows(FirstDataRow).Copy ' copies from FirstDataRow
Rows(.Row).Insert Shift:=xlDown
On Error Resume Next
Rows(.Row - 1).SpecialCells(xlCellTypeConstants).ClearContents
Application.CutCopyMode = False
Cancel = True
End If
End With
End Sub
And this is what will happen. The code module captures when you double-click on a cell and the above procedure responds to that action. If the double-click was on the blank cell below the last used cell in column A (TriggerClm) a blank row will be inserted at that point. It will contain all the formats and formulas it copies from either the row above or the first row in the sheet (FirstDataRow).
And that also defines the required setup. You can specify a TriggerClm other that "A" and a FirstDataRow other than 2. And you must chose between the two sources to copy from, disabling the one you don't want to use. Please read the remarks in the code.
After many years of wavering I now favour the last used row over the first in most of my projects. I also usually add code to insert the date if one is required, usually in the TriggerClm.
You may be familiar with the original function of double-click to switch to in-cell editing. This functionality is cancelled when a row is inserted. But if you take out Cancel = True then the code would stop in Edit mode in the new cell.
Here's the code if you want to leave it as a macro in a module. Not that when excel copies "formulas" it also copies values. I also added a lien to show you how to change column 3 to 5
Sub InsertRow()
Dim CurRow As Range
Dim NewRow As Range
ActiveCell.EntireRow.Insert
Set CurRow = ActiveCell.EntireRow.Offset(1)
Set NewRow = ActiveCell.EntireRow
CurRow.Copy
NewRow.PasteSpecial Paste:=xlPasteFormulas
NewRow.PasteSpecial Paste:=xlPasteFormats
Application.CutCopyMode = False
NewRow.Columns(3) = 5
End Sub
I need a small loop that cuts a row and inserts it at the row 2 for every worksheet.
The entire row should be detected or selected by only selecting a single cell in the first column.
I've tried adding a simple for each to the code (see below) but it end up sorting the every row by date.
Private Sub CommandButton2_Click()
For Each Worksheet In ThisWorkbook
Rows(ActiveCell.Row).Cut
Rows(2).Insert Shift:=xlDown
Next
End Sub
It should move the entire row, in every worksheet, to the top of the list by only selecting a cell and pressing the commandbutton in the first worksheet.
The entire row should be detected or selected by only selecting a single cell in the first column.
If someone could also explain how to work with Activevell and a loop through every worksheet that would be nice as well.
Well the bare minimum of code you are trying to use would look like:
Private Sub CommandButton2_Click()
For Each ws In ThisWorkbook.Sheets
ws.Rows(ActiveCell.Row).Cut
ws.Rows(2).Insert Shift:=xlDown
Next ws
End Sub
This would loop through all elements (sheets) in the sheets collection of the current workbook. Activecell is a reference to the focussed cell currently displayed on the active sheet, the sheet as where you press the commandbutton (assumed) as per your statement:
"It should move the entire row, in every worksheet, to the top of the list by only selecting a cell and pressing the commandbutton in the first worksheet."
I am not a big fan of For Each loop, that's why I'm using just for next loop :)
I think it should be useful :)
Private Sub CommandButton2_Click()
Dim numb As Integer
Dim i As Integer
Dim pos As Integer
numb = Application.Worksheets.Count
For i = 1 To numb
With Sheets(i)
.Select
pos = ActiveCell.Row
.Rows(pos).Cut
.Rows(2).Insert shift:=xlDown
End With
Next i
End Sub
If You still need answer for your last sentence add a comment, hope it helps :)
I'm 15 and I'm doing a Internship as a Developer and I've got a kinda hard exercise.
I have a Table with 3 columns, A is "Number" B is "percent" and C is "Value". The column "value" is blank and I Need to calculate the value with a macro button. I've tried this, but it was wrong because I didnĀ“t calculate it in VBA:
Public Sub PushButton ()
Range("C2:C11").Formula = "=A2*B2/100"
Range("C2:C11").Value = Range("C1:C6).Value
End Sub
How do I solve this?
You are using a defined range, you could do it with a dynamic range like this:
Option Explicit
Sub PushButton()
Dim i As Long, LastRow As Long
With ThisWorkbook.ActiveSheet
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row 'first you need to find the last row on the active sheet
For i = 2 To LastRow 'then iterate through all the rows starting from 2, if row 1 has headers
.Cells(i, 3) = .Cells(i, 1) * .Cells(i, 2) / 100
Next i
End With
End Sub
If you need help understanding this code, let me know.
Edit: Explanation
Well, the first thing you must do is Dimension all your variables, and to help that you can use the Option Explicitright above all your code.
I've dimensioned 1 variable for the loop and another one to find the last row with text.
To find the last row what you are actually doing is going to excel, select the last row (1048576) and the column where it will have text, in this case 1 or column "A" and then pushing ctrl+Up excel and vba will get you to the last cell with text.
To do that you use Cells(Row, column) instead of manually inserting row 1048576 you can just use rows.count and it will be the same.
Once you get the last row you just iterate with a For iloop meaning For a variable called i which equals 2 (For i = 2) To LastRow (to the last row you calculated) VBA will repeat the code in between the ForAnd Next adding 1 number to i everytime the loop restarts.
In this case is just adding a number to the rows on Cells(i, 3) so you can modify that cell depending of its i value.
I think you need to question why Excel needs to calculate on demand rather than automatically like normal. Failing that there are a few options
You could change your calculation method to Manual using the following in the ThisWorkbook object
Option Explicit
Dim xlCalcMethod As XlCalculation
Private Sub Workbook_Open()
With Application
' Store users current method for when closing the workbook
xlCalcMethod = .Calculation
.Calculation = xlCalculationManual
End With
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
' Reset calculation
Application.Calculation = xlCalcMethod
End Sub
and then when the button is pressed use the following code to calculate placed in a Module
Option Explicit
Public Sub Button_Click()
Application.Calculate
End Sub
Another option to do this without looping would be:
Sub CalculateRange()
Dim rng As Range
' Update for your Range
With ActiveSheet
Set rng = .Range("C2:C" & .Cells(.Rows.Count, "A").End(xlUp).Row)
End With
rng.Value2 = Evaluate(rng.Offset(0, -2).Address & "*" & rng.Offset(0, -1).Address & "/100")
End Sub
Finally, the way you've come up with is perfectly acceptable as VBA
I have an issue which i can't solve.I wrote this code:
Private Sub CommandButton2_Click()
Sheets("sheet2").OLEObjects("CheckBox1").Copy
Sheets("sheet3").Range("V7").PasteSpecial
End Sub
This code copy a checkbox from (sheet 2) to (sheet 3) starting from V7 cell. Now I want the next time I press the command button to paste the data to cell V12,next time to V17 etc. My vba knowledge is not very good as you can see.
This code will see how many checkboxes are already in the sheet you are pasting to and add 5 rows for each check box, then paste five rows under the last one.
Private Sub CommandButton2_Click()
' copy checkbox
Sheets("sheet2").OLEObjects("CheckBox1").Copy
Dim wks As Worksheet
Set wks = Sheets("Sheet3")
Dim cb As OLEObject, i As Integer
'determine how many boxes are already there and get count of cell to paste to
i = 7
For Each cb In wks.OLEObjects
If InStr(1, cb.Name, "CheckBox") Then i = i + 5
Next
'paste new checkbox
Sheets("sheet3").Range("V" & i).PasteSpecial
End Sub
Use a global variable. These must be at the top of your sheet, module, or form code above all subs and functions.
Then use that as the row number in your range. Range("V" & lRow)
Private lRow As Long
Private Sub CommandButton2_Click()
'Let's check if this is the first time the button has been used.
If lRow = 0 then
lRow = 7
Else
'Increment the row from the one we wrote to last time.
lRow = lRow + 5
End If
'Do the copy
Sheets("sheet2").OLEObjects("CheckBox1").Copy
Sheets("sheet3").Range("V" & lRow).PasteSpecial
End Sub
I dont know what data you got between in Sheet(3).Range("V7") and Sheet(3).Range("V12")
but juste before you're PasteSpecial, you shoud keep track where was the last time you paste data in Sheets("sheets3") in a specific cell in Sheet("sheets3"), in exemple : Sheets("Sheet3").Range("A1")
Then you'll be able to pastespecial to this cell 5 row under like this :
Sheets("sheet3").Range(Sheets("Sheets3").Range("A1").Offset(5,0)).PasteSpecial
right after that you update the Sheets("Sheets3").Range("A1") = Sheets("sheet3").Range(Sheets("Sheets3").Range("A1").Offset(5,0)).Address
So this should do the work :
Private Sub CommandButton2_Click()
Dim oWsSource as Worksheet
Dim oWsDestination as Worksheet
Set oWsDestination = ThisWorkbook.Worksheet("Sheets3")
Set oWsSource = ThisWorkbook.Worksheet("Sheets2")
'Do the copy
oWsSource.OLEObjects("CheckBox1").Copy
oWsDestination.Range(oWsDestination.Range("A1").Value).Offset(5,0)).PasteSpecial
oWsDestination.Range("A1").Value = oWsDestination.Range(oWsDestination.Range("A1").Value).Offset(5, 0)).Address
End Sub
All the answers put the first checkbox but the next one put it again to the same cell as before.I don't know if its matter but I use excel 2010.