VBA using a ListBox to apply selections to a Module - vba

I'm relatively new to VBA, so thanks in advance for the advice. I am just dabbling to help out a friend to make his work less repetitive in my free time.
I created a GUI that has 2 ListBoxes that contain the names of the worksheets in the excel file. The left ListBox contains the available worksheets and the right ListBox is the list of worksheets the user chooses to perform the analysis on with the ability to move the names between the 2 ListBoxes. I am trying to link the GUI to my Module I created that performs the actual analysis. Once it is linked, I need the Selections that are made in the GUI to become an array that can be looped through in the Module. I am missing a few pieces, so bear with me please.
Submit Button Code:
Dim Size As Integer
Size = Me. ListBox2 . ListCount - 1
ReDim Selection(0 To Size) As String
Dim i As Integer
For i = 0 To Size
Selection (i) = Me.ListBox2.ItemData(i)
Next i
Unload GUI
The Area in the Module that I want to implement the Selection:
'Only performs copy/paste actions on the worksheets that aren't named "Summary".
For Each sh In ActiveWorkbook. Worksheets
If sh.Name < > DestSh.Name Then
'Sets the Range you want to Copy from the source Worksheets. This range is the set of cells with data in it.
Set CopyRng = sh. UsedRange
The goal is to read the Selections from the GUI, locate the sheet that the Selections are named after and link the selections to the "sh" variable somehow.

Since you are already referencing sh as a Worksheet variable in your code, while the Selections array holds strings, I believe the simplest way would be to shift the For..Each statement to:
For Each sel_item In Selection
and add the line:
Set sh = ActiveWorkbook.Worksheets(sel_item)
You will, of course, also need to edit the Next sh statement to Next sel_item, and add a Dim sel_item as Variant
In other words, the beginning of the loop section will look like:
'Only performs copy/paste actions on the worksheets that aren't named "Summary".
For Each sel_item In Selection
Set sh = ActiveWorkbook.Worksheets(sel_item)
If sh.Name < > DestSh.Name Then
'Sets the Range you want to Copy from the source Worksheets. This range is the set of cells with data in it.
Set CopyRng = sh. UsedRange
Instead of this (original):
'Only performs copy/paste actions on the worksheets that aren't named "Summary".
For Each sh In ActiveWorkbook. Worksheets
If sh.Name < > DestSh.Name Then
'Sets the Range you want to Copy from the source Worksheets. This range is the set of cells with data in it.
Set CopyRng = sh. UsedRange

