Macro basics

D

Dallman Ross

I'm trying to develop some macros. I'm not that versed
at it yet. Here is part of something:

Dim iLastCol As Long

With Worksheets("2006 Realized Gains")
Cells.Select
Selection.EntireColumn.Hidden = False
.AutoFilterMode = False

iLastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column

Application.Goto Reference:= _
"INDEX(R1:R65536,COUNTA(C[1])-1,30)"

See that "30" in the last line? That's Column 30 ("AD"). I
tried to use my "iLastCol" variable instead, but could
not. I tried all sorts of permutations; still no luck.
What's the trick?

Seconds question: How can I save the Application.Goto Reference
for repeat use without inserting that statement again each time?

E.g., later I will want to select the row below that
row. How would I do that?

Thanks for any help.
Dallman Ross
 
D

Dave Peterson

Dim iLastCol As Long
dim iLastRow as long

With Worksheets("2006 Realized Gains")
'you have to select the sheet before you can select a range
'but you don't have to select either to get your code to work
'.select
'.Cells.Select
.cells.EntireColumn.Hidden = False
.AutoFilterMode = False

iLastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
iLastRow = .cells(.rows.count,"A").end(xlup).row

application.goto _
reference:=.cells(ilastrow + 1, ilastcol) ',scroll:=true

end with

I used column A to find the nextrow (iLastRow + 1) to go to.

I don't understand the second question.

Dallman said:
I'm trying to develop some macros. I'm not that versed
at it yet. Here is part of something:

Dim iLastCol As Long

With Worksheets("2006 Realized Gains")
Cells.Select
Selection.EntireColumn.Hidden = False
.AutoFilterMode = False

iLastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column

Application.Goto Reference:= _
"INDEX(R1:R65536,COUNTA(C[1])-1,30)"

See that "30" in the last line? That's Column 30 ("AD"). I
tried to use my "iLastCol" variable instead, but could
not. I tried all sorts of permutations; still no luck.
What's the trick?

Seconds question: How can I save the Application.Goto Reference
for repeat use without inserting that statement again each time?

E.g., later I will want to select the row below that
row. How would I do that?

Thanks for any help.
Dallman Ross
 
D

Dallman Ross

Dave Peterson said:
Dim iLastCol As Long
dim iLastRow as long

With Worksheets("2006 Realized Gains")
'you have to select the sheet before you can select a range
'but you don't have to select either to get your code to work
'.select
'.Cells.Select
.cells.EntireColumn.Hidden = False
.AutoFilterMode = False

iLastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
iLastRow = .cells(.rows.count,"A").end(xlup).row

application.goto _
reference:=.cells(ilastrow + 1, ilastcol) ',scroll:=true

end with

Thanks, Dave! That's much better.
A couple more questions: I used "Long" as the var type, and I
chose that because you'd done so earlier (in another thread where
you helped me). But why are we doing that? Why not Integer type?
I used column A to find the nextrow (iLastRow + 1) to go to.

Okay. I don't know if I need the application.goto stuff or not,
but after lots of experimentation recording macros and inspecting
the code and trying to bend it to my needs, I'd come up with that.
Actually, I took the "+ 1" back out, because my next line
in my macros is:

Range(Selection, "A1").Sort _
Key1:=Range("A2"), Order1:=xlAscending, Header:=xlYes

Maybe there's a better way to invoke that range for the sort.
I don't understand the second question.

The second question was how I can keep the value of the
goto reference so I don't have to repeat

application.goto _
reference:=.cells(ilastrow, ilastcol)

a bunch of times when I do other things later in the macro.

Probably if I knew more I wouldn't be using goto anyway,
so the question wouldn't be needed. But I don't know
other ways to set ranges for actions yet.

The next thing I want to do, for example, is move the row
defined as "iLastRow + 1" down X number of lines, where
X is determied by the number of rows in another sheet that
I've just refreshed data for. (The row I'm moving is a
row of totals and other calculated fields.)

(Then I'll want to fill the rows I've just inserted down to
the new location of my bottom, totals, row.)

==============================================================
Dallman said:
I'm trying to develop some macros. I'm not that versed
at it yet. Here is part of something:

Dim iLastCol As Long

With Worksheets("2006 Realized Gains")
Cells.Select
Selection.EntireColumn.Hidden = False
.AutoFilterMode = False

iLastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column

Application.Goto Reference:= _
"INDEX(R1:R65536,COUNTA(C[1])-1,30)"

See that "30" in the last line? That's Column 30 ("AD"). I
tried to use my "iLastCol" variable instead, but could
not. I tried all sorts of permutations; still no luck.
What's the trick?

Seconds question: How can I save the Application.Goto Reference
for repeat use without inserting that statement again each time?

E.g., later I will want to select the row below that
row. How would I do that?

Thanks for any help.
Dallman Ross
 
