Editing a spreadsheet controlled Tabular Note

Is it possible to edit the contents of a cell in a spreadsheet controlled tabular note? I can read and write to a non spreadsheet controlled tab note, but I am only able to read from one that is spreadsheet controlled. The code does not error when "writing", it just does not change the text.

You can determine if a cell is driven by the spreadsheet by looking at the cell preferences. The code below shows how to do this.

To edit the spreadsheet values, I think you would have to extract the spreadsheet to a file, manipulate the values, and then store the spreadsheet back in the part - but I have not verified this workflow yet. The relevant commands would be .ExtractSpreadsheet and .StoreSpreadsheet.

Option Strict Off
Imports System
Imports System.Collections.Generic
Imports NXOpen
Imports NXOpen.UF

Module Module1

Dim theSession As Session = Session.GetSession()
Dim theUfSession As UFSession = UFSession.GetUFSession()

Dim workPart As Part = theSession.Parts.Work
Dim lw As ListingWindow = theSession.ListingWindow

Sub Main()

If IsNothing(theSession.Parts.BaseWork) Then
'active part required
Return
End If

lw.Open()

Const undoMarkName As String = "NXJ journal"
Dim markId1 As Session.UndoMarkId
markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, undoMarkName)

Dim myTabularNoteTags As New List(Of Tag)
If FindTabularNotes(myTabularNoteTags) = 0 Then
'no tabular notes to process
Return
End If

For Each tableNote As Tag In myTabularNoteTags

Dim numRows As Integer
theUfSession.Tabnot.AskNmRows(tableNote, numRows)

Dim numCols As Integer
theUfSession.Tabnot.AskNmColumns(tableNote, numCols)

Dim tableSectionTag As Tag
theUfSession.Tabnot.AskNthSection(tableNote, 0, tableSectionTag)

For i As Integer = 0 To numRows - 1
Dim rowTag As Tag
theUfSession.Tabnot.AskNthRow(tableNote, i, rowTag)

For j As Integer = 0 To numCols - 1
Dim colTag As Tag
theUfSession.Tabnot.AskNthColumn(tableNote, j, colTag)

Dim cellTag As Tag
theUfSession.Tabnot.AskCellAtRowCol(rowTag, colTag, cellTag)

Dim theCellPrefs As UFTabnot.CellPrefs
theUfSession.Tabnot.AskCellPrefs(cellTag, theCellPrefs)

Dim spreadSheetTag As Tag = Tag.Null
Dim sheetNum As Integer
Dim ssRow As Integer
Dim ssCol As Integer

spreadSheetTag = theCellPrefs.referenced_spreadsheet
If spreadSheetTag <> Tag.Null Then
lw.WriteLine("this cell references a spreadsheet cell")
sheetNum = theCellPrefs.ss_sheet
ssRow = theCellPrefs.ss_row
ssCol = theCellPrefs.ss_col
lw.WriteLine("Sheet: " & sheetNum.ToString & " row: " & ssRow.ToString & " column: " & ssCol.ToString)

End If

Dim cellText As String
theUfSession.Tabnot.AskCellText(cellTag, cellText)
lw.WriteLine("ask cell text: " & cellText)

Dim cellEvalText As String
theUfSession.Tabnot.AskEvaluatedCellText(cellTag, cellEvalText)
lw.WriteLine("ask evaluated text: " & cellEvalText)

lw.WriteLine("")

theUfSession.Tabnot.SetCellText(cellTag, "junk")

Next

Next

Next

lw.Close()

End Sub

Function FindTabularNotes(ByRef theTabNotes As List(Of Tag)) As Integer

Dim tmpTabNote As NXOpen.Tag = NXOpen.Tag.Null
Dim type As Integer
Dim subtype As Integer

Do
theUfSession.Obj.CycleObjsInPart(workPart.Tag, UFConstants.UF_tabular_note_type, tmpTabNote)
If tmpTabNote = NXOpen.Tag.Null Then
Continue Do
End If
If tmpTabNote <> NXOpen.Tag.Null Then
theUfSession.Obj.AskTypeAndSubtype(tmpTabNote, type, subtype)
If subtype = UFConstants.UF_tabular_note_subtype Then

theTabNotes.Add(tmpTabNote)

End If
End If
Loop Until tmpTabNote = NXOpen.Tag.Null

