WPF Custom Tab Order

By default, the tab order of controls on a WPF screen is based on the order in which they appear in the XAML.  This is suitable for a typical form, but when working in a composite application, a little more effort is required.

When a screen is composed of more than one custom control (typically a view), the default tabbing will not behave itself so well.  In order to enable tabbing into and across views on a screen, each view must use the attached property:

                 KeyboardNavigation.TabNavigation="Continue"

This facilitates tabbing based on the layout of the XAML into and across views.  If however you require more control over tab order say, within a view or in fact any Content Control, you can use the TabIndex property on each of the contained controls.  However, the controls that have specified a TabIndex lose their tab stop, unless the above line (on the view or content control) is changed to:

                 KeyboardNavigation.TabNavigation="Local"

An oddity with this approach, occurs if you add the above property to an expander, and add tab indexes to the contained controls.  The tabbing steps straight to the contained controls, missing the expander toggle button, which now is tabbed to after all the contained controls have been visited.  Resolve this by moving the above KeyboardNavigation.TabNavigation property to a contained grid instead.

Finally, in order to prevent tabbing from moving on to the next screen, add the following property to the outer containing view for the screen:

                 KeyboardNavigation.TabNavigation="Cycle"

This keeps the focus on the current screen and cycles the tabbing back to the top of the screen.

November 25 2009

Using the Presentation Model Pattern with WPF Views and CAB/CAL (PRISM)

The Established MVP Pattern
When creating WPF views for an application based on Microsoft Patterns & Practices Composite UI Application Block (CAB), a UI framework for developing applications in Windows Forms that also supports hosting WPF Views, the pattern we adopted for binding controls was to call methods from the OnViewReady method to retrieve or create the bound objects in the presenter and pipe them into the view's code behind via method calls on the views interface.  These methods in the view then set the DataContext of the target control, or indeed of the view itself, to the passed in object.  In some cases, when binding directly to an ItemsControl, the passed in object was a collection, and this was bound to the ItemsSource of the target control.  Here is an example:

Presenter:

public override void OnViewReady()

      {

          base.OnViewReady();

          BindView();

      }

      public void BindView()

      {

          ...

          View.BindCustomers(_customers);

          ...

      }

 

View Code Behind:

public void BindCustomers(ObservableCollection<CustomerBindingObject> customers)

{

    customerslistBox.ItemsSource = customers;

}

The Presentation Model
The Presentation Model is an architectural pattern introduced by Martin Fowler and is the basis of Microsoft’s more specialized MVVM (Model-View-ViewModel) pattern.  It is the favoured approach of the Composite Application Library (CAL or PRISM), the UI framework for developing applications in WPF and Silverlight,  and is employed for separating the responsibilities for the visual display of the user interface and the presentation state and behaviour.  This approach is not so different from the traditional MVP pattern, except in the way it handles data binding.

As before, methods are called from the OnViewReady method to retrieve or create the bound objects in the presenter.  But rather than piping them into the view's code behind for subsequent binding to controls in the view, the bound objects are stored in properties on the presenter.  The presenter itself, or presentation model as it should be called, is then bound directly to the data context of the view.

Now, in the Composite Application Library (CAL or PRISM), the way they implement this, seems a backward step to me.  After storing the binding objects in properties on the Presentation Model, they call a method on the view (as we did in the established MVP pattern above), and pass "this" (the instance of the presentation model) in.  They then set the DataContext of the view to the passed in presentation model, in the called method.


I felt that this unnecessarily cluttered the views code behind and so came up with what I feel is a cleaner approach.  I have created an interface called IBindableView.  This interface implements just one property shown below:

IBindableView Interface:

public interface IBindableView

{

    object DataContext { get; set; }

}

 By inheriting from this interface, the interface for a WPF view will expose the DataContext of that view.  For example:

ICustomersView Interface:

public interface ICustomersView : IBindableView

{

}

 This then allows you to directly set the DataContext from the OnViewReady method in the presenter, without needing to call methods on the view:

CustomersPresentationModel:

public override void OnViewReady()

{

    base.OnViewReady();

    ...

    View.DataContext = this;

    ...

}

You can then bind your controls directly to the properties on your presentation model in XAML.  You can also expose your commands as properties on the presentation model and bind to these directly in XAML.  I shall explain how to do this in another post on Delegate Commands.

November 24 2009

Dog slow WPF transparency

It's been awhile since my last post, I've been busy at work so had to take a break from my TeamCity exploits, and then I got side-tracked building a little utility for myself.

