Hi,
Dealing with timers in Excel is hard. If you want to provide data to
the Excel sheet, the right way to do it is to make an RTD Server. You
might make some progress using the xlcOnTime or Application.OnTime
built-in triggers. But trying to drive an Excel sheet via a timer will
most likely lead to tears.
With that warning, it is possible to fix your example so that it kind
of works. I paste another version below.
Here is your immediate problem:
You are using System.Timers.Timer which is a timer for use with worker
threads in a multithreaded environment, typically in a server
environment. You can read more in the remarks for this timer class
here:
http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx
and in this comparison of different timers in .NET:
http://msdn.microsoft.com/en-us/magazine/cc164015.aspx.
Excel is (mostly) a single-threaded environment so you need to take
care to run macros on the main Excel thread.
The easiest way to ensure this with a timer is to use the
System.Windows.Forms.Timer which will raise its events on the thread
that creates the timer. But just executing code on the right thread
will still not allow you to modify the worksheet with xlSet (which is
what ExcelReference.SetValue does). You need to be in a context where
Excel is ready to accept changes, for example in a macro that Excel is
running. Your timer event handler is not such a context. So to
initiate the macro that updates the sheet from the timer event, I use
the COM API to call Application.Run(...). But this call might fail if
Excel is 'busy' - e.g. if you press the mouse button on the sheet, or
start editing a cell.
------
The ExcelReference and XlCall types are helpers for the Excel C API
defined in the Excel SDK. If you are porting code from VBA or
somewhere that used the COM interfaces, you might like to use these
instead. The trick is to use the 'ExcelDnaUtil.Application' in your
code to get to the root Application object. From there you can use the
Workbook, Worksheet and Range classes as you would from VBA or through
COM. From your timer you'd still want to put all the interesting code
in a macro that is run from the timer event via Application.Run so
that you can do all the error handling in one place.
------
Anyway - this gives you some idea of the issues to consider.
-Govert
<DnaLibrary Name="ExcelDna Test AddIn">
<Project Language="VB">
<Reference Path="System.Windows.Forms.dll"/>
<![CDATA[
Imports System.Windows.Forms
Public Module MyTestFunctions
<ExcelCommand(MenuText:="Say &Hello")> _
Sub SayIt()
MessageBox.Show("Hellooooo!")
End Sub
<ExcelCommand(MenuName:="&My Menu", _
MenuText:="Start Timer")> _
Public Sub StartTimer()
Dim aTimer As New Timer()
AddHandler aTimer.Tick, AddressOf OnTimedEvent
' Set the Interval to 1 second.
aTimer.Interval = 1000
aTimer.Enabled = True
aTimer.Start()
End Sub
Public Sub OnTimedEvent(source As Object, e As EventArgs)
' All kind of errors might occur here...
Try
ExcelDnaUtil.Application.Run("UpdateCell")
Catch
'What to do now...?
End Try
End Sub
' Excel-DNA will register this as an Excel macro
Public Sub UpdateCell()
static i as integer
'Dim r as New ExcelReference(3,3)
Dim r as New ExcelReference(0,0,0,0,"Sheet1")
r.SetValue(i)
'Dim target as ExcelReference = New ExcelReference(3,3)