The Actipro Blog

All the latest UI control development news from Actipro

SyntaxEditor for WPF - Expanded use of the service locator design pattern

The other day we posted about how syntax languages are changing to use the service locator design pattern.  This design pattern allows us to generically make available new features for syntax languages, and for language designers to easily implement them.  It’s a win-win for everyone.

While we’ve been working on adding support for languages to respond to user input and show completion lists, etc., we changed the internal model we use for handling IntelliPrompt input to be based on the service locator design pattern as well. 

Which “services” are available?

Right now there are a handful of services available for use by syntax languages.  These include:

  • IExampleTextProvider
  • ILexicalParser
  • ILineCommenter
  • ITextStatisticsFactory
  • ITokenClassifierFactory
  • IWordBreakFinder
  • IActiveEditorViewChangeEventSink
  • IEditorDocumentTextChangeEventSink
  • IEditorViewKeyInputEventSink
  • IEditorViewMouseInputEventSink
  • IEditorViewTextInputEventSink
  • IEditorViewSelectionChangeEventSink

The first six items in that list are feature-related, specifically for syntax language usage.

The "event sink” items are interfaces that can be implemented on objects that wish to receive event notifications.  These can be registered on both syntax languages and IntelliPrompt sessions.

More about event sink interfaces

Let’s talk more about how the event sink interfaces work. 

Say your language wishes to be notified when any document that uses it has its text changed.  In this case, you could have your language implement the IEditorDocumentTextChangeEventSink interface and register itself as a IEditorDocumentTextChangeEventSink service for the language.  Alternatively you could have some other object implement the interface and register that object as a IEditorDocumentTextChangeEventSink service for the language.

The IEditorDocumentTextChangeEventSink interface looks like this:

   1: public interface IEditorDocumentTextChangeEventSink {
   2:  
   3:     void NotifyDocumentTextChanged(SyntaxEditor editor, 
   4:         EditorSnapshotChangedEventArgs e);
   5:  
   6:     void NotifyDocumentTextChanging(SyntaxEditor editor, 
   7:         EditorSnapshotChangingEventArgs e);
   8:     
   9: }

When a SyntaxEditor is edited and a document text change occurs, the IEditorDocumentTextChangeEventSink service registered with the related language, if any, is notified of the text changing and changed events.  It can choose to handle those events or allow them to continue on.

Likewise the other event sink interfaces help with trapping and/or responding to active view changes, key/mouse/text input, and view selection changes.

IntelliPrompt sessions become service locators too

One other major change we’re making with the next build is that we’ve removed input processors.  In the first public beta, input processors were objects that had some mouse/key/text input methods on them and you could attach one to an IntelliPrompt session to filter input before the view received it.  We’ve removed input processors and have instead made IntelliPrompt sessions service locators.

This change allows us to generically add support for new features down the road.  We still support all the input filtering capabilities as before however now they are implemented using the event sink interfaces described above.

Note that this change will most likely not affect your code at all since this part of the IntelliPrompt implementation was done internally before anyhow.

Event sink priorities

Event sinks can be registered on any syntax language or IntelliPrompt session.  When an event sink notification is to be performed, a prioritized list of available event sinks is built.  The list contains the open IntelliPrompt sessions (newest to oldest) that have the related event sink registered, followed by the syntax language if it has the event sink registered.  Each event sync is notified in that particular order and any handler can mark the event as handled along the way.

Summary

The service locator design pattern has proved to be a welcome addition to the SyntaxEditor for WPF design, as it implements a generic methodology for providing features and relaying events notifications.

Comments (2) -

June 14, 2009 at 22:19  

Jan Bannister United Kingdom

I like the services model but I'm not sure why you guys are going with the 'Sink' model. The whole bubbling and tunnelling RoutedEvnet model in WPF gives you all the power you get from Sinks and more flexibility.

Plus RoutedEvents are how all the framework WPF UI controls implement this kind of pattern.

June 15, 2009 at 02:23  

Bill Henning (Actipro) United States

Let me lay out our thoughts on this and feel free to comment since we can still tweak it if you have better ideas...

1) Prioritization - The main reason is so that there can be a prioritized list of sinks that are called.  Consider this scenario... your language opens a completion list session.  From that point on, the completion list session should have the first stab at any key input, not the language.  With routed events, we have no control over which handlers get events first and in more complex scenarios this could definitely cause issues with functionality not working correctly.

2) Externally swapping in functionality - By using the services model, it's easy to swap in your own event handling functionality, even without overriding a language class' methods, of which the ones you want may or may not be overridable.

3) Preventing possible memory leaks - One thing we looked at was having like sets of OnDocumentAttached/Detached and OnSyntaxEditorAttached/Detached methods.  In each case you could have the language attach to the events it cared about and then detach from them.  While this could probably work out ok (other than prioritization issues), the language developer would HAVE to remember to detach from the attached events or else memory leaks would occur due to documents being retained in memory after they were no longer needed.

4) .NET 2.0 support - Our goal with languages is to stick with simpler .NET 2.0 functionality as much as possible since we'd like most language source code to be able to be used in WinForms and other platforms down the road, all based on our text/parsing library, which is .NET 2.0-based.  Since routed events are WPF-specific, they break with this goal.

I hope this explains our reasoning a bit better.

Pingbacks and trackbacks (1)+

Comments are closed