diff --git a/Source/ExcelDna.IntelliSense/ExcelDna.IntelliSense.csproj b/Source/ExcelDna.IntelliSense/ExcelDna.IntelliSense.csproj
index 4e2e07a..0edbcfc 100644
--- a/Source/ExcelDna.IntelliSense/ExcelDna.IntelliSense.csproj
+++ b/Source/ExcelDna.IntelliSense/ExcelDna.IntelliSense.csproj
@@ -88,6 +88,7 @@
+
diff --git a/Source/ExcelDna.IntelliSense/UIMonitor/FormulaEditWatcher.cs b/Source/ExcelDna.IntelliSense/UIMonitor/FormulaEditWatcher.cs
index c7a9eff..dd71b67 100644
--- a/Source/ExcelDna.IntelliSense/UIMonitor/FormulaEditWatcher.cs
+++ b/Source/ExcelDna.IntelliSense/UIMonitor/FormulaEditWatcher.cs
@@ -1,4 +1,5 @@
-using System;
+using ExcelDna.IntelliSense.Util;
+using System;
using System.Diagnostics;
using System.Threading;
using System.Windows;
@@ -71,11 +72,15 @@ public IntPtr FormulaEditWindow
readonly SynchronizationContext _syncContextMain;
readonly WindowWatcher _windowWatcher; // Passed in
- WindowLocationWatcher _windowLocationWatcher; // Managed here
- IntPtr _hwndFormulaBar;
- IntPtr _hwndInCellEdit;
- FormulaEditFocus _formulaEditFocus;
+ WindowLocationWatcher _windowLocationWatcher; // Managed here
+ readonly RenewableDelayExecutor _updateEditStateAfterTimeout;
+
+ IntPtr _hwndFormulaBar;
+ IntPtr _hwndInCellEdit;
+ FormulaEditFocus _formulaEditFocus;
+
+ const int DelayBeforeStateUpdateMilliseconds = 100;
public FormulaEditWatcher(WindowWatcher windowWatcher, SynchronizationContext syncContextAuto, SynchronizationContext syncContextMain)
{
@@ -84,6 +89,7 @@ public FormulaEditWatcher(WindowWatcher windowWatcher, SynchronizationContext sy
_windowWatcher = windowWatcher;
_windowWatcher.FormulaBarWindowChanged += _windowWatcher_FormulaBarWindowChanged;
_windowWatcher.InCellEditWindowChanged += _windowWatcher_InCellEditWindowChanged;
+ _updateEditStateAfterTimeout = new RenewableDelayExecutor(DelayBeforeStateUpdateMilliseconds, () => UpdateEditState());
}
// Runs on the Automation thread
@@ -95,13 +101,13 @@ void _windowWatcher_FormulaBarWindowChanged(object sender, WindowWatcher.WindowC
if (e.ObjectId == WindowWatcher.WindowChangedEventArgs.ChangeObjectId.Self)
{
SetEditWindow(e.WindowHandle, ref _hwndFormulaBar);
- UpdateEditState();
+ _updateEditStateAfterTimeout.Signal();
}
else if (e.ObjectId == WindowWatcher.WindowChangedEventArgs.ChangeObjectId.Caret)
{
// We expect this on every text change
// NOTE: Not anymore after some Excel / Windows update
- UpdateEditStateDelayed();
+ _updateEditStateAfterTimeout.Signal();
}
else
{
@@ -126,7 +132,7 @@ void _windowWatcher_FormulaBarWindowChanged(object sender, WindowWatcher.WindowC
SetEditWindow(e.WindowHandle, ref _hwndFormulaBar);
}
_formulaEditFocus = FormulaEditFocus.FormulaBar;
- UpdateEditState();
+ _updateEditStateAfterTimeout.Signal();
}
break;
case WindowWatcher.WindowChangedEventArgs.ChangeType.Unfocus:
@@ -134,21 +140,21 @@ void _windowWatcher_FormulaBarWindowChanged(object sender, WindowWatcher.WindowC
{
Logger.WindowWatcher.Verbose($"FormulaEdit - FormulaBar Unfocus");
_formulaEditFocus = FormulaEditFocus.None;
- UpdateEditState();
+ _updateEditStateAfterTimeout.Signal();
}
break;
case WindowWatcher.WindowChangedEventArgs.ChangeType.Show:
- Logger.WindowWatcher.Verbose($"FormulaEdit - FormulaBar Show");
+ Logger.WindowWatcher.Verbose($"FormulaEdit - FormulaBar Show");
break;
case WindowWatcher.WindowChangedEventArgs.ChangeType.Hide:
- Logger.WindowWatcher.Verbose($"FormulaEdit - FormulaBar Hide");
+ Logger.WindowWatcher.Verbose($"FormulaEdit - FormulaBar Hide");
break;
case WindowWatcher.WindowChangedEventArgs.ChangeType.LocationChange:
if (e.ObjectId == WindowWatcher.WindowChangedEventArgs.ChangeObjectId.Caret)
{
// We expect this on every text change in newer Excel versions
Debug.Print($"-#-#-#- Text Changed ... ");
- UpdateEditStateDelayed();
+ _updateEditStateAfterTimeout.Signal();
}
else
{
@@ -172,14 +178,14 @@ void _windowWatcher_InCellEditWindowChanged(object sender, WindowWatcher.WindowC
if (e.ObjectId == WindowWatcher.WindowChangedEventArgs.ChangeObjectId.Self)
{
SetEditWindow(e.WindowHandle, ref _hwndInCellEdit);
- UpdateEditState();
+ _updateEditStateAfterTimeout.Signal();
}
else if (e.ObjectId == WindowWatcher.WindowChangedEventArgs.ChangeObjectId.Caret)
{
// We expect this on every text change
// NOTE: Not anymore after some Excel / Windows update
Debug.Print($"-#-#-#- Text Changed ... ");
- UpdateEditStateDelayed();
+ _updateEditStateAfterTimeout.Signal();
}
else
{
@@ -206,7 +212,7 @@ void _windowWatcher_InCellEditWindowChanged(object sender, WindowWatcher.WindowC
Logger.WindowWatcher.Verbose($"FormulaEdit - InCellEdit Focus");
_formulaEditFocus = FormulaEditFocus.InCellEdit;
- UpdateEditState();
+ _updateEditStateAfterTimeout.Signal();
}
break;
case WindowWatcher.WindowChangedEventArgs.ChangeType.Unfocus:
@@ -214,7 +220,7 @@ void _windowWatcher_InCellEditWindowChanged(object sender, WindowWatcher.WindowC
{
Logger.WindowWatcher.Verbose($"FormulaEdit - InCellEdit Unfocus");
_formulaEditFocus = FormulaEditFocus.None;
- UpdateEditState();
+ _updateEditStateAfterTimeout.Signal();
}
break;
case WindowWatcher.WindowChangedEventArgs.ChangeType.Show:
@@ -228,7 +234,7 @@ void _windowWatcher_InCellEditWindowChanged(object sender, WindowWatcher.WindowC
{
// We expect this on every text change in newer Excel versions
Debug.Print($"-#-#-#- Text Changed ... ");
- UpdateEditStateDelayed();
+ _updateEditStateAfterTimeout.Signal();
}
else
{
@@ -313,22 +319,20 @@ void _windowLocationWatcher_LocationChanged(object sender, EventArgs e)
// UpdateFormula(textChangedOnly: true);
//}
- // TODO: Get rid of this somehow - added to make the mouse clicks in the in-cell editing work, by delaying the call to the PenHelper
- void UpdateEditStateDelayed()
+ void UpdateEditState(bool moveOnly = false)
{
- _syncContextAuto.Post(_ =>
- {
- Thread.Sleep(100);
- UpdateEditState();
- }, null);
+ // Switches to our Main UI thread, updates current state and raises StateChanged event
+ _syncContextMain.Post(_ =>
+ {
+ UpdateEditStateImpl(moveOnly);
+ }, null);
}
- // Switches to our Automation thread, updates current state and raises StateChanged event
- void UpdateEditState(bool moveOnly = false)
+ void UpdateEditStateImpl(bool moveOnly = false)
{
Logger.WindowWatcher.Verbose($"> FormulaEdit UpdateEditState - Thread {Thread.CurrentThread.ManagedThreadId}");
Logger.WindowWatcher.Verbose($"FormulaEdit UpdateEditState - Focus: {_formulaEditFocus} Window: {(_formulaEditFocus == FormulaEditFocus.FormulaBar ? _hwndFormulaBar : _hwndInCellEdit)}");
-
+
IntPtr hwnd = IntPtr.Zero;
bool prefixChanged = false;
if (_formulaEditFocus == FormulaEditFocus.FormulaBar)
@@ -410,6 +414,9 @@ public void Dispose()
Debug.Assert(Thread.CurrentThread.ManagedThreadId == 1);
Logger.WindowWatcher.Verbose("FormulaEdit Dispose Begin");
+
+ _updateEditStateAfterTimeout.Dispose();
+
_windowWatcher.FormulaBarWindowChanged -= _windowWatcher_FormulaBarWindowChanged;
_windowWatcher.InCellEditWindowChanged -= _windowWatcher_InCellEditWindowChanged;
@@ -419,7 +426,8 @@ public void Dispose()
{
tempWatcher.Dispose();
}
+
Logger.WindowWatcher.Verbose("FormulaEdit Dispose End");
}
}
-}
+}
\ No newline at end of file
diff --git a/Source/ExcelDna.IntelliSense/Util/RenewableDelayExecutor.cs b/Source/ExcelDna.IntelliSense/Util/RenewableDelayExecutor.cs
new file mode 100644
index 0000000..405cdee
--- /dev/null
+++ b/Source/ExcelDna.IntelliSense/Util/RenewableDelayExecutor.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Timers;
+
+namespace ExcelDna.IntelliSense.Util
+{
+ ///
+ /// Upon a signal, executes the specified action after the specified delay.
+ /// If any other signal arrives during the waiting period, the delay interval begins anew.
+ ///
+ internal class RenewableDelayExecutor : IDisposable
+ {
+ private readonly Timer _timer;
+ private readonly Action _action;
+ private readonly int _debounceIntervalMilliseconds;
+
+ public bool IsDisposed { get; private set; }
+
+ public RenewableDelayExecutor(int debounceIntervalMilliseconds, Action action)
+ {
+ _action = action;
+ _debounceIntervalMilliseconds = debounceIntervalMilliseconds;
+ _timer = new Timer
+ {
+ AutoReset = false,
+ Interval = _debounceIntervalMilliseconds,
+ };
+
+ _timer.Elapsed += OnTimerElapsed;
+ }
+
+ private void OnTimerElapsed(object sender, ElapsedEventArgs e)
+ {
+ if (IsDisposed)
+ {
+ return;
+ }
+
+ _action();
+ }
+
+ public void Signal()
+ {
+ _timer.Stop();
+ _timer.Start();
+ }
+
+ private void Dispose(bool isDisposing)
+ {
+ IsDisposed = true;
+
+ _timer.Elapsed -= OnTimerElapsed;
+ _timer.Dispose();
+
+ if (isDisposing)
+ {
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ public void Dispose() => Dispose(true);
+
+ ~RenewableDelayExecutor()
+ {
+ Dispose(false);
+ }
+ }
+}
\ No newline at end of file