Delimited Cell Expansion
First adjust the values in the constants section and then read through the whole comments because there might be some issues you wouldn't expect. You can use it on any column containing commas as delimiters, so in your case you can use it on the Base column, too.
Sub DelimitedCellExpansion()
Const cVntWsSource As String = "Sheet1" ' Source Worksheet Name/Index
Const cStrSourceFirst As String = "A1" ' Source First Cell Range of Data
Const cVntSplit As Variant = "D" ' Source Column Letter/Number
Const cVntWsTarget As String = "Sheet2" ' Target Worksheet Name/Index
Const cStrTargetFirst As String = "B1" ' Target First Cell Range of Data
Const cStrSep As String = ",,,|,,|, ," ' Wrong Separators
Dim vntSrc As Variant ' Source Array
Dim vntSep As Variant ' Separator Array
Dim vntSplitData As Variant ' Split Data Array
Dim vntSplit As Variant ' Split Array
Dim vntCol As Variant ' Target Column Array
Dim vntTgt As Variant ' Target Array
Dim intCol As Integer ' Source Array Target Column
Dim lng1 As Long ' Source Array Target Column Rows Count(er)
Dim int1 As Integer ' Separator Array Strings Counter
Dim lng2 As Long ' Target Array Rows Count(er)
Dim int2 As Integer ' Split Data Column Counter
' Source Worksheet Data Extraction
With ThisWorkbook.Worksheets(cVntWsSource)
If Not .Cells.Find("*", .Cells(.Rows.Count, .Columns.Count), -4123, , 1) _
Is Nothing Then ' Worksheet has data.
' Paste Source Range into Source Array
vntSrc = .Range(cStrSourceFirst, .Cells(.Cells.Find("*", , , , 1, 2) _
.Row, .Cells.Find("*", , , , 2, 2).Column))
' Calculate Source Array Target Column.
intCol = .Columns(cVntSplit).Column - .Range(cStrSourceFirst).Column + 1
Else ' Worksheet is empty.
GoTo EmptySheetErr
End If
End With
' Split Separator String into Separator Array.
vntSep = Split(cStrSep, "|")
' Introduce Split Data Array
ReDim vntSplitData(1 To UBound(vntSrc))
' Target Array Columns Count
For lng1 = 1 To UBound(vntSrc)
' Clean separators in current field of Target Column.
vntSrc(lng1, intCol) = WorksheetFunction.trim(vntSrc(lng1, intCol))
For int1 = 0 To UBound(vntSep)
vntSrc(lng1, intCol) = Replace(vntSrc(lng1, intCol), _
vntSep(int1), ",")
Next
' Split current field of Target Column.
vntSplit = Split(vntSrc(lng1, intCol), ",")
' Resize Target Column Array.
If Not IsEmpty(vntCol) Then
ReDim Preserve vntCol(1 To UBound(vntCol) + UBound(vntSplit) + 1)
Else
ReDim vntCol(1 To UBound(vntSplit) + 1)
End If
' Copy split values to Target Column Array.
For int1 = 0 To UBound(vntSplit)
vntCol(UBound(vntCol) - UBound(vntSplit) + int1) = trim(vntSplit(int1))
Next
' Collect Split Data.
vntSplitData(lng1) = UBound(vntSplit) + 1
Next
Erase vntSplit
Erase vntSep
' Write data to Target Array
lng2 = 1
ReDim vntTgt(1 To UBound(vntCol), 1 To UBound(vntSrc, 2))
For lng1 = 1 To UBound(vntSrc)
' Write current row of other columns to Target Array.
Select Case intCol
Case 1 ' LBound(vntSrc, 2)
For int1 = 2 To UBound(vntSrc, 2)
vntTgt(lng2, int1) = vntSrc(lng1, int1)
Next
Case UBound(vntSrc, 2)
For int1 = 1 To UBound(vntSrc, 2) - 1
vntTgt(lng2, int1) = vntSrc(lng1, int1)
Next
Case Else
For int1 = 1 To intCol - 1
vntTgt(lng2, int1) = vntSrc(lng1, int1)
Next
For int1 = intCol + 1 To UBound(vntSrc, 2)
vntTgt(lng2, int1) = vntSrc(lng1, int1)
Next
End Select
' Write current row of Source Array Target Column to Target Array.
For int2 = 1 To vntSplitData(lng1)
vntTgt(lng2, intCol) = vntCol(lng2)
lng2 = lng2 + 1
Next
Next
Erase vntCol
' With ThisWorkbook.Worksheets(cVntWsTarget)
' ' Paste Target Array into Target Worksheet.
' .Range(cStrTargetFirst).Resize(UBound(vntTgt), UBound(vntTgt, 2)) = vntTgt
' End With
' This would have been the end, if there was no formatting to do.
' Introducing a Range object.
Dim objRng As Range
Set objRng = ThisWorkbook.Worksheets(cVntWsTarget) _
.Range(cStrTargetFirst).Resize(UBound(vntTgt), UBound(vntTgt, 2))
'***************************************
' This is necessary if there are merged cells in the Target Range.
' This clears the whole Target Worksheet.
objRng.Parent.Cells.Clear
' This clears only the Target Range.
' objRng.Cells.Clear
'***************************************
' Paste Target Array into Target Range of Target Worksheet.
objRng = vntTgt
Erase vntTgt
With objRng
' Paste formatting from first row down to the last.
.Cells(1, 1).Resize(, .Columns.Count).Copy ' Copy first row.
.PasteSpecial Paste:=xlPasteFormats ' Paste formatting down to last.
' The Target Range is selected and is flickering. Therefore:
Application.CutCopyMode = False ' Target Range still selected.
'***********************************************************
' Apply formatting (merge)
'***********************************************************
' This is up to you. I have done only some easy formatting.
' With .Interior
' .ColorIndex = xlNone
' .Pattern = xlSolid
' .PatternColorIndex
' End With
' ' Font
' With .Font
' .Name = "Verdana"
' .Size = 10
' .Strikethrough = False
' .Superscript = False
' .Subscript = False
' .OutlineFont = False
' .Shadow = False
' .Underline = xlUnderlineStyleNone
' .ColorIndex = xlAutomatic
' .Bold = True
' End With
' Borders
With .Borders(xlEdgeLeft)
.LineStyle = xlContinuous
.Weight = xlThin
.ColorIndex = xlAutomatic
End With
With .Borders(xlEdgeTop)
.LineStyle = xlContinuous
.Weight = xlThin
.ColorIndex = xlAutomatic
End With
With .Borders(xlEdgeBottom)
.LineStyle = xlContinuous
.Weight = xlThin
.ColorIndex = xlAutomatic
End With
With .Borders(xlEdgeRight)
.LineStyle = xlContinuous
.Weight = xlThin
.ColorIndex = xlAutomatic
End With
With .Borders(xlInsideVertical)
.LineStyle = xlContinuous
.Weight = xlThin
.ColorIndex = xlAutomatic
End With
With .Borders(xlInsideHorizontal)
.LineStyle = xlContinuous
.Weight = xlThin
.ColorIndex = xlAutomatic
End With
lng2 = 1
For lng1 = 1 To UBound(vntSrc)
' Write current row of other columns to Target Array.
Select Case intCol
Case 1 ' LBound(vntSrc, 2)
For int1 = 2 To UBound(vntSrc, 2): GoSub OtherFormat: Next
Case UBound(vntSrc, 2)
For int1 = 1 To UBound(vntSrc, 2) - 1: GoSub OtherFormat: Next
Case Else
For int1 = 1 To intCol - 1: GoSub OtherFormat: Next
For int1 = intCol + 1 To UBound(vntSrc, 2): GoSub OtherFormat: Next
End Select
GoSub TargetFormat
lng2 = lng2 + vntSplitData(lng1)
Next
Erase vntSplitData
Erase vntSrc
GoTo FormatEnd
'***********************************************************
' This is created to easily adjust (change) formatting.
' The formatting applies only to the Data range.
'***********************************************************
OtherFormat: ' Format other columns.
With .Cells(lng2, int1).Resize(vntSplitData(lng1))
If vntSplitData(lng1) > 1 Then ' Multiple rows.
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
.WrapText = False
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = True
Else ' One row only.
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.WrapText = False
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
End If
End With
Return
TargetFormat: ' Format Target Column.
With .Cells(lng2, intCol).Resize(vntSplitData(lng1))
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.WrapText = False
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
End With
Return
FormatEnd:
' Only autofits the Target Range.
' .Columns.AutoFit
' Autofit from top.
.Columns.EntireColumn.AutoFit
'***********************************************************
End With
ProcedureExit:
Set objRng = Nothing
Exit Sub
EmptySheetErr:
MsgBox "You're in an empty sheet."
GoTo ProcedureExit
End Sub

