I have written a WinForms application in vb.NET using vs 2017. The application opens an excel Workbook which contains a document register, and updates the files as necessary.
I am very happy with the application, however one possible improvement I would like to pursue would be to attach the application to a macro in the Excel worksheet that the application updates, as the users of the application tend to open this worksheet by habit as it is.
I have the following code, which opens the application from the document register:
Sub AppMacro_Click()
Dim AppOpen
AppOpen = Shell("C:\Document Updater.exe", 1)
End Sub
The user can then run the application.
The problem with this method is that the application checks that no user has the workbook open, so that it can open the original copy (workbook is on a network drive), so if the user runs the macro, they will then have to close down the workbook before clicking the run button on the application. I would prefer if this didn't have to be the case.
What I would like to achieve is the following:
1) If the user runs the application using the macro, the application adapts and updates the opened workbook (as long as it isn't read-only)
2) If the user runs the application as usual, it still detects that no other user has the workbook open
What would be the best way to achieve this?
In general storing a workbook on a shared folder to be accessed/modified by multiple users is usually asking for trouble, you are better off using an Access DB if your users pool is fairly small or use full grown SQL (mySQL, SQL Server). Also since you have a working application in .NET where the user is very dependent on Excel, I would recommend you transition to an Excel Add-in. It is really easy and your solution would be more integrated with Excel
Related
After splitting my Access DB I have found that VBA code run in MS Project (called from within Access) has slowed down significantly (by a factor of about 6).
I am using MS Access 2016 and Project 2016, both on my local PC with no network traffic.
The VBA code in MS Project does a series of lookups against the Access database and it is these procedures that are running slowly. I would have expected a slowdown if my Access Backend was on a corporate network, but am surprised to see a slowdown when both back end and front end and MS Project are all local.
Any suggestions?
Thanks
Eric
Most likely, the slowdown you are experiencing is the backend file repeatedly opening and closing when it is being accessed by the frontend. The accepted solution is to open a persistent link between the frontend and the backend. This way the backend file will stay open for as long as a frontend is open.
To set this up, you will need to create an empty table in the backend that isn't used anywhere else in the project. I'll call it tblKeepOpen
Sample code: (called in the AutoExec macro)
Public Sub KeepBackendLinkOpen()
Static rsOpen As DAO.Recordset ' Static makes the Recordset reference persist
Set rsOpen = CurrentDb.OpenRecordSet("SELECT * FROM tblKeepOpen", dbOpenSnapshot)
End Sub
This reference will persist until the VBA interpreter crashes or loses state. You can reload the link by running KeepBackendLinkOpen from the Immediate window, or running AutoExec again.
Some people like to manage persistent links in a hidden form. Your choice.
PS -- there are lots of examples on the internet on how to do this, but I couldn't find any with a nice simple example.
Edited: Is it good practice to have a Store Procedure running retrieving data while the user keeps working while that data is compiled for them?
Old Question: I run a stored procedure to retrieve data from a server that takes about 10 - 15 minutes to complete. While running the stored procedure, my Excel workbook freezes until all the data has been returned. I might have an option to run this stored procedure in the background while allowing the Excel workbook to be workable. The idea is to give the user the possibility to work in the workbook while retrieving data instead of waiting 10 - 15 minutes just looking at the worksheet.
My question: I have never seen this idea before in any other application. I don't know if this means a good practice standard. Having the database loaded while working in the same workbook and when the data finishes alert the user that all the data has come through.
What do you guys think? Do you believe this could complicate something else?
I don't really like the idea but at lest the user wouldn't lose 10 - 15 minutes.
Thank you all :)
You need to create an instance of Excel application and run your stpred procedure there. In that case, your Excel application will still be available for the user, however I am expecting it to be slow. You have to modify your VBA code then to run in under a new instance of Excel application:
dim xlApp as Excel.Application
set xlApp=New Excel.Application
so here after to do your stuff you have to mention the application too since you have now more than one active application. For example to create a new workbook you would do:
xlApp.Workbooks.Add
I think you can probably open a new instance of Excel manually, then open your workbook that contains your VBA code using OPEN from FILE tab and then run your code, in that case the default Excel application will be available for the user to work. To open a new instance of Excel keep SHIFT key down and click on your Excel application icon (on your start menu or desktop etc, if you don't know where you can find it, simply go to Windows Start Menu and Search EXCEL)
Hope this helps!
END GAME: A user saved Workbook opens and mirrors code from a target file.
I am trying to create a simple VBA application that has an Excel front-end and an Access back-end. There will be multiple users who would have the option to save the front-end Excel piece anywhere they desire.
I would like to know the most efficient way to be able to update macros in all user instances when I need to push updates.
Essentially, I would like to mirror code from a "global" file on Workbook_open. In the past I did actually set code to open a separate workbook and run code (dim x as workbook, open, app.runmacro and etc.), But I think that is not really the most efficient way to do it.
Four possible solutions pop to mind for this (other than your option of having an intermediary workbook), there are likely others:
Treat the Workbook as purely an interface, and move the code to the
Access database and have it accept the Workbook as a parameter if
needed. The advantage would be the code could be maintained in one
place (Access), but it would have two main disadvantages. Each user
would need to have Access installed in order for it to instantiate
the application to call methods on, and it would lock in your
"interface" - that is, changes to how it calls Access macros would
still require Workbook updates.
Create a canonical Workbook and have the user Workbook version check
against the canonical Workbook when opened. If the version is
different, open the new one, move all of the data to it, delete the
old one, and save the new copy to the same filename as the old one.
The main disadvantage of this method would be ensuring that old code doesn't run might be difficult, as you would need
to take measures to prevent situations where the user could abort
the update process and still have a working copy of the old code.
Automate the VBE (see this answer for implementation details -
there are numerous resources on how to do this). Depending on how
you wanted to do this, you could either store the current modules as
files and import them, or store the code in the database itself and
query for it. The main disadvantages of this method are that the
VBE can be fickle about changing code that is actually running. I'm
not sure that I'd trust it to change it's own implementation. You
would also need to allow access to the VBE in each user's security
settings, which may pose a security threat.
Store the location of the Workbooks themselves in the database, then
push out updated copies with external code. The Workbook would
report it's filepath when opened, and if it wasn't already recorded
in the database, check to see if it was the most current version,
and then write a record for itself. This has the disadvantage of
only being able to inform the user that they don't have the current
version if they (for example) move the Workbook in Explorer and
don't open it until after your push.
Note that these are all "pull" type as opposed to "push" type solutions with the exception of the last one. Regardless of the method you use for version checking, any push solution is going to share the disadvantage of number 4 - there is no reliable way to make sure that a push catches all the invalidated versions.
As a temporary fix before we transfer the process over to our CRM system, I've written a tool with Excel/VBA that allows the user(s) to do some territory planning.
This tool opens a workbook that's located on a shared directory. It then pulls in data to the user's workbook on hidden sheets. The user then makes their desired changes via sheets they can see (there's a save button on these sheets that changes the appropriate data on the hidden sheets).
When the user is done making all desired changes, they hit a 'confirm' button. There is then a macro that reconnects to the workbook on the shared directory. It pushes the data back to this workbook on the shared directory. This workbook is [shared] so that more than one user can make changes concurrently, and the code is setup to prevent data from being overwritten inappropriately.
That said, I've come up with an annoying problem. We have offices in many locations internationally. It appears that the connection speed of users varies greatly. Users that have slower connections are encountering the following error:
'Microsoft Office Excel is waiting for another application to complete an OLE action.'
I suspect this is due to slower connections as microsoft's support site states that this error can come up when excel attempts to interact with another application which does not respond in a timely manner.
This error is coming up when the macro attempts to open the workbook in the shared directory, so connection speed is the main issue that makes sense.
I have discovered that I can block this error by using Application.DisplayAlerts = False but a better fix would be to make this process more efficient.
So my real question is, is there some more efficient method I can use to pull in that data, other than opening up the workbook?
Edit: Code where the worksheet opens the shared workbook:
Dim xlo As New Excel.Application
Dim xlw As New Excel.Workbook
Dim xlz As String, regions As String
xlz = Sheet1.Range("o1").Value & "\Region Planning\TestDB.xlsx"
Set xlw = xlo.Workbooks.Open(xlz)
Cell O1 contains the drive letter for the shared drive.
I am currently writing an utility that takes two different sets of data from an excel document and sends it to two different web services. Each set of data has it's own button that sends the data to the web service. When the a button is clicked, the corresponding web service then returns data depending on the input values.
What I am trying to do is so that this utility can be distrobuted and used by a wide variety of people. What they are going to do is they are going to be synchronizing there particular cells of their own excel document to the corresponding cell in my utility. Using simple excel formulas. For example:
c:\temp\[book1.xls]sheet1'!a1
Now what I am trying to do is automatically "click" the button, or update, on my utility when the user links their cell to its corresponding cell on my utility.
I have tried the selection change event on my page. But it doesn't actually process until my utility is active, or clicked on.
Is there a way to make my utility automatically update? It would be especially awesome if somehow it did this while my utility was closed. So when the user opens it it is already filled with their information, and the outputs are correct.
The requirements:
you need to guarantee that when a user is using excel your app can collect the data. Your app may not be running at the time. You need the Worksheet_SelectionChange() event to fire automatically not just when your app is active.
Solution:
Basically in order to guarantee that the Worksheet_SelectionChange() is running all the time, and in order to guarantee that whenever the user opens an instance of excel you are able to collect and process the data/formulas entered into the particular target range address you need to wire up the selectionchange event inside of a VB.NET Excel COM Addin. There are several ways to do this, but since your using VB.NET your best off using the newest approach which is building a VSTO COM Addin.
There are numerous other questons available on how to best build a VSTO addin (some in C# some in VB.NET but all this information is exactly the same for both, just different syntax).
So you will need two projects. Your existing one, plus a Excel VSTO addin (which you can also do in VB.NET).
Now inside the selectionchange event you should make sure that your code handles the target range correctly; ie. make sure you check for non-contigious groups of ranges with a selection... A1:B1;D2:E2 and copy the data to your app. You may consider using a format such as xml to serialise the latest data from excel so that if your app is not open at the time, it can deserialise and read back the data stored for it the next time it is opened. I guess it really depends how your handling the data (i.e. is it for loading into a datagrid?) and whether you are going to do further processing and store it somewhere else later (in which case maybe you can put it straight into a database from the addin in excel).