I have a chart that I want user not to delete. Yet I'd like to give him option to move or resize it if he wants to, so I think, that option locked in properties is not appropriate here (I use secure worksheet too).
Is there any possibility here to make code that works when user clicks Del on the keyboard, then some msgBox with information with error, that this chart is not deletable appears and then discard this action of removing?
It is impossible to prevent Chart deletion, while simultaneously enabling it to be moved
There are two following options, which *sorta* achieve what you want:
Enable Sheet Protection (Review -> Protect Sheet) which will achieve the following:
prevent the Chart from being deleted
unfortunately also prevent chart from being moved
Use Application.OnKey to detect press of Delete
Private Sub Worksheet_Activate()
Application.OnKey "{DELETE}", "PreventDeletion"
End Sub
Private Sub PreventDeletion()
MsgBox ("Deletion not allowed in this worksheet")
End Sub
this allows the chart to be moved
unfortunately only creates an illusion of delete protection. If user uses mouse2 and selects the delete option, it will still be available to him. This only interrupts pressing the Delete
also this renders Delete obsolete for entire Sheet, which can be annoying (for the users)
Neither option is ideal, though personally I prefer the first one, as preventing usage of Delete altogether is bound to annoy your users.
Related
I want, when I press the return key, to move the selection one cell to the right.
I know this option can be changed under options in Excel.
I want to set this option programmatically so it only works with one workbook when it loads, but not with other workbooks.
So the option will be reset back to the original "down" action when the workbook is closed.
While maybe not completely recommended – using TAB would be how most people work this – You can change the option with the MoveAfterReturnDirection property.
So if you would have something like
Private Sub Workbook_WindowActivate(ByVal Wn As Excel.Window)
Application.MoveAfterReturnDirection = xlToRight
End Sub
Private Sub Workbook_WindowDeactivate(ByVal Wn As Excel.Window)
Application.MoveAfterReturnDirection = xlDown
End Sub
in the code for the workbook, that should accomplish something along the lines of what you are asking.
Notice that this will change the setting for anyone who are using the workbook, and if they had anything else than Down set, then that will change.
You could work around this by capturing the original value using a global variable, but I wouldn't bet my life on that it wouldn't mess up someones setting at some point.
Actually, testing the latter managed to somehow set my settings to "left", after closing the book.
An even worse way to do it would be to completely change the function of the Enter key, using the OnKey method. Making the Enter key return a Tab key press. The approach would be the same, but using OnKey and then creating a sub that uses something like SendKeys to return the Tab press.
I really don't know what causes error 400.
Below code runs perfectly fine in normal mode but as soon as i enable my excel in sharing mode and tries to user form, it gives me VBA 400.
What i am trying to do here is to change shape's text and disable its OnAction event, once user form is shown to user. so that another user accessing same file will come to know that someone is using "User Form" to enter data.
Dim shp As Shape
For Each shp In ActiveSheet.Shapes
If shp.TextEffect.Text = "Sort Customer" Then
shp.OnAction = ""
shp.TextEffect.Text = "Wait!!!"
End If
Next
Q. Is there any way to publish changes made by any user in shared excel automatically.
I suspect that your code falls in one of the numerous limitations of Excel shared mode, described here (see unsupported features), including
Using a data form to add new data
Using drawing tools
Inserting or changing pictures or other objects
(Please note that, due to its format, I could not easily copy that list of unsupported features in my answer.)
As far as I know, in order to keep the changes you should choose if the first one who introduces the data rules or you will choose in case of conflict. As you are looking for an "automatic" way, you should chose the first one.
You can find a good explanation described here
At Review > Share Workbook , Advanced Tab. At "Conflicting changes between users", you should chose "The changes being saved win". So as the data are introduced and saved, they are reflected.
Hope it helps.
Create a vba function in the sheet (NOT A MODULE) where users can activate the user form:
insert the following function there:
Function HyperlinkClick()
'source: https://stackoverflow.com/a/33114213/11971785
Set HyperlinkClick = Range("B2")
If HyperlinkClick.Value = "Sort Customer" Then
'sets info on WAIT
HyperlinkClick.Value = "WAIT!!!"
'shows userform
UserForm1.Show
Else
'sets info back to normal value
HyperlinkClick.Value = "Sort Customer"
End If
End Function
In the user form you can add an userform_terminate Event, which automatically changes the value in B2 back (I guess you could also do that for an workbook Close Event be on the safe side).
Private Sub userform_terminate()
'Code goes here
Range("B2").Value = "Sort Customer"
End Sub
In Excel now create a "Frontend" such as this:
and add the formula:
=HYPERLINK("#HyperlinkClick()";"Click")
to the cell where a user needs to click to open the UserForm (in this case to D2).
If you now share the workbook and click on "Click" in D2 an Event is triggered and the VBA Function "HyperlinkClick()" is called. In this function you can essentially do anything now.
Explaination:
Instead of using a graphic, button etc. which will not work correctly in shared mode, we can simply use links (which work) to trigger an Event.
Instead of "creating" and "deleting" Hyperlinks (which also does not work in shared mode) we simply build dynamic links which Point to userform.show or to nothing, depending of the situation.
Error 400 Problem: Should be solved by skipping the modify object part of the code.
Multiple User Problem: Should be solved, since only one user can activate the userform.
Is there any way to publish changes made by any user in shared excel automatically.: I guess so, please provide more information on what exactly you want to achive (incl. example).
Tip:
In General you might want to check out MS Access since it has as default feature multi-user Access and users there can use the same form at the same time, since the users only get exclusive Access for specific datapoints not the whole table/workbook or file.
Trying to work with a protected workbook (i have access to the password) with a VBA project (1 UserForm, 5 Class Modules, 1 Standard Module)
I'm really confused about how to properly work with protected sheets in VBA.
I've tried doing an
ActiveSheet.Unprotect PWD
followed after a method call with
ActiveSheet.protect PWD
Also tried
ActiveSheet.protect PWD, UserInterfaceOnly:=True
from the Workbook_open sub
The issues is at random points its like the protection reverts to being protected and the parts of my VBA project that are making changes to the actual spreadsheet freak out.
If i pause the debugger go over and manually unprotect the sheet and hit continue things go off without a hitch
So what's going on here
Is it not enough to unprotect the sheet at the beginning of a method that calls other methods that make changes to the sheet and reportected it at the end?
or do I literally have to prefix and postfix
ActiveSheet.Unprotect PWD
ActiveSheet.Protect PWD
around EVERY single line that changes the sheet data?
Because it seems like the protection cares nothing about anything my VBA project does internally ONLY when I try to do subsequent things with the worksheet
I can post code later today when I get home if its needed
Is it not enough to unprotect the sheet at the beginning of a method that calls other methods that make changes to the sheet and reportected it at the end?
This is why global state is such a pain in the neck. As far as your VBA project is concerned, sheet protection state is global. So if you do:
Public Sub Procedure1()
Sheet1.Unprotect PWD
Procedure2
'do stuff on Sheet1
Sheet1.Protect PWD
End Sub
If Procedure2 re-protects Sheet1 before it exits, then Sheet1 is protected again when Procedure1 resumes to do stuff on Sheet1, and as you're experiencing, it doesn't like it much.
So yes, you need to be sure that the sheet isn't protected before you try to modify it.
or do I literally have to prefix and postfix [...] around EVERY single line that changes the sheet data?
You don't. I mean, you do, but you don't, if you manage your global state in a sane way. If anyone anywhere at any time can go in and re-protect the sheet you just unprotected, your swear jar can fill up rather quickly.
Similarly, Application.Calculation is global; yet if anyone anywhere can set it back to xlCalculationAutomatic after you carefully made it xlCalculationManual in an effort to "improve performance" (same goes for Application.ScreenUpdating), then you'll ultimately find yourself triggering recalculations and introduce very noticeable lags instead.
Global state is nice (hey I can access it from anywhere!), but also double-edged. If you don't structure things properly, global state quickly spagghettifies and becomes a tangled mess of inefficient back-and-forth toggles that you don't need, or want.
The solution is to set yourself up for success, and layer your code properly. Have all the sheet-protection-toggling code in one place, and constrain yourself to only ever invoke that logic from one single layer - everything underneath isn't concerned with sheet protection, it's none of its business. If the sheet it means to work with is protected, then it's not its problem - code in the upper layer has a bug.
In the above example, Procedure2 would be layer 2, and it shouldn't be allowed to toggle sheet protection at all. Let Procedure1 be responsible for that, and move the do stuff on Sheet1 part to some new Procedure3, that 's just as carelessly assuming sheet protection is already handled.
You can even encapsulate the toggling in a class, e.g. Sheet1Protection:
Option Explicit
Private Sub Class_Initialize()
Sheet1.Unprotect PWD
End Sub
Private Sub Class_Terminate()
Sheet1.Protect PWD
End Sub
And now you can do this:
Public Sub Procedure1
With New Sheet1Protection 'unprotects sheet1
Procedure2
Procedure3
End With ' object terminates, sheet1 is protected again
End Sub
Notice that sheet protection is being toggled at the very last minute, when it's needed, and wraps all operations that need to run with the unprotected sheet.
With tooling such as Rubberduck (an OSS add-in project for the VBE, which I manage & contribute to) you can easily locate all the places where sheet protection is being toggled outside of Sheet1Protection, and remove them.
I don’t know if this is possible. I am working with Excel and using VB script. Is there a way to detect if two keys are being pressed at the same time and which ones they are? Then if it is those keys that are pressed I can use an If/Then statement to do whatever processes I need to do?
I know if I have something like a combo box I can use the keydown feature to detect a single key pressed, but this will only work on one key and not two keys. Also, I am not using combo boxes, text boxes, or anything else. I am strictly using cells, so there doesn’t appear to be anything like keydown to detect even the press of a single key.
Again, I would need for it to detect two keys at the same time being pressed. I would also somehow like to get it to detect this at the workbook level as well, instead of each individual worksheet, since there are several worksheets and would like these pressed keys to work from one sheet to the next.
Please let me know if this is possible or not, but I have a feeling it is not possible.
Doug,
Thanks for your suggestion, I figured everything out due to that. Here it is, in case someone else finds this useful:
Private Sub Workbook_Activate()
'When the workbook is active this will run the script in the place of the standard Ctrl + C.
Application.onkey "^{c}", "ThisWorkbook.cCopy"
End Sub
Private Sub Workbook_Deactivate()
'When another workbook is active this will disable this script so the standard Ctrl + C will work again.
Application.onkey "^{c}"
End Sub
Sub cCopy()
'This is the script to run when active. This was used for testing purposes only.
Worksheets("Sites").Range("I1").Value2 = "Yes"
End Sub
I have some data inserted at Rows 500 - 503. I placed the data intentionally at those rows because these rows will be used in Macro's and not for User's view. The data concerned to the user ends at Row 100.
I have hidden the Rows 500 - 503, yet after hitting Ctrl + End, the Cursor moves to Row 499.
My requirement is after hitting Ctrl + End, the Cursor should be at the last cell of Row 100.
So, how do i do this ?
EDIT : Thanks for all the answers. Many advised me to have another sheet for non user related data, but i can't have it. Also i am not supposed to have the vba scripts.(Should have given you these conditions before... my bad!)
This problem will be removed with better isolation.
That is, worksheets which are designed for use by a user should be restricted to containing only the elements necessary for the user interface. In this case, only the rows which the user is supposed to see.
Any data which needs to be persisted in worksheets which is not designed for direct user consumption should be on their own worksheets, which can even be hidden from the user completely.
The specific answer to your question of course is: trap the usage of the Ctrl-END key.
You can do this easily by specifying the following event triggers to your sheet:
Private Sub Worksheet_Activate()
Application.OnKey "^{END}", "SuppressMe"
End Sub
Private Sub Worksheet_Deactivate()
Application.OnKey "^{END}", ""
End Sub
and another module containing
Sub SuppressMe()
' you can be very creative here about line 100 or not 100
MsgBox "Co-Cooooo :-P"
End Sub
Another cute way meeting the requirements you described and eliminating the need of watching and suppressing keys would be to limit navigation to row 100. Now a Ctrl-END would place the user into the rightmost column of row 100, and Ctrl-DOWN would be covered as well. In fact the user just can't go past row 100
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Row > 100 Then
Me.Cells(100, Target.Column).Select
End If
End Sub
but .... wait !!! is there really no way? ... of course the user still may SCROLL past row 100 and detect your little row-500 easter egg. So I concur to 100% with earlier suggestions about a different strategy, because
a user finding out that exactly line 500 - 503 is hidden can become tremendeously curious about the why's
Ctrl-END is not the only way the user can navigate past row 100 - you would need to suppress END-DOWN (a 2 - key combination ... you need to buffer the END and ask the next key: are you a DOWN ... have fun!)
and you are wasting disk space to store a sheet with so many blank rows just for that line 500
You may have reasons to implement a key supressor, but always remember there are many different ways for the user to bypass it and this needs to be considered very carefully.
That's Excel's built-in behaviour, and I don't think you can change it. Suggestions:
Put your hidden data on another sheet of the same workbook. This is really the cleanest way to work.
Alternatively, navigate using CtrlDown arrow or EndDown arrow instead of CtrlEnd.
As others said it's certainly better to separate your non-user data on a different sheet.
That said, one thing you could do is protect your sheet and disallow access to locked cells, leaving only the cells in which the user has to input something unlocked.
Doing that prevents CtrlEnd from doing anything at all: it can't jump to the last cell as it is locked, so it doesn't do anything. (Tested on Excel 2007)
But again, you'll probably be better off by separating user and non-user data in different sheets.