Related

Conditional formatting for highlighting top 2 values for each row for visibile cells only

I am trying to highlight top 2 values for each row for visible cells only using conditional formatting in Excel macro. My range is dynamic, hence I am running a loop to arrive at the last cell of the range.
Here is my code:
With Sheets("pcSupplyChainAnalysis").Select
For i = 2 To ctr
Set rng = Range("C" & i & ":" & "I" & i).SpecialCells(xlCellTypeVisible)
rng.FormatConditions.AddTop10
rng.FormatConditions(rng.FormatConditions.Count).SetFirstPriority
With rng.FormatConditions(1)
.TopBottom = xlTop10Top
.Rank = 2
.Percent = False
End With
With rng.FormatConditions(1).Interior
.PatternColorIndex = xlAutomatic
.Color = 255
.TintAndShade = 0
End With
rng.FormatConditions(1).StopIfTrue = False
Next
End With
Ctr is a counter I am running to find the position of the last non blank cell, as my data has blank values too and I am copying it from another sheet using macro.
ctr = 2
Do While (ActiveSheet.Range("A" & ctr).Value <> "")
ctr = ctr + 1
Loop
ctr = ctr - 1
ActiveSheet.Range("B2:I" & ctr).Select
Selection.Cut
Range("C2:J" & ctr).Select
ActiveSheet.Paste
Attached is the image of the format of my data. I want to highlight top 2 numbers for each row and ONLY FOR VISIBLE CELLS (as I am using some filters also in the range).
Try this:
Option Explicit
Public Sub ShowTop2()
Dim rng As Range, visibleRow As Range
Application.ScreenUpdating = False
With ThisWorkbook.Worksheets("pcSupplyChainAnalysis")
.Columns.FormatConditions.Delete
Set rng = .UsedRange.SpecialCells(xlCellTypeVisible)
End With
For Each visibleRow In rng.Rows
If visibleRow.Row > 1 Then
With visibleRow.FormatConditions
.AddTop10
.Item(.Count).SetFirstPriority
With .Item(1)
.TopBottom = xlTop10Top
.Rank = 2
.Interior.Color = 255
End With
End With
End If
Next
Application.ScreenUpdating = True
End Sub
An easier way to determine the last used row in column A:
ctr = Worksheets("pcSupplyChainAnalysis").Cells(Rows.Count, "A").End(xlUp).Row
You don't need to Select anything for any of your actions