D

Dave Peterson

Longs can go from -2,147,483,648 to 2,147,483,647.
Integers can go from -32,768 to 32,767

Since there are 64k rows in a worksheet, using Integer could cause trouble if
you exceed 32,767 rows. And on top of that (from what I've read), modern
computers will convert Integers to Longs before they work with them. So why
waste their time!

Instead of selecting the cells (using .select or application.goto), you can work
directly with the range.

For instance:

dim iLastCol as long
dim iLastRow as long
dim myRng as range

with worksheets("sheet9999")
ilastcol = .cells(1,.columns.count).end(xltoleft).column
ilastrow = .cells(.rows.count,"A").end(xlup).row

set myrng = .range("a1",.cells(ilastrow,ilastcol)
end with

Then you can do what you want to that range

with myrng
.sort key1:=.columns(2), order1:=xlascending, header:=xlyes
end with

..columns(2) belongs to myrng. It's the second column within that range--maybe
not the second column in the worksheet (in this sample, it is, but it doesn't
have to be).

I'm not sure I'd keep track of any variable (sometimes, I do, though).

I like to just come back and figure it out again:

ilastrow = .cells(.rows.count,"A").end(xlup).row

If I've done something that increase/decreased the number of rows in that
worksheet.


Dallman said:
Dave Peterson said:
Dim iLastCol As Long
dim iLastRow as long

With Worksheets("2006 Realized Gains")
'you have to select the sheet before you can select a range
'but you don't have to select either to get your code to work
'.select
'.Cells.Select
.cells.EntireColumn.Hidden = False
.AutoFilterMode = False

iLastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
iLastRow = .cells(.rows.count,"A").end(xlup).row

application.goto _
reference:=.cells(ilastrow + 1, ilastcol) ',scroll:=true

end with

Thanks, Dave! That's much better.
A couple more questions: I used "Long" as the var type, and I
chose that because you'd done so earlier (in another thread where
you helped me). But why are we doing that? Why not Integer type?
I used column A to find the nextrow (iLastRow + 1) to go to.

Okay. I don't know if I need the application.goto stuff or not,
but after lots of experimentation recording macros and inspecting
the code and trying to bend it to my needs, I'd come up with that.
Actually, I took the "+ 1" back out, because my next line
in my macros is:

Range(Selection, "A1").Sort _
Key1:=Range("A2"), Order1:=xlAscending, Header:=xlYes

Maybe there's a better way to invoke that range for the sort.
I don't understand the second question.

The second question was how I can keep the value of the
goto reference so I don't have to repeat

application.goto _
reference:=.cells(ilastrow, ilastcol)

a bunch of times when I do other things later in the macro.

Probably if I knew more I wouldn't be using goto anyway,
so the question wouldn't be needed. But I don't know
other ways to set ranges for actions yet.

The next thing I want to do, for example, is move the row
defined as "iLastRow + 1" down X number of lines, where
X is determied by the number of rows in another sheet that
I've just refreshed data for. (The row I'm moving is a
row of totals and other calculated fields.)

(Then I'll want to fill the rows I've just inserted down to
the new location of my bottom, totals, row.)

==============================================================
Dallman said:
I'm trying to develop some macros. I'm not that versed
at it yet. Here is part of something:

Dim iLastCol As Long

With Worksheets("2006 Realized Gains")
Cells.Select
Selection.EntireColumn.Hidden = False
.AutoFilterMode = False

iLastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column

Application.Goto Reference:= _
"INDEX(R1:R65536,COUNTA(C[1])-1,30)"

See that "30" in the last line? That's Column 30 ("AD"). I
tried to use my "iLastCol" variable instead, but could
not. I tried all sorts of permutations; still no luck.
What's the trick?

Seconds question: How can I save the Application.Goto Reference
for repeat use without inserting that statement again each time?

E.g., later I will want to select the row below that
row. How would I do that?

Thanks for any help.
Dallman Ross
 
D

Dallman Ross

Dave Peterson said:
Longs can go from -2,147,483,648 to 2,147,483,647.
Integers can go from -32,768 to 32,767

