@@ -209,6 +209,7 @@ void SetEditWindow(IntPtr newWindowHandle, ref IntPtr hwnd, ref AutomationElemen
209209 try
210210 {
211211 UninstallTextChangeMonitor ( element ) ;
212+ UninstallLocationMonitor ( ) ;
212213 Logger . WindowWatcher . Verbose ( $ "FormulaEdit Uninstalled event handlers for { hwnd } ") ;
213214 }
214215 catch ( Exception ex )
@@ -230,6 +231,8 @@ void SetEditWindow(IntPtr newWindowHandle, ref IntPtr hwnd, ref AutomationElemen
230231 try
231232 {
232233 InstallTextChangeMonitor ( element ) ;
234+ if ( IsEditingFormula )
235+ InstallLocationMonitor ( GetTopLevelWindow ( element ) ) ;
233236 Logger . WindowWatcher . Verbose ( $ "FormulaEdit Installed event handlers for { newWindowHandle } ") ;
234237 }
235238 catch ( Exception ex )
@@ -317,6 +320,36 @@ void FormulaPollingCallback(object _unused_)
317320 }
318321 }
319322
323+ WindowLocationWatcher _windowLocationWatcher ;
324+
325+ // Runs on our Automation thread
326+ void InstallLocationMonitor ( IntPtr hWnd )
327+ {
328+ if ( _windowLocationWatcher != null )
329+ {
330+ _windowLocationWatcher . Dispose ( ) ;
331+ }
332+ _windowLocationWatcher = new WindowLocationWatcher ( hWnd , _syncContextAuto ) ;
333+ _windowLocationWatcher . LocationChanged += _windowLocationWatcher_LocationChanged ;
334+ }
335+
336+ // Runs on our Automation thread
337+ void UninstallLocationMonitor ( )
338+ {
339+ if ( _windowLocationWatcher != null )
340+ {
341+ _windowLocationWatcher . Dispose ( ) ;
342+ _windowLocationWatcher = null ;
343+ }
344+ }
345+
346+ // Runs on our Automation thread
347+ void _windowLocationWatcher_LocationChanged ( object sender , EventArgs e )
348+ {
349+ UpdateEditState ( moveOnly : true ) ;
350+ _windowWatcher . OnFormulaEditLocationChanged ( ) ;
351+ }
352+
320353 // Runs on an Automation event thread
321354 // CONSIDER: With WinEvents we could get notified from main thread ... ?
322355 void TextChanged ( object sender , AutomationEventArgs e )
@@ -326,15 +359,7 @@ void TextChanged(object sender, AutomationEventArgs e)
326359 UpdateFormula ( textChangedOnly : true ) ;
327360 }
328361
329- // Runs on an Automation event thread
330- // CONSIDER: With WinEvents we could get notified from main thread ... ?
331- void LocationChanged ( object sender , AutomationPropertyChangedEventArgs e )
332- {
333- // Debug.Print($">>>> FormulaEditWatcher.LocationChanged on thread {Thread.CurrentThread.ManagedThreadId}");
334- Logger . WindowWatcher . Verbose ( $ "FormulaEdit - Location changed") ;
335- UpdateEditState ( true ) ;
336- }
337-
362+ // Switches to our Automation thread, updates current state and raises StateChanged event
338363 void UpdateEditState ( bool moveOnly = false )
339364 {
340365 Logger . WindowWatcher . Verbose ( "> FormulaEdit UpdateEditState - Posted" ) ;
@@ -368,6 +393,8 @@ void UpdateEditState(bool moveOnly = false)
368393 // Neither have the focus, so we don't update anything
369394 Logger . WindowWatcher . Verbose ( "FormulaEdit UpdateEditState End formula editing" ) ;
370395 CurrentPrefix = null ;
396+ if ( IsEditingFormula )
397+ UninstallLocationMonitor ( ) ;
371398 IsEditingFormula = false ;
372399 prefixChanged = true ;
373400 // Debug.Print("Don't have a focused text box to update.");
@@ -380,7 +407,10 @@ void UpdateEditState(bool moveOnly = false)
380407
381408 var pt = Win32Helper . GetClientCursorPos ( hwnd ) ;
382409 CaretPosition = new Point ( pt . X , pt . Y ) ;
410+ if ( ! IsEditingFormula )
411+ InstallLocationMonitor ( GetTopLevelWindow ( focusedEdit ) ) ;
383412 IsEditingFormula = true ;
413+
384414 var newPrefix = XlCall . GetFormulaEditPrefix ( ) ; // What thread do we have to use here ...?
385415 if ( CurrentPrefix != newPrefix )
386416 {
@@ -391,7 +421,14 @@ void UpdateEditState(bool moveOnly = false)
391421 }
392422
393423 // TODO: Smarter notification...?
394- OnStateChanged ( new StateChangeEventArgs ( ( ( bool ) moveOnlyObj && ! prefixChanged ) ? StateChangeType . Move : StateChangeType . Multiple ) ) ;
424+ if ( ( bool ) moveOnlyObj && ! prefixChanged )
425+ {
426+ StateChanged ? . Invoke ( this , new StateChangeEventArgs ( StateChangeType . Move ) ) ;
427+ }
428+ else
429+ {
430+ OnStateChanged ( new StateChangeEventArgs ( StateChangeType . Multiple ) ) ;
431+ }
395432 } , moveOnly ) ;
396433 }
397434
@@ -429,5 +466,24 @@ public void Dispose()
429466 } , null ) ;
430467 Logger . WindowWatcher . Verbose ( "FormulaEdit Dispose End" ) ;
431468 }
469+
470+
471+ static IntPtr GetTopLevelWindow ( AutomationElement element )
472+ {
473+ TreeWalker walker = TreeWalker . ControlViewWalker ;
474+ AutomationElement elementParent ;
475+ AutomationElement node = element ;
476+ if ( node == AutomationElement . RootElement )
477+ return node . NativeElement . CurrentNativeWindowHandle ;
478+ do
479+ {
480+ elementParent = walker . GetParent ( node ) ;
481+ if ( elementParent == AutomationElement . RootElement )
482+ break ;
483+ node = elementParent ;
484+ }
485+ while ( true ) ;
486+ return node . NativeElement . CurrentNativeWindowHandle ;
487+ }
432488 }
433489}
0 commit comments