Highlight Row-Column of selected cell

Be gentle guys, I'm not a programmer.
I got this snippit of code off the internet many many moons ago. I would give credit, but I don't remember where it came from.
Sub Worksheet_SelectionChange(ByVal Target As Excel.Range)
Static xRow
Static xColumn
If xColumn <> "" Then
With Columns(xColumn)
.Interior.ColorIndex = xlNone
End With
With Rows(xRow)
.Interior.ColorIndex = xlNone
End With
End If
pRow = Selection.Row
pColumn = Selection.Column
xRow = pRow
xColumn = pColumn
With Columns(pColumn)
.Interior.ColorIndex = 19
.Interior.Pattern = xlSolid
End With
With Rows(pRow)
.Interior.ColorIndex = 19
.Interior.Pattern = xlSolid
End With
End Sub
The above code highlights rows and columns of a selected sell. The problem is that it highlights columns from 1 to 1048576, which causes the vertical scroll bar to get tiny. Plus if there is any color coding in the spreadsheet it screws that up. I decided to write my own highlighter. I put a border around my selected row,column and only do it for 500 rows. It works, almost. The problem is that something in my code cancels the copy command, and will not allow me to paste, which did not happen in the code above. Copy/Paste is a must. Any help would be greatly appreciated.
Sub Worksheet_SelectionChange(ByVal Target As Excel.Range)
Range("A1:N500").Borders(xlEdgeLeft).Weight = xlThin
Range("A1:N500").Borders(xlEdgeTop).Weight = xlThin
Range("A1:N500").Borders(xlEdgeBottom).Weight = xlThin
Range("A1:N500").Borders(xlEdgeRight).Weight = xlThin
Range("A1:N500").Borders(xlInsideVertical).Weight = xlThin
Range("A1:N500").Borders(xlInsideHorizontal).Weight = xlThin
Range("A1:N500").Borders(xlEdgeLeft).Color = vbBlack
Range("A1:N500").Borders(xlEdgeTop).Color = vbBlack
Range("A1:N500").Borders(xlEdgeBottom).Color = vbBlack
Range("A1:N500").Borders(xlEdgeRight).Color = vbBlack
Range("A1:N500").Borders(xlInsideVertical).Color = vbBlack
Range("A1:N500").Borders(xlInsideHorizontal).Color = vbBlack
Dim SplitAddress() As String
SplitAddress = Split(ActiveCell.Address, "$")
Dim RowSelection As String
RowSelection = "A" & SplitAddress(2) & ":" & "N" & SplitAddress(2)
Dim ColSelection As String
ColSelection = SplitAddress(1) & "1" & ":" & SplitAddress(1) & "500"
With Range(RowSelection)
.BorderAround Weight:=xlThick, Color:=RGB(255, 0, 0)
End With
With Range(ColSelection)
.BorderAround Weight:=xlThick, Color:=RGB(255, 0, 0)
End With
End Sub
try this.
it is work in progress
it copies the format, as the default format, from the very last cell in worksheet
the code uses no copy/paste to do the borders
i am still working on copy/paste between cells that you are having trouble with
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Application.ScreenUpdating = False
Dim aaa As DisplayFormat
Set aaa = Range("XFD1048576").DisplayFormat ' copy format from very last cell (it is a cheat)
Range("A1:N500").Borders.Color = aaa.Borders.Color ' revert border color to its default
Range("A1:N500").Borders.LineStyle = aaa.Borders.LineStyle
Dim i As Integer
For i = xlEdgeLeft To xlEdgeRight ' loop the four outside borders (7 to 10)
Target.EntireRow.Resize(1, 8).Borders.Item(i).Color = vbRed
Target.EntireRow.Resize(1, 8).Borders.Item(i).Weight = xlThick
Target.EntireColumn.Resize(500, 1).Borders.Item(i).Color = vbRed
Target.EntireColumn.Resize(500, 1).Borders.Item(i).Weight = xlThick
Next i
Application.ScreenUpdating = True
End Sub

Buttons To Add A Row and Add A column Bug?