This is good stuff you're feeding me. Tks. :)
Since there are 64k rows in a worksheet, using Integer could
cause trouble if you exceed 32,767 rows. And on top of that
(from what I've read), modern computers will convert Integers to
Longs before they work with them. So why waste their time!

Okay, I see.

Instead of selecting the cells (using .select or
application.goto), you can work directly with the range.

For instance:

dim iLastCol as long
dim iLastRow as long
dim myRng as range
Oh!


with worksheets("sheet9999")
ilastcol = .cells(1,.columns.count).end(xltoleft).column
ilastrow = .cells(.rows.count,"A").end(xlup).row

set myrng = .range("a1",.cells(ilastrow,ilastcol)
end with

Yup, that rocks! (Needed a matching closed paren at the end of the
"set myrng" line, but I figured that out quickly.)

I notice it the code doesn't give me a blatant error if I
don't use "set" -- though I didn't try to run it without.
But how do I know when to use set and when not to?


I'm also getting confused about when to invoke a "with" and when
to end it. E.g., in my macro I unhide columns before the
sort (to make sure Col. A. was sorted, as it is normally
hidden). Is that inside the 'With worksheets("name")' ?

Then you can do what you want to that range

with myrng
.sort key1:=.columns(2), order1:=xlascending, header:=xlyes
end with

Good. "Key1:=.rows(2)", of course, but I know you're typing fast. :)
That puzzled me for a moment, but I decided to risk trying to run it
with ".columns(2)" and guessed it wouldn't do anything horrible to
my data, which it didn't. But that did tell me I wanted .rows, since
nothing much happened with .columns, and they didn't make sense to
me there anyway. :) :)

I generally back up my stuff before changes, of course, but still . . .
.columns(2) belongs to myrng. It's the second column within that
range--maybe not the second column in the worksheet (in this
sample, it is, but it doesn't have to be).
I'm not sure I'd keep track of any variable (sometimes, I do, though).

Well, if you know you're through with one, do you bother to free
up env space?
I like to just come back and figure it out again:

ilastrow = .cells(.rows.count,"A").end(xlup).row

If I've done something that increase/decreased the number of rows in that
worksheet.

Okay.

In any case, I'm working fine so far. Now I refresh the data source
on another sheet in this workbook. (My macro does that, but it
stops and asks me to confirm the name. I don't know how to get it
not to ask me. So that's one more question.)

The next thing that happens is, the sheet we've been working on
spits at me in the totals row over what is now a circular reference.
That's because there are named ranges in use referring to the
data sheet I've just refreshed and made longer, so the totals
row now finds itself in the middle of the data it's totalling. Oops.

So I need to tell the macro to shut up already about circular refs,
because I'm about to move the totals row to the new end and fill down
the new cells I'll insert. More coding help will be very much
appreciated!

====================================================================
Dallman said:
Dave Peterson said:
Dim iLastCol As Long
dim iLastRow as long

With Worksheets("2006 Realized Gains")
'you have to select the sheet before you can select a range
'but you don't have to select either to get your code to work
'.select
'.Cells.Select
.cells.EntireColumn.Hidden = False
.AutoFilterMode = False

iLastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
iLastRow = .cells(.rows.count,"A").end(xlup).row

application.goto _
reference:=.cells(ilastrow + 1, ilastcol) ',scroll:=true

end with

Thanks, Dave! That's much better.
A couple more questions: I used "Long" as the var type, and I
chose that because you'd done so earlier (in another thread where
you helped me). But why are we doing that? Why not Integer type?
I used column A to find the nextrow (iLastRow + 1) to go to.

Okay. I don't know if I need the application.goto stuff or not,
but after lots of experimentation recording macros and inspecting
the code and trying to bend it to my needs, I'd come up with that.
Actually, I took the "+ 1" back out, because my next line
in my macros is:

Range(Selection, "A1").Sort _
Key1:=Range("A2"), Order1:=xlAscending, Header:=xlYes

Maybe there's a better way to invoke that range for the sort.
I don't understand the second question.

The second question was how I can keep the value of the
goto reference so I don't have to repeat

application.goto _
reference:=.cells(ilastrow, ilastcol)

a bunch of times when I do other things later in the macro.

Probably if I knew more I wouldn't be using goto anyway,
so the question wouldn't be needed. But I don't know
other ways to set ranges for actions yet.

The next thing I want to do, for example, is move the row
defined as "iLastRow + 1" down X number of lines, where
X is determied by the number of rows in another sheet that
I've just refreshed data for. (The row I'm moving is a
row of totals and other calculated fields.)

(Then I'll want to fill the rows I've just inserted down to
the new location of my bottom, totals, row.)

==============================================================
Dallman Ross wrote:

I'm trying to develop some macros. I'm not that versed
at it yet. Here is part of something:

Dim iLastCol As Long

With Worksheets("2006 Realized Gains")
Cells.Select
Selection.EntireColumn.Hidden = False
.AutoFilterMode = False

iLastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column

Application.Goto Reference:= _
"INDEX(R1:R65536,COUNTA(C[1])-1,30)"

See that "30" in the last line? That's Column 30 ("AD"). I
tried to use my "iLastCol" variable instead, but could
not. I tried all sorts of permutations; still no luck.
What's the trick?

Seconds question: How can I save the Application.Goto Reference
for repeat use without inserting that statement again each time?

E.g., later I will want to select the row below that
row. How would I do that?

Thanks for any help.
Dallman Ross
 
D

Dave Peterson

Sorry about missing the closing paren.

But you use Set when you want to make an assignment to an object variable.

