1

This is a bit of a niche question, as generally it would be preferable to protect the sheet, but I would argue in some cases you simply need to protect the contents of the cell to avoid accidental edits and deletions without limiting editability of the sheet as a whole through Protect Sheet, such as creating new filters.

My Issue:

I have a need to export Revit schedules through a script to an excel spreadsheet for editing, then to import back into Revit. This is because editing elements, especially rooms, can be taxing with the size of our projects and lack of intuitive and quick ways of sorting and replacing contents within Revit schedules. I created a macro that will save the Excel file's fullpath (UNC path for shared network drives), among other things, to a config file to essentially "link" the schedule to that Excel file. If an excel file is not found in the config file, then I create one with through OpenFileDialog() with CheckFileExists = false, and so arises the issue.

If I create a new Excel file and use the Microsoft.Office.Interop.Excel method sheet.Protect("password") when saving the file, the user who export to the newly created file needs to know where to look through the macro in order to unlock the sheet to do some setup that a protected sheet disallows. This would also allow them to alter any protected data, purposefully or otherwise, and compromise the validity of the data. What's worse, if/when I have time to make this into a more developed addin to Revit rather than a macro, the used password would be hidden, disallowing even more trustworthy, advanced users from setting up the sheet. If I allow the user to set the password on creation of the new Excel file, then they can just unlock it whenever they want.

The Need:

The data exported with this script contains an ElementId for the Revit element in addition to what is contained within the Revit schedule, which is needed to tie the Excel data back to a Revit element with 100% accuracy (this is the only unique property of an element that does not change while the element exists). If this is altered in a way that creates a duplicate value in the Excel file, the validity of the import is ruined, and a rollback necessitated (which increases in severity the longer this error goes unnoticed).

Simply hiding the ElmentId column does nothing to prevent user nosiness from unhiding it, and Protect Sheet disallows a small set of needed alterations to the sheet when a new Excel file is created.

So, I need to "protect" against accidental alterations the ElementId column, but still allow for the alteration of that small set of features disallowed by Protect Sheet.

Again, I realize this is pretty niche need and not generally a desired limitation for protecting data. I also realize this does nothing to protect against deletion of the row as with Protect Sheet. That said, my goal is to prevent the alteration of an ElementId value in Excel from being edited into another valid ElementId value, nothing more. As such, deletion of a row would simply mean that the Revit element associated with that specific ElementId would not receive any form of update upon importing the data, as the cell is either null which is skipped or contains an invalid ElementId which can't be associated to an existing element, thus protecting the validity of said element data.

pmackni
  • 307
  • 3
  • 12
  • So have a data sheet and then use the "very hidden" option that can be set in vba... – Solar Mike Aug 11 '21 at 19:55
  • Can this setting also be set through C# via the Interop library, or only through an internal VBA macro? If the latter, is there a way to run/trigger that through the Interop library? If this could be automated, this would allow me to do a couple other things to streamline to process, but I'm not as familiar with anything outside of reading/writing directly from/to the cells via Interop. Otherwise, I suppose I could create a template file that contains that type of sheet, and have my macro use that rather than opening a blank file. – pmackni Aug 12 '21 at 14:54
  • Have you checked the very hidden option in vba already? Then you might see if you can get to it with C#... – Solar Mike Aug 12 '21 at 15:31
  • Yes. I have used it before, but due to lack of need of it had forgotten about it. I checked the Excel.Interop documentation, and visibility is an exposed property, and it has three options, including `xlSheetVeryHidden`. What would you put on this hidden datasheet, though? I would think ElementId, which would make row deletion a concern in a way that it wasn't before. I know of a macro to prevent that, but that requires a .xlsm file, which I believe macros could then be disabled... – pmackni Aug 12 '21 at 16:41

1 Answers1

1

There are two solutions to this. The first was through my own experimentation. The second is a line of thought spawned thanks Solar Mike reminding me of an all too useful feature that I forgot about due to lack of use (generally do not have a need for it): Very Hidden Sheets. The avenue he seemed to be suggesting may not work exactly as he intended as, ideally, this Revit macro and Excel file combo should be usable by anyone in my company, but there is a method to make it work for anyone. Below are the two methods to resolve my issue, and hopefully anyone else in a similar bind that is, for some reason, unable to use Protect Sheet to do this job.


Solution 1: Data Validation