The application is built in WPF and is yet another .NET natural language command window, but with some neat tricks. It was however, performing absolutely terribly, and I thought I only had myself to blame. Initially I thought it was me patching into some unmanaged funtions for some of the jiggery-pokery, as outlined below.

zrclip-001p2b8c83ba.png

However, I was unable to work out why SOO much time was being taken in the GetMessageW and DispatchMessage funtions, it was a real mind-f**k. After exausting all possibilities, I tried some random attacks here and there, one of them was turning off the transparency on the main window, and low and behold, the application is now performing super-quick, but why? Some googling turn up this.

So unfortunately, if your on an unpatched Vista or XP, you will need to get one of the following patches...

Vista

XP

According to the thread it seems the hotfix is already in Vista SP1 and XP SP3. Glad I found this one before... doesn't bear thinking about; my fault for not updating to SP1.

SAPI: Speech synthesis on Vista

The MIX07 Keynote includes a brief screen capture from the Daily Mail eReader and reminded me that I had meant to blog about the speech synthesis portion of the app.  It's something which many users might not have seen, because it's only available on Windows Vista.  Vista includes the Speech API (SAPI) version 5.3 which provides a text-to-speech (TTS) engine which is greatly superior to SAPI 5.2 which shipped with Windows XP.

tts

How it looks 

The way the UI works is pretty straightforward - it's meant to mimic an autocue.  So a user will trigger the functionality and the window you see on the right will appear.  As the user's computer begins speaking the news story, the text will slowly scroll up.  The word currently being spoken will always be bold and will always appear in the letterbox.

I'm personally a bit tickled that this bit of UI made it into Ray Ozzie's keynote presentation (even if it was only for about a quarter of a second) because I developed this part of the eReader and wrote this UI.

Getting SAPI to produce speech

The TTS functionality is actually quite easy to kick off.  All it takes is a call to the appropriate function, passing in a string holding the text to be read, and SAPI will begin to speak.  Specifically, .Net 3.0 provides a System.Speech assembly which does all the hard work for you.  This assembly includes the class System.Speech.Synthesis.SpeechSynthesizer which has a method public void Speak(string textToSpeak).  This is easy to use if all you want is text-to-speech.

The problem is that this is a synchronous call.  So the call will block until the speech rendering is complete.  To get around this, the SpeechSynthesizer class also includes a method SpeakAsync.  This does pretty much what you'd expect and runs the TTS activity on a background thread.

Getting updated with TTS progress

Now the SpeechSynthesizer class even provides some helpful events relating to the progress of the speech rendering.  However, it turns out that the SAPI libraries regard these events as sort of incidental to their main job - i.e. they will raise the events when they can.  So there is every possibility that these events will occur some time after the actual speech rendering of a word (or phoneme, etc) has started.  The SAPI library also seems to stop raising the events altogether, if the event handlers are consuming too much time.  This was probably a design decision made by Microsoft that the quality of the speech shouldn't be affected by calls to user code.

This last point means that you need to be very careful how you write your event handlers.  I suspect that a fair amount of time is consumed with the transition from the unmanaged SAPI libraries back into the System.Speech assembly, which doesn't leave much time for your C# code to do anything useful.  It certainly doesn't leave enough time to update a XAML UI.  The initial approach I used would update the UI with about 2 or 3 words and then UI updating would cease altogether.

The solution was (a) to be very, very careful to write efficient code; (b) use the Output window for debug info rather than trying to break into the running code; and (c) make good use of asynchronous delegates.  The SAPI events are called on a background thread, which means that a Dispatcher.Invoke call is needed before code can update the UI at all.  So the simplest solution was to replace this with a Dispatcher.BeginInvoke call which then updated the UI asynchronously to the event handler.

Constructing the UI

It took a little bit of trial and error to get a XAML UI that could efficiently update without continually doing a lot of layout work.  Ironically, the grey letterbox with its opacity and the opacity gradient of the textual content were the easy bits!  And getting a thick window border of Vista glass was also pretty trivial. The part which took some effort was working out that to display the content I needed to use a ScrollViewer control with three separate Run elements - one for the text which had been read, one for the text being read and one for the text which is still to be read.  The updates from the TTS engine can then simply be translated into shuffling characters between the various Run elements.

Getting the highlighted word to stay in the letterbox also took some trial and error.  The solution lay in the following line of code:

Rect screenPosition = CurrentlyReadingRun.ContentStart.GetCharacterRect(LogicalDirection.Forward);

This gave me the screen co-ordinates of the currently bold word, which I then used like so:

_scrollViewer.ScrollToVerticalOffset(screenPosition.Top + _scrollViewer.VerticalOffset - _paddingHeight);

May 10 2007
Newer Posts Older Posts