Dim Wks as worksheet
set wks = worksheets(...)

dim wkbk as workbook
set wkbk = workbooks(...)

dim pt as pivottable
set pt = activesheet.pivottables(...)

These objects have lots of properties that can be addressed.
wks.name, wks.range(...), wks.protectcontents and lots more stuff.

If the variable represents something simple (no properties), then you don't use
the Set

Dim myVal as long or string or boolean or...

myval = 1234
myval = "qwer"
myval = false

=====================
In my sort sample, I did want key1:=.columns(2). This meant that my column to
sort by was column 2 of that range (the dot (.) said that columns(2) belonged to
the previous With statement. It wasn't an error.

=====================

I like typing:

with worksheets("sheet999")
set myRng = .range(.cells(1,1),.cells(ilastrow, ilastcol))
end with

But if you like you can qualify each range reference:

set myrng = worksheets("sheet999").range(worksheets("sheet999").cells(1,1), _
worksheets("sheet999").cells(ilastrow, ilastcol))

(watch out for typos. I didn't count my ()'s.

I think it makes the code easier to read and understand (and type!).

=========
If I've ever bothered freeing any variable space, it was a mistake! Excel/VBA
will release any memory used when the procedure ends running. (There are some
bugs that cause a memory leak, but I don't worry about them either. You can
google "memory leaks" if you're really interested.

And if you automate excel from another application, you'll want to release your
object variables:

set wkbk = nothing
set wks = nothing
set xlapp = nothing

But that's a different subject.

==========

I'm not sure what you're refreshing that causes the warning, but lots of times,
you can suppress warnings by:

application.displayalerts = false
'your code that causes the warning message here
application.displayalerts = true

Another option would be to remove your subtotals, add your rows, and reinsert
the subtotal rows.

Or insert your new data above the subtotal rows.

David McRitchie has some notes about not having to adjust formulas when
additional rows are inserted:

http://www.mvps.org/dmcritchie/excel/insrtrow.htm#offset

Dallman said:
Dave Peterson said:
Longs can go from -2,147,483,648 to 2,147,483,647.
Integers can go from -32,768 to 32,767

This is good stuff you're feeding me. Tks. :)
Since there are 64k rows in a worksheet, using Integer could
cause trouble if you exceed 32,767 rows. And on top of that
(from what I've read), modern computers will convert Integers to
Longs before they work with them. So why waste their time!

Okay, I see.
Instead of selecting the cells (using .select or
application.goto), you can work directly with the range.

For instance:

dim iLastCol as long
dim iLastRow as long
dim myRng as range
Oh!


with worksheets("sheet9999")
ilastcol = .cells(1,.columns.count).end(xltoleft).column
ilastrow = .cells(.rows.count,"A").end(xlup).row

set myrng = .range("a1",.cells(ilastrow,ilastcol)
end with

Yup, that rocks! (Needed a matching closed paren at the end of the
"set myrng" line, but I figured that out quickly.)

I notice it the code doesn't give me a blatant error if I
don't use "set" -- though I didn't try to run it without.
But how do I know when to use set and when not to?

I'm also getting confused about when to invoke a "with" and when
to end it. E.g., in my macro I unhide columns before the
sort (to make sure Col. A. was sorted, as it is normally
hidden). Is that inside the 'With worksheets("name")' ?
Then you can do what you want to that range

with myrng
.sort key1:=.columns(2), order1:=xlascending, header:=xlyes
end with

Good. "Key1:=.rows(2)", of course, but I know you're typing fast. :)
That puzzled me for a moment, but I decided to risk trying to run it
with ".columns(2)" and guessed it wouldn't do anything horrible to
my data, which it didn't. But that did tell me I wanted .rows, since
nothing much happened with .columns, and they didn't make sense to
me there anyway. :) :)

I generally back up my stuff before changes, of course, but still . . .
.columns(2) belongs to myrng. It's the second column within that
range--maybe not the second column in the worksheet (in this
sample, it is, but it doesn't have to be).
I'm not sure I'd keep track of any variable (sometimes, I do, though).

Well, if you know you're through with one, do you bother to free
up env space?
I like to just come back and figure it out again:

ilastrow = .cells(.rows.count,"A").end(xlup).row

If I've done something that increase/decreased the number of rows in that
worksheet.

Okay.

In any case, I'm working fine so far. Now I refresh the data source
on another sheet in this workbook. (My macro does that, but it
stops and asks me to confirm the name. I don't know how to get it
not to ask me. So that's one more question.)

The next thing that happens is, the sheet we've been working on
spits at me in the totals row over what is now a circular reference.
That's because there are named ranges in use referring to the
data sheet I've just refreshed and made longer, so the totals
row now finds itself in the middle of the data it's totalling. Oops.

So I need to tell the macro to shut up already about circular refs,
because I'm about to move the totals row to the new end and fill down
the new cells I'll insert. More coding help will be very much
appreciated!

====================================================================
Dallman said:
In <[email protected]>, Dave Peterson
<[email protected]> helped me out by fixing up my code some:

Dim iLastCol As Long
dim iLastRow as long

With Worksheets("2006 Realized Gains")
'you have to select the sheet before you can select a range
'but you don't have to select either to get your code to work
'.select
'.Cells.Select
.cells.EntireColumn.Hidden = False
.AutoFilterMode = False

iLastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
iLastRow = .cells(.rows.count,"A").end(xlup).row

application.goto _
reference:=.cells(ilastrow + 1, ilastcol) ',scroll:=true

end with

Thanks, Dave! That's much better.
A couple more questions: I used "Long" as the var type, and I
chose that because you'd done so earlier (in another thread where
you helped me). But why are we doing that? Why not Integer type?

I used column A to find the nextrow (iLastRow + 1) to go to.

Okay. I don't know if I need the application.goto stuff or not,
but after lots of experimentation recording macros and inspecting
the code and trying to bend it to my needs, I'd come up with that.
Actually, I took the "+ 1" back out, because my next line
in my macros is:

Range(Selection, "A1").Sort _
Key1:=Range("A2"), Order1:=xlAscending, Header:=xlYes

Maybe there's a better way to invoke that range for the sort.

I don't understand the second question.

The second question was how I can keep the value of the
goto reference so I don't have to repeat

application.goto _
reference:=.cells(ilastrow, ilastcol)

a bunch of times when I do other things later in the macro.

Probably if I knew more I wouldn't be using goto anyway,
so the question wouldn't be needed. But I don't know
other ways to set ranges for actions yet.

The next thing I want to do, for example, is move the row
defined as "iLastRow + 1" down X number of lines, where
X is determied by the number of rows in another sheet that
I've just refreshed data for. (The row I'm moving is a
row of totals and other calculated fields.)

(Then I'll want to fill the rows I've just inserted down to
the new location of my bottom, totals, row.)

==============================================================
Dallman Ross wrote:

I'm trying to develop some macros. I'm not that versed
at it yet. Here is part of something:

Dim iLastCol As Long

With Worksheets("2006 Realized Gains")
Cells.Select
Selection.EntireColumn.Hidden = False
.AutoFilterMode = False

iLastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column

Application.Goto Reference:= _
"INDEX(R1:R65536,COUNTA(C[1])-1,30)"

See that "30" in the last line? That's Column 30 ("AD"). I
tried to use my "iLastCol" variable instead, but could
not. I tried all sorts of permutations; still no luck.
What's the trick?

Seconds question: How can I save the Application.Goto Reference
for repeat use without inserting that statement again each time?

E.g., later I will want to select the row below that
row. How would I do that?

Thanks for any help.
Dallman Ross
 
D

Dallman Ross

Dave Peterson said:
But you use Set when you want to make an assignment to an object
variable.

Dim Wks as worksheet
set wks = worksheets(...)

Ah! Gotcha, thanks.
=====================
In my sort sample, I did want key1:=.columns(2). This meant that
my column to sort by was column 2 of that range (the dot (.) said
that columns(2) belonged to the previous With statement. It
wasn't an error.

Okay. I'm not yet clear on something about it. I guess it's because
I was converting to your syntax from my

Range(Selection, "A1").Sort _
Key1:=Range("A2"), Order1:=xlAscending, Header:=xlYes

So I'm sorting based on Column 1. Okay, I'll change it to:

With myRng
.Sort key1:=.Columns(1), order1:=xlAscending, header:=xlYes
End With

Yes, I just tried it, and that works fine. Sorry for being dense!

=====================
I like typing:

with worksheets("sheet999")
set myRng = .range(.cells(1,1),.cells(ilastrow, ilastcol))
end with

But if you like you can qualify each range reference:

set myrng = worksheets("sheet999").range(worksheets("sheet999").cells(1,1), _
worksheets("sheet999").cells(ilastrow, ilastcol))


Okay, I see what you like about it. I agree that it's cleaner.
==========
I'm not sure what you're refreshing that causes the warning, but
lots of times, you can suppress warnings by:

application.displayalerts = false
'your code that causes the warning message here
application.displayalerts = true

I'll see how that works. I'm painstakingly building this macro
line-by-line, and I'm not quite there yet. I had a macro earlier
that worked, but I've completely changed my sheet around since
then -- and that macro was not at all optimal, anyway.
Another option would be to remove your subtotals, add your rows,
and reinsert the subtotal rows.

Well, it's formatted a certain way and has some complex calculated
fields, not just totals, so I'd rather not use that approach. I
also could temporarily move it to Row 1, then move it back. If I
do, I think I'll want to turn off the display refresh during that
part of the macro, because that will grate on my sensibilities for
some reason I can't quite put into words. I suppose the reason
is that I'm a purist at heart, but I know too little about what
I'm doing with VBA to be able to live up to my high demands
of myself for creating robust and reusable code.
Or insert your new data above the subtotal rows.

The problem is, I don't know how long the new data is until I refresh
that sheet's data source. The source is a CSV file, so I can't
use fancy VBA code to inspect it first, either.
David McRitchie has some notes about not having to adjust
formulas when additional rows are inserted:

http://www.mvps.org/dmcritchie/excel/insrtrow.htm#offset

Bookmarked! Thanks again.

Dallman Ross

[Prior stuff deleted]
 
D

Dave Peterson

I've used this technique when I import files.

I get the data and put it in row 3. I add headers to row 2. I add autofilters
to that range (A2:X9999). But I put all my subtotals in row 1. (and set rows
to repeat at top to 1:2 and freeze panes so that rows 1 and 2 are always
visible).

I find it much easier and even more useful to have the subtotals at the top. If
I use =subtotal()'s in my formulas, they react to the changes in my
autofiltering. And I don't have to scroll through the worksheet to find those
numbers.

Another approach.

Drop the subtotals completely and use data|pivottable to create a nice summary
table. (Or use both the subtotals at the top and pivottables.)

Dallman said:
Dave Peterson said:
But you use Set when you want to make an assignment to an object
variable.

Dim Wks as worksheet
set wks = worksheets(...)

Ah! Gotcha, thanks.
=====================
In my sort sample, I did want key1:=.columns(2). This meant that
my column to sort by was column 2 of that range (the dot (.) said
that columns(2) belonged to the previous With statement. It
wasn't an error.

Okay. I'm not yet clear on something about it. I guess it's because
I was converting to your syntax from my

Range(Selection, "A1").Sort _
Key1:=Range("A2"), Order1:=xlAscending, Header:=xlYes

So I'm sorting based on Column 1. Okay, I'll change it to:

With myRng
.Sort key1:=.Columns(1), order1:=xlAscending, header:=xlYes
End With

Yes, I just tried it, and that works fine. Sorry for being dense!
=====================
I like typing:

with worksheets("sheet999")
set myRng = .range(.cells(1,1),.cells(ilastrow, ilastcol))
end with

But if you like you can qualify each range reference:

set myrng = worksheets("sheet999").range(worksheets("sheet999").cells(1,1), _
worksheets("sheet999").cells(ilastrow, ilastcol))

Okay, I see what you like about it. I agree that it's cleaner.
==========
I'm not sure what you're refreshing that causes the warning, but
lots of times, you can suppress warnings by:

application.displayalerts = false
'your code that causes the warning message here
application.displayalerts = true

I'll see how that works. I'm painstakingly building this macro
line-by-line, and I'm not quite there yet. I had a macro earlier
that worked, but I've completely changed my sheet around since
then -- and that macro was not at all optimal, anyway.
Another option would be to remove your subtotals, add your rows,
and reinsert the subtotal rows.

Well, it's formatted a certain way and has some complex calculated
fields, not just totals, so I'd rather not use that approach. I
also could temporarily move it to Row 1, then move it back. If I
do, I think I'll want to turn off the display refresh during that
part of the macro, because that will grate on my sensibilities for
some reason I can't quite put into words. I suppose the reason
is that I'm a purist at heart, but I know too little about what
I'm doing with VBA to be able to live up to my high demands
of myself for creating robust and reusable code.
Or insert your new data above the subtotal rows.

The problem is, I don't know how long the new data is until I refresh
that sheet's data source. The source is a CSV file, so I can't
use fancy VBA code to inspect it first, either.
David McRitchie has some notes about not having to adjust
formulas when additional rows are inserted:

http://www.mvps.org/dmcritchie/excel/insrtrow.htm#offset

Bookmarked! Thanks again.

Dallman Ross

[Prior stuff deleted]
 
D

Dallman Ross

Dave Peterson said:
I've used this technique when I import files.

I get the data and put it in row 3. I add headers to row 2.
I add autofilters to that range (A2:X9999). But I put all my
subtotals in row 1. (and set rows to repeat at top to 1:2 and
freeze panes so that rows 1 and 2 are always visible).

I've thought about that, based on something someone else said last
week, but I haven't yet succeeded in weaning myself from where they
are now. I like them there! Oh, well.

As for pivot tables, I haven't learned enough about them to get
that to work for my needs. It's something I want to get to. But
meanwhile, I have what I want; I just want to automate the
refresh.

Speaking of auto-filters, my plan with my macro is to turn them off --
I listed the code where I did that -- then do my various refreshes,
etc., then turn them on. But maybe I should just leave them on but
disable them. Not sure how to do that or if it makes much difference.

My latest question is, how do I do a loop with two sheetnames.
The two are:

myCSV = "2006 Realized - CSV Data"
and
myCSV = "Current - CSV Data"


And what I'll do twice is:

With Worksheets(myCSV)
'ActiveSheet.Visible = True
ActiveSheet.Unprotect

Selection.QueryTable.Refresh BackgroundQuery:=False

ActiveSheet.Protect DrawingObjects:=True, _
Contents:=True, Scenarios:=True

'ActiveSheet.Visible = False
End With



Also, when I run that code above, the macro stops and waits
me to hit the Enter key to accept the suggested name for the
query refresh. I don't know why. (And I just got the
circular-reference warning trying my half-complete macro
up to this point. I'll have to try that warning-shut-off
syntax you suggested might help.)

Thanks for letting me pick your brain, Dave!

Dallman Ross
 
D

Dave Peterson

Maybe just doing the equivalent of Data|ShowAll would be sufficient.

And you could use:

dim iCtr as long
dim wksNames as variant
wksnames = array(""2006 Realized - CSV Data", "Current - CSV Data")

for ictr = lbound(wksnames) to ubound(wksnames)
with worksheets(wksnames(ictr))
'do a bunch of stuff
end with
next ictr
 
D

Dallman Ross

Dave Peterson said:
Maybe just doing the equivalent of Data|ShowAll would be sufficient.

Okay, that's promising, but if all is already displayed, I get
an error. So I need an if-statement. Don't know how to formulate
it. Hoping you'll show me.
And you could use:

dim iCtr as long
dim wksNames as variant
wksnames = array(""2006 Realized - CSV Data", "Current - CSV Data")

for ictr = lbound(wksnames) to ubound(wksnames)
with worksheets(wksnames(ictr))
'do a bunch of stuff
end with
next ictr

Yes, that's excellent. It works well, once I figured out a
couple of critical things. First, I didn't know about

.Activate

but lucked out in finding it in the VBA Help pages when I
couldn't figure out why my directive to unprotect the sheet
wasn't working. Second, my query refresh wouldn't work and
caused a debug error once I inserted it into this "do stuff"
part of the loop. I flailed around for a while with Google
and help pages to no avail. When I was about to give up,
I finally tried this more or less by accident from the help
pages:

.QueryTables(1).Refresh BackgroundQuery:=False

and, lo! it worked. (What I'd ever want instead of (1),
I couldn't tell you.) :)

N.B.: That was instead of this, which was suddenly barfing:
Selection.QueryTable.Refresh BackgroundQuery:=False

The macro still stops at the refresh and waits for me to
hit the Enter key to accept the name of the data source
that's correctly displayed in the pop-up window.
I would like it if it didn't wait for me to pound on the
Enter key. But that is not critical.

I suppose I also wouldn't mind a "do while" thing with the
variant instead of setting a counter, just as a matter
of style and to build my burgeoning macro know-how.

Okay, this thing is really starting to get somewhere!
Thanks, Dave.

Dallman Ross
 
D

Dallman Ross

Dallman Ross <dman@localhost.> said:
Okay, that's promising, but if all is already displayed, I get an
error. So I need an if-statement. Don't know how to formulate
it. Hoping you'll show me.

I found this!

http://www.contextures.com/xlautofilter03.html

If ActiveSheet.FilterMode Then
ActiveSheet.ShowAllData
End If

I actually had tried something very similar, but it hand't worked.
I had left off "ActiveSheet" and started with the dots.

-dman-
 
D

Dallman Ross

Dave Peterson said:
It sounds like you're in business.
Good luck with the tweaks.

I've been tweaking away. Uncovered and fixed a couple of bugs.
Now I'm trying to do more with the loop that uses an array of
worksheet names.

My main question for this part of the thread is: can I, in
VBA, set variable using dynamic names?

To explain, I'll show where I'm at now, with stuff before and after
left off:

-----------------
Sub RGUpdate()

'stuff deleted

Set csvRG = Worksheets("2006 Realized - CSV Data")
Set csvUG = Worksheets("Current - CSV Data")

'stuff deleted

Dim iCtr As Long
Dim wsNames As Variant

wsNames = Array(csvRG, csvUG)
For iCtr = LBound(wsNames) To UBound(wsNames)
With wsNames(iCtr)

.Activate 'this turns out to be necessary
.Visible = True
.Unprotect
Range("A1").Select 'just a "focus" thing

'still wish I didn't have to hit Enter to accept this query:
.QueryTables(1).Refresh BackgroundQuery:=False

.Protect DrawingObjects:=True, _
Contents:=True, Scenarios:=True

'code I want to add with dynamic var naming:
'==> csvRGLastR = .Cells(.Rows.Count, "A").End(xlUp).Row
'^^^^^ {OR}
'==> csvUGLastR = .Cells(.Rows.Count, "A").End(xlUp).Row
'^^^^^ Those are strings, but based on the nicknames for the sheets!

.Visible = False

End With
Next iCtr

'stuff deleted

End Sub
-----------------

Okay, is anything like that possible?

Thanks,
-dman-
 
D

Dave Peterson

Nope.

But you could define another variable.

Option Explicit
Sub RGUpdate()

Dim LastRows As Variant
Dim iCtr As Long
Dim wsNames As Variant
Dim csvRG As Worksheet
Dim csvUG As Worksheet

'stuff deleted

Set csvRG = Worksheets("2006 Realized - CSV Data")
Set csvUG = Worksheets("Current - CSV Data")

'stuff deleted

wsNames = Array(csvRG, csvUG)
ReDim LastRows(LBound(wsNames) To UBound(wsNames))

For iCtr = LBound(wsNames) To UBound(wsNames)
With wsNames(iCtr)

.Activate 'this turns out to be necessary
.Visible = True
.Unprotect
Range("A1").Select 'just a "focus" thing

'still wish I didn't have to hit Enter to accept this query:
'.QueryTables(1).Refresh BackgroundQuery:=False

.Protect DrawingObjects:=True, _
Contents:=True, Scenarios:=True

'code I want to add with dynamic var naming:

LastRows(iCtr) = .Cells(.Rows.Count, "A").End(xlUp).Row

.Visible = False

End With
Next iCtr

'stuff deleted

End Sub
 
J

jsd219

Hi Dave, jsd219 here. i just saw that posted to here minutes ago so i
thought i would check inwith you. i am struggling with w script i am
trying to write and you wrote me something kinda similiar so i was
hoping you could help figure this out. it involves checking multiple
columns.

God bless
jsd219
 
D

Dallman Ross

Good stuff, Dave. That works out. (Had to figure out that the
values for iCtr in LastRows(iCtr) would be, in the case of my
meager two loops, 0 and 1, but that wasn't that hard to suss out.)

Speaking of figuring out, how to I tell VBA to print the
result to the screen so I can test things?

Also, what is the VBA equivalent for statements such
as "continue" or "break" in some other languages, e.g.,
for working with loops?

Dallman

========================
 
D

Dave Peterson

You can use
msgbox sometextvariablehere
or
debug.print sometextvariablehere

And to exit a loop, you can use "exit for" or "exit do".

I try not to use Continue (like in Fortran????). I just include the equivalent
of an if statement within the loop:

do
if something = false then
'do nothing
else
'do something
end if
if somethingelse = true then
exit do
end if
loop
 
D

Dave Peterson

The msgbox will popup a message that you'll have to dismiss.

The debug.print will appear in the immediate window of the VBE.
 
D

Dallman Ross

Dave Peterson said:
Dallman said:
[H]ow to I tell VBA to print the
result to the screen so I can test things?
You can use
msgbox sometextvariablehere
or
debug.print sometextvariablehere

Okay, I'll try these. I saw your further clarification as well.
Thanks much.

My question about break/continue, etc., isn't from the FORTRAN
world, though I did take a FORTRAN class in college in 1980.
(I can't remember a thing about it.) But I am a Unix scripter.
So I have quite a fair bit of experience with syntax from,
e.g., the Bourne shell under Unix.


By the way, in you sample code snippet you put in declarations:


I have a question about it. I actually had that originally,
then took out the Dim statements because in testing I found it
worked fine without them and with just the Sets. You put
them back. So I, also, put them back. But how come it
works without them?


Now I've started to make another loop higher up in the macro.
I've run into trouble and need help to get it working.

Previously, we had "Dim whatever As Range"; but now I want
that to be in a loop as well. So I tried:

'snip stuff

Dim wsRG As Worksheet
Dim wsUG As Worksheet

Set wsRG = Worksheets("2006 Realized Gains")
Set wsUG = Worksheets("Current Positions")

' Loop through regular worksheets
Dim iCtr As Long
Dim wsNames As Variant
Dim wsLastRows As Variant
Dim wsLastCols As Variant
Dim wsRng As Variant

wsNames = Array(wsRG, wsUG)
ReDim wsLastRows(LBound(wsNames) To UBound(wsNames))
ReDim wsLastCols(LBound(wsNames) To UBound(wsNames))
ReDim wsRng(LBound(wsNames) To UBound(wsNames))

For iCtr = LBound(wsNames) To UBound(wsNames)
With wsNames(iCtr)

.Activate
wsLastRows(iCtr) = .Cells(1, .Columns.Count).End(xlToLeft).Column
wsLastCols(iCtr) = .Cells(.Rows.Count, "A").End(xlUp).Row

' next line barfs <======================
wsRng(iCtr) = .Range("A1", .Cells(wsLastRows, wsLastCols))

'snip rest


Okay, so how do I fix that type mismatch?

Dallman Ross
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top