By setting Allow: Custom and Formula: "", I can trigger the Error Alert feature with Style set to "Stop" that will disallow any alterations.

Even better, if I want to disallow the deletion of cell data, unchecking the "Ignore blank" box causes an Error Alert upon hitting the Delete key on the cell as well.

Using this in conjunction with hiding the column obfuscates the process for editing these cells through sheer volume of steps to enable editing, discouraging users from bothering to mess with cell contents.

Pros:

  • Can utilize Input Message to warn users of the danger of altering cell contents
  • Can use Error Alert to outright block data entry or deletion of cell contents
  • Requires additional steps before people can alter values, using effort as a deterrent

Cons:

  • Does not protect against the following (read further for detailed descriptions):
    • Deletion of rows (offset by using this macro on the workbook in the Visual Basic editor)
    • Copy and paste another cell(s) over read-only cell(s) (working on a macro to eliminate this issue)
    • Alteration of cells through code or macro
  • Requires macros to mitigate some of the above limitations, which can be disabled and put data at risk of outlined issues.
  • Requires additional column (which can and likely should be hidden) in your data, which becomes obvious to those who look at column letters.
  • Does not prevent people from turning off data validation, should someone deem the effort worth it

Deletion of row:

This does not matter as much for me with this solution, as I am reading data from excel row by row, using the ElementId cell to find the Revit element. If an Excel row was deleted, the associated Revit element simply misses get data with that import, and is then re-added into the Excel file from the schdule upon the next export run, as long as the element still exists in Revit. But if row data needs to be maintained to avoid data corruption in your situation, you can use the linked macro to disable it.

Alteration by code:

At least external code; I have not tested this against a VBA macro from within Excel, but I assume the same to be true. I reason this is due to the fact that Data Validation is activated by a user interacting with the cell on an interface level, but when writing to the cell using the Microsoft.Office.Interop.Excel library in C# you are bypassing the interface and interacting directly with the cell data, thus not triggering Data Validation. I am not concerned about security or validation issues arising from this as few in our AEC firm know how to code, and the ones that do have no reason to interact with these files and/or lack the permission to access the folder (seperation of disciplines). While imperfect for most cases, this is the best solution I can come up with for our current needs.

Solution 2: Very Hidden Sheet

This method utilizes two features of Excel:

  • Very hidden sheet visibility setting
  • Macros

Specifically, the macro I am referring to is the link from Solution 1's Con section, Prevent Row/Column Removal.

The solution is to have your script write to two different sheets, one for your overall data, and the second for your unique identifiers. The writing of these should be coded in such a way that you would write to a line in each sheet before moving to the next one to ensure the identifier and data are placed on the same row and to reduce errors when editing the code in the future.

When saving the newly generated and format- and filter-less Excel file, set the identifier sheet's visibility to xlSheetVeryHidden, or 2. This setting should be exposed through the Interop.Excel interface as a worksheet property. This way, only advanced users who can use macros can unhide the sheet, and that's if they even know to look for it.

Pros:

  • Ensures unique identifier is accessible only to those explicitly told of its existence, and even then only to those with the ability or given the tools to access it
  • By utilizing the Row Deletion Prevention macro, identifier will always be paired with correct data, as long as code writes the data to Excel correctly
  • Is more secure than Data Validation by way of eliminating copy/paste as a risk

Cons:

  • Requires file to be saved as .xlsm, or Macro Enabled Workbook, which depending on your company's security settings may send a red flag to those who are unfamiliar with why the file is set this way
  • If macros are disabled via security settings, validity of data cannot be guaranteed as the row deletion prevention would be disabled

Final Thoughts

While I prefer Solution 2 and the usability it would provide, I can't in good conscience use it. Our firm's OOTB Excel install disables macros by default, and several people consider them non-essential to their tasks in the file and just ignore the warning at the top of the file all together. The risk of data becoming compromised purely because someone doesn't want to enable macros is too great.

I'd also like to add: If you are not in exactly the same situation as me, I do NOT readily recommend either solution. Protect Sheet is the strongest way to protect cell data, and does not disallow the use of existing Sort/Filter features. I am only using these as my go-to solutions due to the experience and/or knowledge level of those in the office who could potentially use my macro (and hopefully future addin), and recognize that neither route is a perfect defense.

If anyone has any other suggestions, I'd appreciate hearing them and I'll definitely update this answer if/when I have a better working version of either solution going.

pmackni
  • 307
  • 3
  • 12