Return theTabNotes.Count

End Function

Public Function GetUnloadOption(ByVal dummy As String) As Integer

'Unloads the image immediately after execution within NX
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.Immediately

End Function

End Module

Any Idea what the spreadsheet name would be? The help file only lists:
"MODELING_SHEET" - modeling spreadsheet data
"DEFAULT_SHEET" - Gateway spreadsheet data
"PART_FAMILY_SHEET" - Part Family spreadsheet data

I tried the modeling and default sheets. Both times I got:
The first parameter passed in was invalid.
NXOpen.NXException: The first parameter passed in was invalid.
at NXOpen.UF.UFXs.ExtractSpreadsheet(String spreadsheet_name, String file_name)
at Redacted.RedactedForm.Replace(String RedactedItem, String SelectedReplacement, Boolean IsVisible) in C:\Users\Redacted\program.vb:line 545

The code below doesn't quite work, but it is close. The code looks at each tabular note; if the note in row 1, col 1 references a spreadsheet, it will attempt to modify the note. It successfully extracts and edits the spreadsheet; but it errors on the call to .StoreSpreadsheet. It doesn't seem to like the name of the spreadsheet even though it is the name that was extracted from the spreadsheet. This might be a case for GTAC to investigate. My guess is that the .StoreSpreadsheet method has not been updated to handle tabular note spreadsheets.

For what it's worth, here's the code (edit the value of "tempFile" to something appropriate for your setup before running it):

Option Strict Off
Imports System
Imports System.Collections.Generic
Imports NXOpen
Imports NXOpen.UF

Module Module2

Dim theSession As Session = Session.GetSession()
Dim theUfSession As UFSession = UFSession.GetUFSession()
Dim theUISession As UI = UI.GetUI

Dim workPart As Part = theSession.Parts.Work
Dim lw As ListingWindow = theSession.ListingWindow

Sub Main()

If IsNothing(theSession.Parts.BaseWork) Then
'active part required
Return
End If

lw.Open()

Const undoMarkName As String = "NXJ journal"
Dim markId1 As Session.UndoMarkId
markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, undoMarkName)

Dim myTabularNoteTags As New List(Of Tag)
If FindTabularNotes(myTabularNoteTags) = 0 Then
'no tabular notes to process
Return
End If

Const tempFile As String = "C:\Temp\sheetnote.xlsx"

For Each tableNote As Tag In myTabularNoteTags

Dim numRows As Integer
theUfSession.Tabnot.AskNmRows(tableNote, numRows)

Dim numCols As Integer
theUfSession.Tabnot.AskNmColumns(tableNote, numCols)

Dim tableSectionTag As Tag
theUfSession.Tabnot.AskNthSection(tableNote, 0, tableSectionTag)

'edit cell 0,0

Dim rowTag As Tag
theUfSession.Tabnot.AskNthRow(tableNote, 0, rowTag)

Dim colTag As Tag
theUfSession.Tabnot.AskNthColumn(tableNote, 0, colTag)

Dim cellTag As Tag
theUfSession.Tabnot.AskCellAtRowCol(rowTag, colTag, cellTag)

Dim theCellPrefs As UFTabnot.CellPrefs
theUfSession.Tabnot.AskCellPrefs(cellTag, theCellPrefs)

Dim spreadSheetTag As Tag = Tag.Null
Dim sheetNum As Integer
Dim ssRow As Integer
Dim ssCol As Integer

spreadSheetTag = theCellPrefs.referenced_spreadsheet
If spreadSheetTag <> Tag.Null Then
lw.WriteLine("this cell references a spreadsheet cell")

Dim ssObj As NXObject
ssObj = Utilities.NXObjectManager.Get(spreadSheetTag)
lw.WriteLine("Spread sheet name: " & ssObj.Name)

sheetNum = theCellPrefs.ss_sheet
ssRow = theCellPrefs.ss_row
ssCol = theCellPrefs.ss_col
lw.WriteLine("Sheet: " & sheetNum.ToString & " row: " & ssRow.ToString & " column: " & ssCol.ToString)

theUfSession.Xs.ExtractSpreadsheet(ssObj.Name, tempFile)

EditSpreadSheetNote(tempFile, theCellPrefs, "ss text edit")

'lw.WriteLine("storing Excel file")
theUfSession.Xs.StoreSpreadsheet(ssObj.Name, tempFile)

