I am wondering if maybe ReSharper is able to run through every class and remove unused usings? I looked but I don't see an option like this in R# 4.5. Has anyone seen this in Resharper outside of just being able to remove usings in a single class?
Since Resharper 9, you can just select "in solution" scope when you clean up a using block.
I believe that cleanup across a project is a new feature in ReSharper 5.
I take that back, the feature is in ReSharper 4.5. If you right click on the solution, there's a Cleanup Code... item, which allows you to apply a cleanup profile to the solution. You can create a new cleanup profile from the Code Cleanup node within ReSharper options, if you want a profile to just adjust the using directives.
There is also another way I found here, using Macros.
Step 1: Create a new macro in Visual
Studio through the Tools | Macros
menu.
Step 2: Paste the code below into the
Module and save it
Public Module Module1
Sub OrganizeSolution()
Dim sol As Solution = DTE.Solution
For i As Integer = 1 To sol.Projects.Count
OrganizeProject(sol.Projects.Item(i))
Next
End Sub
Private Sub OrganizeProject(ByVal proj As Project)
For i As Integer = 1 To proj.ProjectItems.Count
OrganizeProjectItem(proj.ProjectItems.Item(i))
Next
End Sub
Private Sub OrganizeProjectItem(ByVal projectItem As ProjectItem)
Dim fileIsOpen As Boolean = False
If projectItem.Kind = Constants.vsProjectItemKindPhysicalFile Then
'If this is a c# file
If projectItem.Name.LastIndexOf(".cs") = projectItem.Name.Length - 3 Then
'Set flag to true if file is already open
fileIsOpen = projectItem.IsOpen
Dim window As Window = projectItem.Open(Constants.vsViewKindCode)
window.Activate()
projectItem.Document.DTE.ExecuteCommand("Edit.RemoveAndSort")
'Only close the file if it was not already open
If Not fileIsOpen Then
window.Close(vsSaveChanges.vsSaveChangesYes)
End If
End If
End If
'Be sure to apply RemoveAndSort on all of the ProjectItems.
If Not projectItem.ProjectItems Is Nothing Then
For i As Integer = 1 To projectItem.ProjectItems.Count
OrganizeProjectItem(projectItem.ProjectItems.Item(i))
Next
End If
'Apply RemoveAndSort on a SubProject if it exists.
If Not projectItem.SubProject Is Nothing Then
OrganizeProject(projectItem.SubProject)
End If
End Sub
End Module
Step 3: Run the macro on any solution
that you'd like and there you have it!
Enjoy :)
Related
I am willing to add some code to the begining and end of each sub or function for enabling flow tracing / debugging.
Now I copy this (almost standard code manually into the beginig of each sub / function, and also before each exit sub/function and end sub / function statement.
Something like this
public sub a()
...
**logging_successful = pushCallIntoStack("sub a")**
...
On Error Goto errorOccured
...
**logging_successful = popCallFromStack("sub a")**
Exit Sub
...
errorOccured:
...
**logging_successful = popCallFromStack("sub a")**
...
End Sub
Being able to insert these standart codes via VBIDE as default - at least in the standard entry and exit points - will save me sometime.
What you want to do is technically doable but are you sure you want to add boilerplate code with hard-coded strings to all of your procedures? That is lot of maintenance which also makes it much harder to refactor your code. I've seen lot of error messages saying it came from "Foo" but they came from "Bar" because at one point the code was in Foo but then it got moved or renamed to Bar, but they forgot to update the string constant. There is no such guarantees that the string constants are in sync with the actual procedure names.
Before sinking potentially hours into this solution, I would encourage you to first consider third-party addins that can do a much better job of helping you getting the detailed error output you need. One such solution would be vbWatchDog which provides you not only the stack tracing but also much extended diagnostics... without any changes to your source code. Precisely because it can do without any embedding constants, it won't be liable to give you outdated information.
I should note that there are also other third-party addin such as MZ-Tools which provides a one click button for adding an error template that could conceivably be used to provide what you want. However, the operation is not reversible, which adds to your maintenance burden; changing a procedure would mean you'd have to strip away the old error template, then re-add, and if there's any customization, to re-add it.
If in spite of all, you insist on continuing doing it by your own hand, you can do something like the following:
Public Sub AddBoilerPlate(TargetComponent As VBIDE.VBComponent)
Dim m As VBIDE.CodeModule
Dim i As Long
Set m = TargetComponent.CodeModule
For i = m.CountOfDeclarationLines + 1 To m.CountOfLines
Dim ProcName As String
Dim ProcKind As VBIDE.vbext_ProcKind
ProcName = m.ProcOfLine(i, ProcKind)
Dim s As Long
s = m.ProcBodyLine(ProcName, ProcKind) + 1
m.InsertLines s, <your push code>
'Loop the lines within the procedure to find the End *** line then insert the pop code
Next
End Sub
This is an incomplete sample, does not perform checks for pre-existing template. A more complete sample would probably delete any previous template before inserting.
You may need to amend the codes for your own needs but below's the general idea (e.g. change "Module2" to the name of your module and include more checks to determine where to add in new codes)
Public Sub sub_test()
Dim i As Long
With ThisWorkbook.VBProject.VBComponents("Module2").CodeModule
For i = 1 To .Countoflines
If InStr(.Lines(i, 1), "End Sub") > 0 Then
.Insertlines i, "**logging_successful = popCallFromStack(""sub a"")**"
End If
Next i
End With
End Sub
Well, the title is because I had a hard time walking through some unbearable docs and finding what I was looking for so, if these keywords can help for other google searches...
Then, when quitting Excel, all previously marked lines of code are lost and it is very frustrating when you have to go to sleep or Excel crashes :)
So, in a moment of pure madness I thought it could be possible, and even not too hard, to just save those line bookmarks and restore them at start up...
You will tell me there are other choices: don't sleep.. or use some powerful add-ins like MZ-Tools or Rubberduck, but I would like to have a native solution and understand what the problem is.
To cut to the edge, here is the core of my problem:
'sub to move cursor to a selected line and add a line bookmark:
Public Sub AddBmkOnly(ByVal CompName As String, ByVal numLine As Long)
Application.VBE.VBProjects("VBAProject") _
.VBComponents(CompName).CodeModule.CodePane _
.SetSelection numLine, 1, numLine, 1
Application.VBE.CommandBars("Edit").Controls(18).Controls(1).Execute 'the only way I could find it to work
End Sub
What happens:
1) with only one call it works!
Public Sub test_addBmk()
Call AddBmkOnly("module 1", 10)
End Sub
2) once there are more, or in a loop for example:
Public Sub test_addBmk()
Call AddBmkOnly("module 1", 10) 'cursor is just moved to selected line
Call AddBmkOnly("module 2", 5) 'line bookmark is added only in the last opened/activated/selected/visible/shown/focused on..? codepane
'...
End Sub
Place your cursor inside the 2nd test_addBmk, run and you will see a beautiful cyan blue mark appearing in the margin of your "module 2" at line 5 but that's all, no where else.
I well tried to add this kind of lines in AddBmkOnly to keep focus/active state, but it has no effect:
With Application.VBE.VBProjects("VBAProject").VBComponents(CompName)
.Activate
.CodeModule.CodePane.Window.SetFocus
.CodeModule.VBE.ActiveCodePane.Show
'...?
end with
I tried adding some DoEvents, Debug.Print, loop to 1M or likes to see if it was due to some latency/refreshing effect, but no effect either.
It could have something to do with the active state of the module or codepane window, but I can't find a working combination (also, closing the last pane - .ActiveCodePane.Window.Close - will avoid a bookmark to be added too).
It seems also that the focus is lost before an anchor is added, whatever I try, or the 'add bookmark' menu action doesn't see where to apply.... or it is something else...
Calling test_addBmk() multiple times doesn't work neither, the only way I found is creating 'one action' buttons in an Excel sheet, as many as the number of bookmarks I needed... that's not funny.
What am I doing wrong? Is it even possible the way I'm trying? How can I add more than a single bookmark?
You need to active the code pane before you invoke the menu item:
Public Sub AddBmkOnly(ByVal CompName As String, ByVal numLine As Long)
Dim editor As VBE
Dim project As VBProject
Dim component As VBComponent
Set editor = Application.VBE
Set project = Application.VBE.VBProjects("VBAProject")
Set component = project.VBComponents(CompName)
component.CodeModule.CodePane.SetSelection numLine, 1, numLine, 1
component.Activate
Application.VBE.CommandBars("Edit").Controls("&Toggle Bookmark").Execute 'the only way I could find it to work... almost[*]
End Sub
Note that this won't work if you try to step through it in a debugger, because each time you step it sets the active pane back to the code that you're executing.
A couple other notes:
You should be testing the number of code lines before trying to set the selection - if numLine is higher than the lines of code in the module, that's an application error.
Call should be considered deprecated - there's absolutely no reason to use it.
You should avoid hard coding an index in the Controls collection - other add-ins can modify these, so who knows what you'll get.
Thanks for mentioning Rubberduck! (I'm a contributor)
This version of the above works for me.
Public Sub test_addBmk()
Call AddBmkOnly("module1", 10)
Call AddBmkOnly("module2", 5)
End Sub
Public Sub AddBmkOnly(ByVal CompName As String, ByVal numLine As Long)
Dim editor As VBE
Dim project As VBProject
Dim component As VBComponent
Set editor = Application.VBE
Set project = Application.VBE.VBProjects("VBAProject")
Set component = project.VBComponents(CompName)
component.CodeModule.CodePane.SetSelection numLine, 1, numLine, 1
component.Activate: DoEvents
editor.CommandBars("Edit").Controls("&Toggle Bookmark").Execute
DoEvents
End Sub
Sorry about the non descriptive Title, I just didn't know how to describe my goal.
I'm new at VBA and didn't yet understand how things really work.
I've written a function which gets a directory from the user, and displays data from the first file in the directory. Now, I want to add a "next" button.
When the "next" button is pressed, my code should display data from the next file in the directory.
I tried to use global variables but they seem to get initialized each time the button is pressed.
What is the best way to achieve my goal? Do I have to use the spreadsheet as memory and write and read everything from there? Or does Excel VBA have some other "live memory" mechanism?
Thanks,
Li
Globals will not normally be reinitialized when you click a button. They will be reinitialized if you recompile your VBA project. Therefore, while debugging, you may see a global being reinitialized.
You can use the spreadsheet as memory. One way to do this is to have a worksheet whose Visibility property you set to xlSheetVeryHidden (you can do this from the VBA project). This worksheet won't be visible to users, so your VBA application can use it to store data.
This could be approached many ways, as with any problem I guess!
You could break the problem up into two subroutines:
1) Retrieve all the file names in the selected directory and display the first file's data
2) If it's not the last file, get the next file's data and display it
You could use a global variable to store the filenames and an index to remember where you are up to in the collection of filenames.
Global filenames As Collection
Global fileIndex As Integer
Public Sub GetFilenames()
Dim selectedDirectory As String
Dim currentFile As String
selectedDirectory = "selected\directory\"
currentFile = Dir$(selectedDirectory)
Set filenames = New Collection
While currentFile <> ""
filenames.Add selectedDirectory & currentFile
currentFile = Dir$()
Wend
' Make sure there were files
If filenames.Count >= 1 Then
fileIndex = 1
' Call a method to display data
DisplayData(filenames(fileIndex))
Else
' No files
End If
End Sub
Public Sub GetNextFile()
' Make sure we have a filenames object
If Not filenames Is Nothing Then
If fileIndex < filenames.Count Then
fileIndex = fileIndex + 1
' Call the display method again
DisplayData(filenames(fileIndex))
Else
' Decide what to do after reaching the final file
End If
Else
' No filenames
End If
End Sub
I didn't include the DisplayData procedure as I'm not sure what type of files you're grabbing or what you are doing with them but if it were say excel files it could be something like:
Public Function DisplayData(filename As String)
Dim displayWb As Workbook
Set displayWb = Workbooks.Open(filename)
' Do things with displayWb
End Function
You could then set the macro of the button to "GetNextFile" and it will cycle through the files after each click. As for the lifetime of global variables, they only reinitialize when the VBA project is reset or when they are specifically initialized through a procedure or the immediate window.
Perhaps these two functions can also help you:
SaveSetting
GetSetting
as showed here: http://www.j-walk.com/ss/excel/tips/tip60.htm
This seems like a simple thing, but I cannot figure it out, or find it online.
If I select 5 cells in a column(say A1:A5), and I would like to move this selection shape(column 1:5) over (to B1:B5); Is there shortcut to do this? Currently I hit the left arrow, and the select box changes size to just B1, and I have to hit shift and select B2:B5. Ideally I would like to discover a hot key that "locks" the shape of the select box.
It has been suggested by colleagues to write a macro, but this is ineffective in many cases. For example what if instead of a column I wanted to do the same thing with a row, or with a different sized shape. It seems likely that excel has this feature built in.
I'm not sure how a macro would be ineffective. I would write procedures similar to what's below, then assign them to hotkeys. Let me know if it works for you.
Option Explicit
Public rowDim As Long
Public colDim As Long
Public putSel as Boolean
Sub togglePutSel()
putSel = Not putSel
End Sub
Sub GetSelShape()
rowDim = Selection.Rows.Count
colDim = Selection.Columns.Count
putSel = True
End Sub
Sub PutSelShape()
Selection.Resize(rowDim, colDim).Select
End Sub
If you want to make it work for whenever you hit the arrow keys, then in your Sheet code, you can use this. You may want to do a quick check that rowDim and colDim aren't 0. The only issue with this is that you'd be stuck with that behavior unless you create a trigger to stop calling PutSelShape. So, I'd suggest one macro (hotkeyed to GetSelShape) to toggle it, and another hotkey for togglePutSel.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If putSel Then
Call PutSelShape
End If
End Sub
Let me set up the environment.
This is VBA code running in Excel.
I have a userform that contains a msflexgrid. This flexgrid shows a list of customers and the customer', salesperson, csr, mfg rep, and territories, assignments. When you click in a column, let's say under the Territory column, another userform opens to show a list of Territories. You then click on the territory of your choice, the userform disappears and the new territory takes the place of the old territory.
This all works great until you click on the territory of your choice the 'Territory' userform does not disappear (it flickers) and the new territory does not transfer the underlying userform.
I should mention that when I'm stepping through the code it works great.
I'm assuming it has something do to with the flexgrid as all the other userform (that don't have flexgrids) that open userform work just fine.
Following is the some code sample:
** Click event from flexgrid that shows Territory userform and assignment of new territory when territory userform is closed.
Private Sub FlexGrid_Customers_Click()
With FlexGrid_Customers
Select Case .Col
Case 0
Case 2
Case 4
Case 6
UserForm_Territories.Show
Case Else
End Select
If Len(Trim(Misc1)) > 0 Then
.TextMatrix(.Row, .Col) = Trim(Misc1)
.TextMatrix(.Row, .Col + 1) = Trim(Misc2)
End If
End With
End Sub
** The following Subs are used in the Territory userform
Private Sub UserForm_Activate()
Misc1 = ""
Misc2 = ""
ListBox_Territory.Clear
Module_Get.Territories
End Sub
Private Sub UserForm_Terminate()
Set UserForm_Territories = Nothing
End Sub
Private Sub ListBox_Territory_Click()
With ListBox_Territory
Misc1 = Trim(.List(.ListIndex, 0))
Misc2 = Trim(.List(.ListIndex, 1))
End With
Hide
UserForm_Terminate
End Sub
I know this a long winded explanation but I'm a fairly decent VBA programmer and this has me stumped.
Any help would be greatly appreciated.
I'm not going to say what you're doing is wrong (in that it won't ever work), but it scares the heck out of me. This is not the way I'd deal with forms.
Firstly, you're using UserForm_Territories (the class/form name) to refer to an implicitly-created instance of the form. This is something I've always avoided doing. I would always create an instance of a form explicitly, so instead of:
UserForm_Territories.Show
I would do:
Dim oTerritoriesForm As UserForm_Territories
Set oTerritoriesForm = New UserForm_Territories
oTerritoriesForm.Show vbModal
' get the values from the form here
Unload oTerritoriesForm
Next, and much more worryingly, you're subverting the UserForm_Terminate behaviour by calling it explicitly. Why you're doing this I can't imagine, unless you thought that it would work around your stated problem. My advice: don't do that.
Worse, you're attempting to assign to the implicitly-created instance of the form within that Terminate method. You shouldn't be doing that, either. I'm surprised that even compiles.
It seems like you're trying to force the implicitly-created instance of the form to mimic an explicitly-created one. In which case, create it explicitly, as shown above.