I have a table as you can see in the image. The add row and add column button when pressed takes a user input meaning if the user wants the column in lets say C of the table it gets generated; same for the row button.
If I added a column in C using the column button and I added a row at line 5 using the row button look at what occurs:
Notice column C how the colors are distorted??
The only time this does not occur is if the user entered values that created rows and columns at the end of the table.
Add Row button code :
Private Sub CommandButton21_Click()
Dim varUserInput As Variant
Dim inpt As String
Dim oLo As ListObject
Dim RowNum
inpt = MsgBox("Do You Want To Add A Row At The END Of The Table?", vbYesNo + vbQuestion, "Add Row Choice") 'user input
If inpt = vbNo Then
' add row to table 'runs if condition is user selected no
varUserInput = InputBox("Enter The Row Number You Want To Generate:", _
"What Row?")
If varUserInput = "" Then Exit Sub
RowNum = varUserInput 'adds row based on user input
Rows(RowNum & ":" & RowNum).Insert shift:=xlDown
Rows(RowNum - 1 & ":" & RowNum - 1).Copy Range("A" & RowNum)
Range(RowNum & ":" & RowNum).ClearContents
Else
Set oLo = ActiveSheet.ListObjects(1) 'first table on sheet
With oLo
.ListRows.Add AlwaysInsert:=True 'adds row to end of table
.Range.Rows(.Range.Rows.Count).RowHeight = 30
End With
End If
End Sub
Add Column button:
Private Sub CommandButton22_Click()
' add column to table
Dim userinput As String
Dim QuestionToMessageBox As String
Dim colIndex As Variant
Dim StrtRow As Long, EndRow As Long, i As Long
Dim oLo As ListObject
userinput = MsgBox("Do you want to add the column at the END of the table?", vbYesNo + vbQuestion, "Add Column Choice") 'user input
If userinput = vbNo Then 'condition if no is selected
On Error GoTo Canceled '
colIndex = Application.InputBox("Enter a column that you want to add: ", "What column?")
If colIndex = "" Then Exit Sub
With ThisWorkbook.Sheets("Sheet1")
.Columns(colIndex).Insert shift:=xlRight '<--| reference column you want to insert
'sheet row numbers from table rows
Set oLo = .ListObjects(1) '<~~ first table on sheet
With oLo
StrtRow = .ListRows(1).Range.Row
EndRow = .ListRows.Count + StrtRow - 1
End With
For i = StrtRow To EndRow
.Cells(i, colIndex).Interior.Color = .Cells(i, 1).DisplayFormat.Interior.Color
Next i
End With
Else 'condition if yes is selected
Set oLo = ActiveSheet.ListObjects(1) 'first table on sheet
With oLo
.ListColumns.Add
.ListColumns(.ListColumns.Count).Range.ColumnWidth = 25
End With
'macro loops through to end of table to generate the proper around column lines
Range("Table1[[#Headers],[Stages]]").Select
Do Until ActiveCell.Value = ""
ActiveCell.Offset(0, 1).Activate
Loop
ActiveCell.Offset(0, -1).Activate
Selection.Borders(xlDiagonalDown).LineStyle = xlNone
Selection.Borders(xlDiagonalUp).LineStyle = xlNone
Selection.Borders(xlEdgeLeft).LineStyle = xlNone
With Selection.Borders(xlEdgeTop)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlThin
End With
With Selection.Borders(xlEdgeBottom)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlThin
End With
Selection.Borders(xlEdgeRight).LineStyle = xlNone
Selection.Borders(xlInsideVertical).LineStyle = xlNone
Selection.Borders(xlInsideHorizontal).LineStyle = xlNone
End If
Canceled:
End Sub
Because you're dealing with a ListObject the formatting should just work, so I removed all the code that was supposed to format the table. I may have missed something and you'll need to add it back.
You had an On Error Goto Cancelled statement at the beginning of your column procedure which was basically an On Error Exit Sub statement. Unless you were very clear on what errors you expected your code could quit at any error with unexpected or misunderstood results.
Below is my attempt to simplify both routines. I moved things around a lot to try to avoid repetition, got rid of some Variant variables and made other changes.
I also changed the InputBox to an Application.InputBox, which allows you to specify the input type. This means that a blank response will throw up a confusing message to the user, though, so I put Application.DisplayAlerts around the InputBox prompt to suppress the message.
I hope this works as is, I expect that something will be off from what you want. Hopefully though it will get you partway there and show you some new tricks!
Private Sub CommandButton21_Click()
'add row to table
Dim InputPosition As Long
Dim InputEndOfTable As VbMsgBoxResult
Dim oLo As ListObject
Set oLo = ActiveSheet.ListObjects(1) '
With oLo
InputEndOfTable = MsgBox("Do You Want To Add A Row At The END Of The Table?", vbYesNo + vbQuestion, "Add Row Choice")
If InputEndOfTable = vbNo Then
Application.DisplayAlerts = False
InputPosition = Application.InputBox(Prompt:="Enter The Row Number You Want To Add:", Title:="What Row?", Type:=1)
Application.DisplayAlerts = True
Else
InputPosition = .Range.Rows.Count + 1
End If
If InputPosition = 0 Then Exit Sub
If InputEndOfTable = vbYes Then
.ListRows.Add
Else
.ListRows.Add InputPosition
End If
.Range.Rows(InputPosition).RowHeight = 30
End With
End Sub
Private Sub CommandButton22_Click()
'add column to table
Dim InputPosition As Long
Dim InputEndOfTable As VbMsgBoxResult
Dim oLo As ListObject
Set oLo = ActiveSheet.ListObjects(1) '
With oLo
InputEndOfTable = MsgBox("Do You Want To Add A Column At The END Of The Table?", vbYesNo + vbQuestion, "Add column Choice")
If InputEndOfTable = vbNo Then
Application.DisplayAlerts = False
InputPosition = Application.InputBox(Prompt:="Enter The Column Number You Want To Add:", Title:="What Column?", Type:=1)
Application.DisplayAlerts = True
Else
InputPosition = .Range.Columns.Count + 1
End If
If InputPosition = 0 Then Exit Sub
If InputEndOfTable = vbYes Then
.ListColumns.Add
Else
.ListColumns.Add InputPosition
End If
.ListColumns(InputPosition).Range.ColumnWidth = 25
End With
End Sub