Else
'edit normal cell text
theUfSession.Tabnot.SetCellText(cellTag, "normal text edit")

End If

lw.WriteLine("")

Next

lw.Close()

End Sub

Function FindTabularNotes(ByRef theTabNotes As List(Of Tag)) As Integer

Dim tmpTabNote As NXOpen.Tag = NXOpen.Tag.Null
Dim type As Integer
Dim subtype As Integer

Do
theUfSession.Obj.CycleObjsInPart(workPart.Tag, UFConstants.UF_tabular_note_type, tmpTabNote)
If tmpTabNote = NXOpen.Tag.Null Then
Continue Do
End If
If tmpTabNote <> NXOpen.Tag.Null Then
theUfSession.Obj.AskTypeAndSubtype(tmpTabNote, type, subtype)
If subtype = UFConstants.UF_tabular_note_subtype Then

theTabNotes.Add(tmpTabNote)

End If
End If
Loop Until tmpTabNote = NXOpen.Tag.Null

Return theTabNotes.Count

End Function

Sub EditSpreadSheetNote(ByVal ssFile As String, ByVal theCellPrefs As UFTabnot.CellPrefs, ByVal newText As String)

Dim objExcel As Object
Dim objWorkbook As Object
Dim objWorksheet As Object

Dim markId1 As Session.UndoMarkId
markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, "Excel import points")

Dim excelFileExists As Boolean = False
'cell pref row seems to be "one" based while the column seems to be "zero" based
Dim row As Long = theCellPrefs.ss_row
Dim column As Long = theCellPrefs.ss_col + 1

'lw.WriteLine("row: " & row.ToString & " column: " & column.ToString)

'create Excel object
Try
objExcel = CreateObject("Excel.Application")
Catch ex As Exception
'handle error, warn user
Return
End Try

If objExcel Is Nothing Then
theUISession.NXMessageBox.Show("Error", NXMessageBox.DialogType.Error, "Could not start Excel, journal exiting")
theSession.UndoToMark(markId1, "journal")
Exit Sub
End If

If IO.File.Exists(ssFile) Then
'Open the Excel file
excelFileExists = True
objWorkbook = objExcel.Workbooks.Open(ssFile)
Else
theUISession.NXMessageBox.Show("Error", NXMessageBox.DialogType.Error, "Excel file does not exist")
theSession.UndoToMark(markId1, "journal")
Exit Sub
End If

If objWorkbook Is Nothing Then
theUISession.NXMessageBox.Show("Error", NXMessageBox.DialogType.Error, "Could not open Excel file: " & ssFile & ControlChars.NewLine & "journal exiting.")
theSession.UndoToMark(markId1, "journal")
Exit Sub
End If

objExcel.Visible = True

objWorksheet = objWorkbook.WorkSheets(theCellPrefs.ss_sheet)

objWorksheet.cells(row, column).value = newText

objWorkbook.Save()
objWorkbook.Close()

objExcel.Quit()
objWorksheet = Nothing
objWorkbook = Nothing
objExcel = Nothing

End Sub

Public Function GetUnloadOption(ByVal dummy As String) As Integer

'Unloads the image immediately after execution within NX
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.Immediately

End Function

End Module

I spoke with GTAC this morning.
I have confirmed that we do not have any way to do this using NXOpen. The extract/store spreadsheet functions are only expected to work with three specific spreadsheets, as documented in the Open C Reference for the original C function from which
the wrapper method is created:

Spreadsheet object name:
"MODELING_SHEET" - modeling spreadsheet data
"DEFAULT_SHEET" - Gateway spreadsheet data
"PART_FAMILY_SHEET" - Part Family spreadsheet data

While you might be able to successfully extract a spreadsheet with a different name - such as TABNOT_SPREADSHEET2 - The related Store function is not going to work with names other than what is shown in the docs. This is why you are seeing the
error message about "The first parameter passed in was invalid.
at NXOpen.UF.UFXs.StoreSpreadsheet(String spreadsheet_name..." -- it will only accept one of the names shown in the documentation.

This IR was converted into an enhancement request (ER) on 20-OCT-2015
and is now referenced as ER Number 7540975. It is OPEN for review at this time.

They suggested to convert the tabular notes from spreadsheet controlled via ConvertToNonSpreadsheetTabnote().

Thank you for your help.