public void Unsubscribe() { Debug.Assert(_currentSubscription != null); _currentSubscription.Dispose(); _currentSubscription = null; _currentObserver = null; }
// Under unpatched Excel 2010, we rely on the ExcelRtd2010BugHelper to ensure we get a good Unsubscribe... public void Unsubscribe() { Debug.Assert(_currentSubscription != null); _currentSubscription.Dispose(); _currentSubscription = null; _callerState.RemoveObserver(_currentObserver); _currentObserver = null; }
public void RemoveObserver(ExcelRtdObserver observer) { _observers.Remove(observer); if (_observers.Count == 0 && _caller != null) { _callerStates.Remove(_caller); } }
internal static void ConnectObserver(Guid id, ExcelRtdObserver rtdObserver) { // TODO: Checking...(huh?) AsyncObservableState state = _observableStates[id]; // Start the work for this AsyncCallInfo, and subscribe the topic to the result state.Subscribe(rtdObserver); }
protected override object ConnectData(Topic topic, IList <string> topicInfo, ref bool newValues) { Debug.Print("ExcelObserverRtdServer.ConnectData: ProgId: {0}, TopicId: {1}, TopicInfo: {2}, NewValues: {3}", RegisteredProgId, topic.TopicId, topicInfo[0], newValues); // The topic might be "completed" on a separate thread, so we need to return the initial, intended active value // Mostly this is TopicValueActive, but sometimes TopicValueActiveNA // Rather than store this for every topic, we just recalculate it here object valueActive = ValueActiveFromTopicInfo(topicInfo); if (newValues == false) { // Excel has a cached value, and we are being called from the file open refresh. // Indicating "newValues", should be safe since it is consistent with normal updates. // Result should be a Disconnect followed by a proper Connect via the wrapper. newValues = true; return(valueActive); } // Retrieve and store the GUID from the topic's first info string - used to hook up to the Async state Guid id = ((ObserverRtdTopic)topic).Id; // Create a new ExcelRtdObserver, for the Topic, which will listen to the Observable // (Internally this will also set the initial value of the Observer wrapper to #N/A) ExcelRtdObserver rtdObserver = new ExcelRtdObserver(topic); // ... and subscribe it AsyncObservableImpl.ConnectObserver(id, rtdObserver); // Now ConnectData needs to return some value, which will only be used by Excel internally (and saved in the book's RTD topic value). // Our wrapper function (ExcelAsyncUtil.Run or ExcelAsyncUtil.Observe) will return #N/A no matter what we return here. // However, it seems that Excel handles the special 'busy' error #N/A here (return ExcelErrorUtil.ToComError(ExcelError.ExcelErrorNA)) // in a special way (<tp t="e"><v>#N/A</v> in volatileDependencies.xml) - while other values seem to trigger a recalculate on file open, // when Excel attempts to restart the RTD server and fails (due to transient ProgId). // So for the ObserverRtdTopic we ensure the internal value is not an error, // (it is initialized to TopicValueActive) // which we return from here. // 2017-03-19: Except if the topic is configured as ExcelObservableOptions.NoAutoStartOnOpen, in which case we _do_ want // the internal value to be #N/A. We return whichever ective value was set in the CreateTopic according to the topic info. // 2016-11-04: We are no longer returning the current value of topic.Value here. // Since calls to UpdateValue inside the ConnectData no longer raise an // UpdateNotify automatically, we need to ensure a different value // is returned for a completed topic (so that ConnectData.returned != topic.Value) // to raise an extra UpdateNotify, for the Disconnect of the already completed topic // (I.e. if the completion happened during the ConnectData call). return(valueActive); }
protected override object ConnectData(Topic topic, IList <string> topicInfo, ref bool newValues) { // Retrieve and store the GUID from the topic's first info string - used to hook up to the Async state Guid id = new Guid(topicInfo[0]); _topicGuids[topic] = id; // Create a new ExcelRtdObserver, for the Topic, which will listen to the Observable // (Internally also set initial value - #N/A for now) ExcelRtdObserver rtdObserver = new ExcelRtdObserver(topic); // ... and subscribe it AsyncObservableImpl.ConnectObserver(id, rtdObserver); // Return something: #N/A for now. Not currently used. // TODO: Allow customize? return(ExcelErrorUtil.ToComError(ExcelError.ExcelErrorNA)); }
// Safe to call with an invalid Id, but that's not expected. internal static void ConnectObserver(Guid id, ExcelRtdObserver rtdObserver) { Debug.Print("AsyncObservableImpl.ConnectObserver - Id: {0}", id); // It's an error if the id is not on the list - the ExcelObserverRtdServer should protect is from the onw known case // - when RTD is called from sheet open AsyncObservableState state; if (_observableStates.TryGetValue(id, out state)) { // Start the work for this AsyncCallInfo, and subscribe the topic to the result state.Subscribe(rtdObserver); } else { // Not expected - the ExcelObserverRtdServer should have protected us, // since the only invalid id call would be for sheet open with direct RTD refresh. Debug.Fail("AsyncObservableImpl.ConnectObserver - Invalid Id: " + id); } }
protected override object ConnectData(Topic topic, IList <string> topicInfo, ref bool newValues) { Debug.Print("ExcelObserverRtdServer.ConnectData: ProgId: {0}, TopicId: {1}, TopicInfo: {2}, NewValues: {3}", RegisteredProgId, topic.TopicId, topicInfo[0], newValues); if (newValues == false) { // Excel has a cached value, and we are being called from the file open refresh. // Calling UpdateNotify here seems to work (it causes the wrapper function to recalc, // which Disconnects the bad topic, and allows a fresh one to be created) // Not needed if we return a new 'fake' value, which is safe since it is consistent with normal updates. // Result should be a Disconnect followed by a proper Connect via the wrapper. // topic.UpdateNotify(); newValues = true; return(DateTime.UtcNow.ToOADate()); } // Retrieve and store the GUID from the topic's first info string - used to hook up to the Async state Guid id = ((ObserverRtdTopic)topic).Id; // Create a new ExcelRtdObserver, for the Topic, which will listen to the Observable // (Internally this will also set the initial value to #N/A) ExcelRtdObserver rtdObserver = new ExcelRtdObserver(topic); // ... and subscribe it AsyncObservableImpl.ConnectObserver(id, rtdObserver); // Now ConnectData needs to return some value, which will only be used by Excel internally (and saved in the book's RTD topic value). // Our wrapper function (ExcelAsyncUtil.Run or ExcelAsyncUtil.Observe) will return #N/A no matter what we return here. // However, it seems that Excel handles the special 'busy' error #N/A here (return ExcelErrorUtil.ToComError(ExcelError.ExcelErrorNA)) // in a special way (<tp t="e"><v>#N/A</v> in volatileDependencies.xml) - while other values seem to trigger a recalculate on file open, // when Excel attempts to restart the RTD server and fails (due to transient ProgId). // So we already return the same kind of value we'd return for updates, putting Excel into the 'value has been updated' state // even if the sheet is saved. That will trigger a proper formula recals on file open. return(DateTime.UtcNow.ToOADate()); }
public void Subscribe(ExcelRtdObserver rtdObserver) { _currentObserver = rtdObserver; _callerState.AddObserver(_currentObserver); _currentSubscription = _observable.Subscribe(rtdObserver); }
public void AddObserver(ExcelRtdObserver observer) { _observers.Add(observer); }
protected override object ConnectData(Topic topic, IList<string> topicInfo, ref bool newValues) { // Retrieve and store the GUID from the topic's first info string - used to hook up to the Async state Guid id = new Guid(topicInfo[0]); _topicGuids[topic] = id; // Create a new ExcelRtdObserver, for the Topic, which will listen to the Observable // (Internally also set initial value - #N/A for now) ExcelRtdObserver rtdObserver = new ExcelRtdObserver(topic); // ... and subscribe it AsyncObservableImpl.ConnectObserver(id, rtdObserver); // Return something: #N/A for now. Not currently used. // TODO: Allow customize? return ExcelErrorUtil.ToComError(ExcelError.ExcelErrorNA); }
public void Subscribe(ExcelRtdObserver rtdObserver) { _currentObserver = rtdObserver; _currentSubscription = _observable.Subscribe(rtdObserver); }