Export SQL Query to Excel File with multiple sheets

I have the below query (thanks to stackoverflow) that will loop through a list of groups and give me the permissions the group will have for a category. In Linqpad I can export the result into one Excel sheet, but I was wondering if it was possible to export each group result in the loop to a separate sheet in the Excel file. I was going to try in C# first, but I was wondering if it can be done via SQL or Linqpad as well.
Also, Ad Hoc Distributed Queries are disabled on the server.
SELECT GroupId, Name
INTO #GroupTemp
FROM [Group]
DECLARE #Id INT
WHILE EXISTS (
SELECT * FROM #GroupTemp
)
BEGIN
SELECT TOP 1 #Id = GroupId
FROM #Temp
SELECT g.NAME AS 'GroupName'
,c.NAME AS 'CategoryName'
,c.CategoryId
,c.ParentCategoryId
,p.[Read]
,p.Edit
,p.[Delete]
,p.[Add]
,p.Share
,p.Admin
FROM GroupCategoryPermission p
INNER JOIN [Group] g ON p.GroupId = #Id
INNER JOIN Category c ON p.CategoryID = c.CategoryID
WHERE g.GroupId = #Id
DELETE #GroupTemp
WHERE GroupId = #Id
END
I just decided to use an Excel macro after I exported the query from Linqpad. My VBA is a little rusty and I have a couple of small issues that I need to work out (I'm sure there is an easier way than I did it), but this is okay for now. Basically, I searched for every row in column one with GroupName as the value. From there I stored those in an array and used the different in between each for each sheet to be added.
Option Explicit
Private Function Sleep()
Application.Wait Now + 1 / (24 * 60 * 60.0# * 2)
End Function
'Remove 1st row of Sheet 1 and blank rows from sheet
Private Function CheckEmpty()
On Error Resume Next
Worksheets(1).Select()
Columns("A").SpecialCells(xlBlanks).EntireRow.Delete()
Rows("1:1").Select()
Selection.Delete Shift:=xlUp
End Function
'Function to get the name of the group and name the sheet that name
Private Function NameSheet()
Dim groupName As String
groupName = ActiveSheet.Range("A2").Value
If Len(groupName) > 31 Then
groupName = Left(groupName, 31)
ActiveSheet.Name = groupName
Else
ActiveSheet.Name = groupName
End If
End Function
'This will format the sheet
Private Function FormatSheet()
Cells.Select()
With Selection
.WrapText = False
End With
Rows("1:1").Select()
Selection.Font.Bold = True
Cells.Select()
With Selection
.HorizontalAlignment = xlLeft
.VerticalAlignment = xlBottom
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
.Columns.AutoFit()
End With
End Function
'Main sub to separate groups into their own sheets
Sub SplitToSheets()
'Variables
Dim ws As Worksheet, rng As Range, cell As Range, findString As String
Dim counter As Long, numbers() As Long, lastRow As Long, firstRow As Long
'Clean sheet 1
Worksheets(1).Activate()
CheckEmpty()
FormatSheet()
'Set the range that we will be checking
firstRow = Rows("1:1").Row
lastRow = ActiveSheet.Cells(Rows.Count, "A").End(xlUp).Row
rng = Range("A" & firstRow & ":" & "A" & lastRow)
rng.Select()
'Set the counter so we loop through array
counter = 1
'Loop through array and store row numbers
For Each cell In rng
If cell.Value = "GroupName" Then
ReDim Preserve numbers(counter)
numbers(counter) = cell.Row
'Increase counter by 1
counter = counter + 1
End If
Next
'Separate groups to sheet using numbers array
'Variables
Dim inx As Long, rStart As Long, rEnd As Long, ind As Long
'Copy first group to new sheet on it's own (need to change logic to avoid separation, eventually)
rStart = numbers(1)
rEnd = numbers(2) - 1
Rows(rStart & ":" & rEnd).Select()
Selection.Copy()
Sheets.Add After:=Sheets(Sheets.Count)
Selection.PasteSpecial(Paste:=xlPasteValuesAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False)
NameSheet()
FormatSheet()
'Index Counter for looping through array
ind = 0
For inx = LBound(numbers) To UBound(numbers)
'Need to loop once and make sure the counter is greater than 1
If ind > 0 Then
'Revert to sheet 1
Worksheets(1).Select()
'Start row number
rStart = numbers(ind)
'End row number
rEnd = (numbers(ind) - numbers(ind - 1))
'Selection must start on second group
If rEnd > 1 Then
'Select range
Rows(rStart & ":" & rStart + rEnd).Select()
'Copy
Selection.Copy()
'Add next availble sheet
Sheets.Add After:=Sheets(Sheets.Count)
'Paste values
Selection.PasteSpecial(Paste:=xlPasteValuesAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False)
'Set sheet name and rename to match group
NameSheet()
FormatSheet()
Sleep()
End If
End If
'Increase index by 1
ind = ind + 1
Next
'This deletes the main sheet that we no longer need
Application.DisplayAlerts = False
Worksheets(1).Delete()
Application.DisplayAlerts = True
Worksheets(1).Activate()
End Sub
'This macro will give option to seach for sheet
Sub GetSheet()
Dim SearchData As String
SearchData = InputBox("Enter 'exact' group name.")
If SearchData <> vbNullString Then
On Error Resume Next
Sheets(SearchData).Activate()
If Err.Number <> 0 Then MsgBox "Unable to find group named: " & SearchData
On Error GoTo 0
End If
End Sub

Automatic plotting of graphs from different sheet

I am programming an excel application that takes info from a Tables Sheet ( that it is also programmed and the length and position of each table can change) and generate a graphic for each table in other sheet, called Estimation Sheet, when a button is press.
I managed to do this task for the first graphich (corresponding to first table) but when I try to use the same method for the second...it doesn't work. This is the method used to draw the first graphic:
Public Sub generateGraphicsC(RowResistiveC As Integer)
Dim FirstRow As Integer, FirstColumn As Integer, LastRow As Integer, LastColumn As Integer, GraphLocation As Integer
Dim XelementsC As Integer, Yelements As Integer
Dim myChtObj As ChartObject
Dim rngChtData As Range
Dim rngChtXVal As Range
Dim i As Integer
Dim WSD As Worksheet
Set WSD = Worksheets(2) 'Data source
Dim CSD As Worksheet
Set CSD = Worksheets(3) 'ChartOutput
'Dim chrt As ChartObject
'Dim cw As Long
'Dim rh As Long
' get the current charts so proper overwriting can happen Dim chtObjs As ChartObjects
Set chtObjs = CSD.ChartObjects
WSD.AutoFilterMode = False ' Turn off autofilter mode
'Dim finalRow As Long ' Find the last row with data
'finalRow = WSD.Cells(Application.Rows.Count, 1).End(xlUp).Row
FirstRow = RowResistiveC
FirstColumn = 5
XelementsC = countXelementsC(FirstRow - 1, FirstColumn) 'Count the x Elements (amperes)
Yelements = countYelements(FirstRow) 'Count the y Elements (Combinations)
LastRow = FirstRow + Yelements - 1 'The last row and column I will read
LastColumn = FirstColumn + XelementsC - 1
'---------------------DRAW THE GRAPHIC----------------------------------------------'
' Delete any previous existing chart
'Dim chtObj As ChartObject
' define the x axis values
WSD.Activate
Set rngChtXVal = WSD.Range(Cells(FirstRow - 1, FirstColumn), Cells(FirstRow - 1, LastColumn))
' add the chart
Charts.Add
With ActiveChart
' make a XY chart
.ChartType = xlXYScatterLines
' remove extra series
Do Until .SeriesCollection.Count = 0
.SeriesCollection(1).Delete
Loop
.Location Where:=xlLocationAsObject, Name:="Estimation Sheets"
End With
'-----------------------------------------------------------------------------
With ActiveChart
.HasTitle = True
.ChartTitle.Characters.Text = "Factor C"
'To Interpolate between the ungiven values
.DisplayBlanksAs = xlInterpolated
'TITLE STYLE
.ChartTitle.AutoScaleFont = False
With .ChartTitle.Font
.Name = "Calibri"
.FontStyle = "Bold"
.Size = 14
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ColorIndex = xlAutomatic
.Background = xlAutomatic
End With
'AXIS STYLE-----------------------------------------------------------------------
.Axes(xlCategory).TickLabels.AutoScaleFont = False
With .Axes(xlCategory).TickLabels.Font
.Name = "Arial"
.FontStyle = "Regular"
.Size = 10
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ColorIndex = xlAutomatic
.Background = xlAutomatic
With Selection.Border
.ColorIndex = 15
.LineStyle = xlContinuous
End With
End With
.Axes(xlValue).TickLabels.AutoScaleFont = False
With .Axes(xlValue).TickLabels.Font
.Name = "Calibri"
.FontStyle = "Regular"
.Size = 8
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ColorIndex = xlAutomatic
.Background = xlAutomatic
End With
End With
'-----------------------------------------------------------------------------
' HEIGHT; WIDTH AND POSITION
GraphLocation = CSD.Cells(Rows.Count, 2).End(xlUp).Row + 3
Dim RngToCover As Range
Set RngToCover = ActiveSheet.Range(Cells(GraphLocation, 2), Cells(GraphLocation + 20, 11))
With ActiveChart.Parent
.Height = RngToCover.Height ' resize
.Width = RngToCover.Width ' resize
.Top = RngToCover.Top ' reposition
.Left = RngToCover.Left ' reposition
End With
' for each row in the sheet
For i = FirstRow To LastRow
Dim chartName As String
' define chart data range for the row (record)
Set rngChtData = WSD.Range(WSD.Cells(i, FirstColumn), WSD.Cells(i, LastColumn))
'To get the serie name that I´m going to add to the graph
Dim serieName As String
Dim varItemName As Variant
WSD.Activate
varItemName = WSD.Range(Cells(i, 1), Cells(i, 4))
serieName = CStr(varItemName(1, 1) + " " + varItemName(1, 2) + " " + varItemName(1, 3) + " " + varItemName(1, 4))
' add series from selected range, column by column
CSD.ChartObjects.Select
With ActiveChart
With .SeriesCollection.NewSeries
.Values = rngChtData
.XValues = rngChtXVal
.Name = serieName
End With
End With
Next i
'We let as last view the page with all the info
CSD.Select
End Sub
I am calling this Sub from other one. The next step will be calling a similar method (exactly the same but other starting point to get the data and some different format properties)for other kind of table and graphic:
Public Sub printGraphics()
Modul4.ClearGraphs
Modul4.generateGraphicsC (RowResistiveC)
Modul4.generateGraphicsT (RowResistiveT)
End Sub
And so on. CountXelements and Yelements counts the number of elements from the Tables Sheet and RowResistiveC, for example, keeps the position of the table.
GenerateGraphicsC works but generateGraphicsT (exactly the same) crush in the line:
With .SeriesCollection.NewSeries
Whit error 91 ( I have a german version of excel at work but it's something like variable object or bloque object not given).
As I suspected the error came from :
CSD.ChartObjects.Select
That works in my solution for the first graph since I'm selecting the single graphic on the sheet, but when I add more it doesn´t.
I just changed that line for:
CSD.ChartObjects(1).Activate
and so on. It works perfectly. I also had to make some adjusments to avoid all the graphs being plotted over the previous one, but it works nice.