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.
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.
Related
I have come to a problem that I had kind of sorted, but it came back to haunt me.
I have a set of Excel files with information (origin) that feed an aggregator/wrangler/chewer in Excel (workbook) and then I have a set of files, one for each subsidiary (output), that have the same structure (sheets, hyperlinks, headers) but only get the relevant info for that subsidiary.
Right now I have everything automated, I download, prepare the information and update the origin files, then the only thing I need to do is open and save all the other documents (aggregator/subsidiary exports) and I do it with VBA.
In order to distribute the subsidiary files I have to come up with something that can have the same structure as an Excel workbook, but doesn't have the links to the aggregator, or the subsidiaries won't be able to see the information as there will be #REF errors everywhere (even on some sheets headers).
The solution that I put in place was exporting the files as MHTML (Single File Web Page), as it managed to keep the sheets and hyperlinks and everything as intended, while only needing to "open [subsidiary file].xlsx & save as [subsidiary file].mht", but then I came to learn that it is only possible to open this file type in IE, which not everyone has access to.
Then my problem is that I need an alternative to this solution while using only "plain" solutions as the IT department won't allow me to use anything more than standard administrative tools (standard MS Office suite).
I tried to replicate the subsidiary files by copying and pasting every sheet, but then I cannot come up with a way to keep every formatting (mainly header width) as intended when pasting as "values", or as "values and source formatting", and this approach doesn't escalate well.
Do you know of a way of saving the file as is but without the formulas, or preventing them from updating when the file reopens (even if you say no to "update links" prompt sometimes some cells ate updated, don't know why), or some different approach?
The idea is to generate a file that no matter who the user is, and does, is able to see the information, and cannot screw the file/info in it, and if possible keeping it "easy" to update with a "simple" macro.
Kind regards,
Flávio
I could not find an answer to this, neither on Stack or on the wild web. I have a sheet where I need users to be able to use Text to Columns, however, I also need to protect the sheet. Everything works fine if Excel automatically does this (from Text to Column "short-term memory"), but I cannot access the option when protection is enabled.
It is not that any text is spilling onto locked cells, it's just that the option is greyed out after protecting the sheet. I would appreciate a non-VBA answer as I do not want to use macros on a shared file (the server is extremely slow and even normal excels take ages to save). However, if absolutely necessary, can you ninjas please tell me how I can set it up so that this problem is solved with the least possible performance hit?
P.S.: I am pretty new to VBA (practically uninitiated, I prefer R for everything). Also, the shared server is basically a network folder, so it is not likely that it will cause any issues other than being super-sloth.
If you protect a sheet, then only unlocked cells can be edited, that is, users can change the cell manually.
That is the core and purpose of sheet protection.
In a protected sheet you will not be able to perform a text to columns manually.
Whether or not the file lives on a server is totally unrelated to using VBA for a solution.
The real question is: What are you trying to achieve? Your question is about running Text to Columns on a protected sheet, but if you step back from that particular approach, there may be other ways to achieve what you really need to do.
I have a excel file that I need to give to a client. It currently comprises of one worksheet but could easily have many more. On the sheet there is a graph and a selection of slicers.
The problem I have is that if I hand this over to the client, they'll see the underlying code and possibly not need our services going forward.
How do I protect the sheet/file and only allow them to view data and operate the slices without seeing any of the background workings?
They must not be able to save a copy, access the SQL code or save changes.
Thanks,
JJ
You can bolt down excel with the protection options so that people can't edit objects and can only select non-locked items. You can also password protect vba and force read-only open of the spreadsheet.
http://office.microsoft.com/en-gb/excel-help/password-protect-worksheet-or-workbook-elements-HP010078580.aspx
There are lots of protection options available but the best thing you can do is reference a report/webpage with the data on instead of doing a direct db connection. This will alleviate a lot of security risks whilst also blackboxing your database.
IMHO
Only lock your spreadsheet down enough to prevent silly mistakes - combined with your data being a blackbox, they will get a usable spreadsheet and be much happier with you than if they felt you were aggresively trying to withold stuff from them. They may run off with your spreadsheet but the extension of trust to them will help discourage the behaviour and at the end of the day, if someone wants to really harvest your IP, they will do so no matter how many controls you have in place.
you also can try to protect VBAProject at the VBA window, something like this:
and put a password
this way the user can manipulate the worksheet without seeing your code, additional to this, block your worksheet like #StephLocke saids.
Your Workbooks can always be vulnerable, but you can lessen the risk to corrupt your code.
PS. My Excel is Spanish but the options are at the same place.
It is possible to protect the worksheets and still allow to interact with the slicers (and only the slicers).
Set each slicer's properties to not "Locked" by right clicking the slicer and selecting "Size and Properties..." and unsetting the "Locked" property.
Protect all other worksheet elements including cells and Pivot Charts in a similar fashion (set their "Locked" property).
Finally, protect the worksheet via “Review” -> “Protect Sheet” and select "Use AutoFilter" as the only action in the selection "allow for all users of this worksheet to".
You might also confer to this answer.
I have an Excel sheet which is used for bug-tracking. Each client has their own .xlsx and each application for that client has its own sheet within the .xlsx. So multiple Excel files with multiple sheets, all in the same format.
All sheets have the same headings and some columns have data validation and conditional formatting. Occasionally, however, the layout/headings or values allowed in data-validated cells, etc. must change and I have to go through each sheet and manually make the changes.
Is it possible to have a master sheet from which other sheets will inherit headings and heading styles with all cells under particular headings having data validation and conditional formatting?
(Before this is suggested, I used to simply put everything in one sheet and use filters to show a particular client/application, but this became impractical when sharing and versioning the sheets with multiple people)
The term you are looking for is a template. You create the template and give that to your 'clients' to track bugs. If you make an update to the template and give it to the client, they can just copy/paste data into the new form.
In my opinion, you're going about this the wrong way. Excel is a spreadsheet programme, while it CAN be used as a 'list' of sorts, it is a poor choice for bug tracking. If you're stuck on Office applications, use an Access database or something that can actually give you a 'front end display' separate from the 'back end data'. There are many free bug tracking software programmes on the internet. Set one of them up and just have your clients log a bug there.
Using a template and then getting the clients to copy+paste the old data is one way, but its not exactly the safest method.
If you did want to distribute a new template to your users it would be a good idea to add some import functionality. So VBA handles copying the old data across.
If you (personally) could do the changes to the template manually, then you might also be able to create a workbook+macros to "patch" the source (or a copy of the source) data in-place.
With either approach you'd probably need to add something to the source workbook to keep track of what version they have and make sure they they import from and to the correct version to prevent unhappiness in the future.
Could you show an example of a change? before and after etc
As far as I understand cells with formulas in Microsoft Office Excel can contain calculated values when serialized and saved in Office Open XML formats (specifically SpreadsheetML). This most likely applies to other types of dependencies and functions of values from other cells (like charts, pivot tables, etc.). I most likely do something wrong, but when processing this XML documents (SpreadsheetML) by external tools, that do not use any .Net components or similar APIs provided by MS, but just directly manipulating XML, I get into a problem that when I modify some content of one of worksheets Excel will still use last generated values in cells containing formulas. So when user opens generated spreadsheet he sees modified data but all the calculated fields are outdated. Now the only thing that I could find (these days) on internet was this:
http://openxmldeveloper.org/discussions/formats/f/14/p/1561/4164.aspx
This is really not a preferable solution especially if it applies to any kind of calculated cells and objects (charts, etc.) as it means partially reimplementing some SpreadsheetML processor when you do not know exactly the structure of all worksheets.
I would hope there would certainly be either an option in Excel or a configuration in one of the SpreadsheetML parts to force recalculations or to mark cells dirty, but I couldn't find one yet.
There is an assumption that scripting would help, but my lack of knowledge of that area didn't brought me to any successful results yet as I'm not sure how to include scripts into SpreadsheetML worksheet. Though I found quite some examples how to trigger recalculation and how to add open event listeners.
The easiest way is to remove the calculated value from the cell (as also noted in the link you provided).
You do not have to know the exact structure of worksheets. Just remove all occurrences of <v>#VALUE!</v> in worksheets/sheet1.xml (so that other functions will not be affected).
press F9 to recalculate all open workbooks
Excel Recalculation
Perhaps your Calculation Mode for the workbook is getting set to manual. Force this mode to Automatic when you open the workbook by setting it to null in the code with the following:
public void SetAutomaticCalculationMode(WorkbookPart workbookPart1)
{
Workbook workbook1 = workbookPart1.Workbook;
CalculationProperties calculationProperties1=workbook1.GetFirstChild<CalculationProperties>();
calculationProperties1.CalculationMode = null;
}
This will correspond to Automatic calculation mode as seen in the Options of Excel 2007 client: