.Select, .Activesheet, .Activecell etc…

前端 未结 5 449
傲寒
傲寒 2021-01-13 04:40

For this question, I refer to the post below to clarify myself:
Why is my conditional format offset when added by VBA?

In many, many posts I see these days, OP\'

相关标签:
5条回答
  • 2021-01-13 05:07

    From my perspective, with few exceptions, the only time you should use Select is as a user input, and only then after careful consideration of alternative design/UI requirements.

    For example, I'd say it's generally not advisable to rely on Selection to let user define a Range object when this method keeps execution within the code:

    Dim myRange as Range
    Set myRange = Application.InputBox("Select your range", Type:=8)
    

    However, if you need to prompt users to select a particular shape or object on the worksheet, then maybe it's better to let them make a Selection (however, this can open up a Pandora's Box of problems without good error-handling and logic to prevent undesired user actions...).

    Here is an example of one such exception that I have in PowerPoint. I have some RibbonUI XML and VBA that adds buttons to the Shapes right-click context menu in PowerPoint, and adds similar buttons to the Ribbon itself. These are seamless UI that give the end-user a more "native" experience with the application -- users want to be able to right-click the chart and then run some macro procedures against that selected chart or table, etc. They don't want to press a button to open up a user form and scroll through a listbox of generic shape names or GUIDs.

    The procedure code needs to examine the Selection in order to handle it properly so I can use something like below, where

    Sub UpdateOrEditSelection(update As Boolean)
    'This procedure invoked when user edits/updates a chart.
    Dim uid As Variant
    Dim sel As Selection
    Dim s As Integer
    Dim chartsToUpdate As Object
    Dim multipleShapes As Boolean
    Dim sld As Slide
    Set sel = ppPres.Windows(1).Selection
    
    If update Then
        Set chartsToUpdate = CreateObject("Scripting.Dictionary")
        Select Case sel.Type
            Case ppSelectionShapes
                For s = 1 To sel.ShapeRange.count
                    uid = sel.ShapeRange(s).Name
                    '....
                    '...
                    '..
                    '.
                Next
            Case ppSelectionSlides
                For Each sld In sel.SlideRange
                    For s = 1 To sld.Shapes.count
                        uid = sld.Shapes(s).Name
                        '....
                        '...
                        '..
                        '.
    
                    Next
                Next
            Case ppSelectionText
                s = 1
                If sel.ShapeRange(s).HasTable Or sel.ShapeRange(s).HasChart Then
                    uid = sel.ShapeRange(s).Name
                    '....
                    '...
                    '..
                    '.
    
                End If
        End Select
     '....
     '...
     '..
     '.
    

    Where does it come from?

    The Macro Recorder. Essentially, this feature records every literal user input: scrolling, selecting, viewing, activating, default properties, etc., to the point of overkill. While this is sometimes helpful, it does encourage bad code written by people who don't know that it's bad, but I will not belabor that point which has been made here:

    How to avoid using Select in Excel VBA macros

    What is better, conceptually?

    Program to the objects directly. If you're merely using VBA to mimic keystrokes and mouseclicks, you're doing it wrong.

    Exceptions:

    I've found when applying formatting to series data in charts, where Select is occasionally necessary. This seems IMO to be a bug with Excel and not a design feature.

    Other applications (because VBA is not only Excel):

    • Word is a different animal, which relies a lot more on Selection object
    • In PowerPoint there are some sort of operations that can only be performed when the application and slide/shape are visible or otherwise in view. While you don't usually need to "select" anything, it does require more cumbersome code.

    I found this snippet in my App:

        Set tb = cht.Shapes.AddTextbox(msoTextOrientationHorizontal, ptLeft, tBoxTop, ptWidth, ptHeight)
        tb.Select  '<--- KEEP THIS LINE OTHERWISE TEXTBOX ALIGNMENT WILL NOT WORK ## ## ##
    

    And this:

        'PPT requires selecting the slide in order to export an image preview/jpg
    sld.Select
    ppPres.Windows(1).View.GotoSlide sld.SlideIndex
    sld.Shapes(1).Chart.Export imgPath, ppShapeFormatJPG
    

    And this, dealing with individual Point objects:

            pt.Select
            pt.format.Line.Visible = msoTrue
            pt.format.Line.Visible = msoFalse
            pt.MarkerSize = pt.MarkerSize + 2
    

    This is not an exhaustive list, just some examples of exceptions that I found. While these were from PowerPoint, the charts in PowerPoint use the same object model as Excel so I would not be surprised if some of these also need to be hacked in Excel, and Word, too.

    • Outlook: I don't do much with Outlook, it is a lot like Word and actually uses the Word object model in the Inspector, but what little I do with Outlook does rely on things like ActiveInspector, etc.

    Neither Word or PowerPoint have a "macro recorder" anymore (actually, I think Word might but it's so damn impotent as to be useless) and by the time most people do any development in other applications, they've figured most of this out already.

    0 讨论(0)
  • 2021-01-13 05:08

    There are a few methods in Excel that require Activate or ActiveSheet/ActiveWorkbook etc as I've been caught with a gotchas on occasion. The only one I can remember at the moment is the zoom property. Zoom affects only the sheet that's currently active in the window so to zoom all sheets you would need something like

    Sub SetZoom()
    Dim ws As Worksheet
        Application.screenupdating = false
    
        For Each ws In Worksheets
            ws.Select
            ActiveWindow.Zoom = 80
        Next ws
    
        Application.screenupdating = true
    End Sub
    
    0 讨论(0)
  • 2021-01-13 05:17

    I think it is important in this matter to distinguish some:

    • Active-something: Only use this if it is absolutely necessary to know what the user is handling right now. In my experience, this is usually Data Validation or Active Sheet Detection (e.g. "Update the Sheet where the user just pressed a button").
    • Selection: Somewhat the same as Active, only use readingly. Userful either for Data Validation, or for gimmicks like "Interpret the cell value as path and open it in a new Explorer Window".
    • Select, Activate: Imho different from Selection, as it actually changes the selected Cell, Sheet etc. Never ever use this to read or write data, since it enables a user to mess up your program by just clicking. Users love to click. Only use this to Zoom (see answer by @user3357963) or clean up a view after your code has finished working (see answer by @enderland). (I'm not sure, but I think handling the PageView also requires ActiveSheet).
    • Select, Activate the 2nd: If you are new to VBA and are learning via Macro Recorder, you will find a lot of code generated like this: First Range("A5").Select, then Selection.Value="NewValue". Join this to Range("A5").Value="NewValue".
    • Offset: Personally, I don't have a problem using .Offset() - I never encountered problems with this command. Instead, I think it's a handy way of saying "The cell next to this" without having to go through "This cell's sheet at this cell's row and column+1" every time.

    In many, many posts I see these days, OP's are silently allowed to use .Activate, .Select, .Offset, etc...

    I agree with this. Even though it's easier to just give the necessary answer to make a piece of code work, the use of ActiveCell.Value and the like should be discouraged. This will be much easier if there's a well explained Thread to link to, as this here is hopefully becoming :-)

    0 讨论(0)
  • 2021-01-13 05:28

    You can use .Select to determine what a user's view is after running code - for example if you create a new workbook in your code, without using Activate or Select your user may not know this happens.

    I frequently end a long operation creating a new workbook or other largescale data manipulations with

    FinalViewWorkbook.FinalViewSheet.Range("A1").Select
    

    Just to inform the end user about something - "oh, this created a new workbook of reports!" etc.

    0 讨论(0)
  • 2021-01-13 05:33

    I agree about Select and Activate, but not ActiveWorkbook, ActiveSheet, and ActiveCell (I agree that they are abused, but not that they should be avoided, per se). There are definitely legitimate uses for those. I have a program that automates a "fill series" that does so from the ActiveCell. My program can't predict what cells will be used; it's up the user to select it. That's part of the user interface.

    However, there are three situations where I have had to use Select (now four that I read about zoom, but I don't ever use it).

    1. Conditional Formatting. There is a work around using Application.ConvertFormula, but it's worse than just storing the selection, selecting the right cell, doing the deed, and reselecting the previous selection.
    2. Data Validation. Same reason.
    3. Shapes. I wish I could remember the details, but it's been too long since I've worked with Shapes. There was something I couldn't do without selecting the shape first.

    Ridding code of Select and Activate is a noble fight.

    0 讨论(0)
提交回复
热议问题