Dynamically Generated Proxies and Missing Namespaces

Problem

The DateTimeInput control from DotNetBar used on my current project has a clear button on the calendar.  Clicking this, attempts to set the bound value to null.  When running this in a simple test harness, it worked.  However, running the same code within my project which uses the open source framework Castle Dynamic Proxy 2, resulted in an “Object Reference Not Set” exception deep within the WPF framework.

Cause
Debugging through the .NET Framework source code, it eventually became apparent that the PropertyPathWorker class attempts to get the namespace of the bound type.  In our scenario, this happened to be a MeetingProxy which returns null for the following Framework code:

         void DetermineWhetherDBNullIsValid()
        { 
            bool result = false;
            PropertyDescriptor pd;
            object item = GetItem(Length - 1); 
            if (item != null && 
                item.GetType().Namespace.StartsWith("System.Data", StringComparison.Ordinal) && 
                ((pd = GetAccessor(Length-1) as PropertyDescriptor) != null)) 
            { 
                result = DetermineWhetherDBNullIsValid(item, pd); 
            } 
            _isDBNullValidForUpdate = result; 
        }

The Namespace method in the RtType class is below:  

        public override String Namespace

        {

            get

            {

                string ns = Cache.GetNameSpace()
                if (ns == null || ns.Length == 0)

                    return null;

 

                return ns;

            }

        }

The problem here is that Microsoft has assumed that all types will return a Namespace.  However this does not appear to be the case for our Dynamic Proxies.  Either this assumption is incorrect or the Dynamic Proxy Code should retain the namespace when the Proxies are generated.

 Initial Work Around
Create a pass through property on the data binding class which wrappers the property on the proxied object, e.g.

        public virtual DateTime? CompleteByDate

        {

            get { return Meeting.CompleteByDate; }

            set { Meeting.CompleteByDate = value; }

        }

Final Solution
This morning I updated the source of our copy of the Castle Dynamic Proxy 2 assembly to set namespaces on the generated proxies, so this is no longer a problem.  Now, I must remember to submit this change to the Castle Project!

Using Delegate Commands with WPF Views (UPDATED)

UPDATED: Scroll to end of post.

Please note:  the examples in this post depend on an implementation of the Presentation Model described in a previous post. 

DelegateCommand<T> is a class that comes with the Composite Application Library (CAL or PRISM), which is the latest incarnation of the Composite application block (CAB).  I have included it in my solution for an application that is based on CAB and have also created a second class DelegateCommand which extends DelegateCommand<T> simply by overriding the constructors and specifying T as object.  This second class allows you to use delegate commands when you don't need to use a command parameter.  I will not go into detail here regarding the implementation of the DelegateCommand<T> class (you can look at the code for yourself – see the attached zip file at the bottom of the post), but will stick to describing how it is used.

Delegate commands can be used instead of WPF Routed commands.  The advantage of a delegate command is that they can be bound to directly on the PresentationModel without the need for a pass through method in the View's code behind.  The command parameters are also type safe, whereas with Routed Commands, parameters are always of type object, and so need to be cast before use.

 Delegate commands are exposed on the Presentation Model as a property.  For example:

OrdersPresentationModel: AddCommand Property

public DelegateCommand<Order> AddCommand { get; set; }

Here we are declaring a DelegateCommand<T>. If the command is to be passed a command parameter, the type of the parameter should be included as the template for the command.  If not, use DelegateCommand instead (no template).

I have chosen to use automatic properties for my commands.  I therefore need to initialise the commands from the OnViewReady method.  (Alternatively, I could have included the initialisation in the getter via a null check). I do this by calling the InitialiseCommands method:

OrdersPresentationModel: InitialiseCommands Method

private void InitialiseCommands()

{

    ...

    AddCommand = new DelegateCommand<Order>(OnAdd, CanAdd);

    ...

}

 My OnAdd() and CanAdd() methods are also declared in the presentation model as follows:

OrdersPresentationModel: OnAdd and CanAdd Methods

private static bool CanAdd(Order order)

{

    return order != null;

}

private void OnAdd(Order order)

{

    if (order == null) return;

    Customer.Orders.AddUnique(Order);

}

So how do we use the command?  As simple as any other property binding:  

OrdersView

<Button Margin="5,0,5,0"

        Height="20"

        Width="70"

        Command="{Binding AddCommand}"

        CommandParameter="{Binding ElementName=ordersComboBox, Path=SelectedItem}">

 

 That's it.  Very simple.  Notice that the OnAdd and CanAdd methods are private.  How can we test them then?  Better than just testing the methods, we can actually call the command from the test.  

OrdersPresentationModelTest

public void AddCommand_AddOrder_OrderAddedToCustomersOrdersCollection()

{

    ....

   _model.AddCommand.Execute(new Order(1));

    ....

    model.AddCommand.CanExecute(new Order(1));

    ....

} 

UPDATE:  Thanks to a tip off from a colleague of mine at EMC, Marcin Kaluza, regarding Josh Smith’s excellent post Allowing CommandManager to query your ICommand objects, I have updated the DelegateCommand class by removing the RaiseCanExecuteChanged and the OnCanExecuteChanged methods and instead implementing add and remove accessors on the CanExecuteChanged event that hook the event up to the CommandManager.RequerySuggested event.  As a result, we no longer need to implement a DispatcherTimer (as suggested below) for each view to periodically poll the CanExecute methods.  I’m sure you’ll agree, this is a much neater solution and has the added bonus of removing the concern of CPU spiking, since we are no longer polling the can execute methods every half second or so, but only when the CommandManager detects conditions that might change the ability of a command to execute.  Thanks Josh!

 There is just one fly in the ointment with this approach.  The CanExecute fires only once when the view is loaded.  This seems like a flaw in the DelegateCommand, since the WPF's Routed Command regularly polls CanExecute methods.  There may now be other implementations of the delegate command that claim to have solved this problem (the Relay Command may be one of them), but I have yet to research them.  In the mean time I have implemented a DispatcherTimer that raises the RaiseCanExecuteChanged() on the delegate command every half second.  This is initialised from the OnViewReady method by calling the InitialiseCanExecuteDispatcherTimer()  method on the PresentationModel.  Ideally this should be pushed down into the DelegateCommand class, but I have yet to look into solving that particular problem.

OrdersPresentationModel: InitialiseCanExecuteDispatcherTimer Method

private void InitialiseCanExecuteDispatcherTimer()

{

    _dispatcherTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0,500) };
    _dispatcherTimer.Tick += (sender, e) => AddCommand.RaiseCanExecuteChanged();
    _dispatcherTimer.Start();

} 

Automated Tool Tip for TextBlocks in DataTemplates

We had a long standing problem for WPF Screens in our application where tool tips on TextBlocks appeared on MouseOver, regardless of text truncation, when the tool tip was set to the Text property.  Those familiar with the Label in WinForms, will be aware that when truncated, it displays ellipses and a tooltip will appear on MouseOver.  When not truncated, no tooltip appears on MouseOver.

In order to replicate this behaviour in WPF screens I have added the TextBlockService class (attached) to our solution, cobbled together from code posted on the fantastic Tranxition Developer Blog, that registers a couple of attached properties and a ClassHandler (event handler)  with the WPF Framework.  See Customizing “lookful” WPF controls – Take 2 on the Tranxition Developer Blog for a detailed breakdown of the code in the TextBlockService.

These attached properties are applied to all TextBlocks via a MultiTrigger in an implicit style I have created in the attached TextBlockStyles.xaml.  In order to get the behaviour described above, the tool tip needs to be removed from the TextBlock.  You can also remove the text trimming property and vertical alignment property (if set to center) since I have also added those to the style, e.g.

           <TextBlock Text="{Binding Path= Person.Surname}
                     VerticalAlignment="Center
                     TextTrimming="CharacterEllipsis
                     ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Text}"/>

Becomes:

           <TextBlock Text="{Binding Path= Person.Surname}"/>

(Caveat: This approach only applies if the tooltip needs to be set to the text property for truncated TextBlocks).

Unfortunately Microsoft, in their ultimate wisdom, have not enabled implicit styles to be passed into DataTemplates.  Microsoft has this to say about this apparent ‘feature’! 

"This behavior is 'By Design' and this is why. Templates are viewed as an encapsulation boundary. Elements produced by these templates fall within this boundary. And lookup for a style with a matching TargetType stops at this boundary. Hence the TextBlock in the repro which is produced through a template does not pick up the Style in question. Whereas the TextBlock defined outside the template does.
One way to work around this problem is to give an explicit name to the Style and reference the style by this name on the TextBlock within the template.

They also say:

Templates are viewed as an encapsulation boundary when looking up an implicit style for an element which is not a subtype of Control.

...which explains why a Button (which is a subtype of Control) in DataTemplates does implement an implicit Button style, but TextBlock (which is not a subtype of Control, but inherits directly from FrameworkElement) does not implement an implicit TextBlock style.

Anyway, referring to the first statement, I have implemented a keyed style in TextBlockStyles.xaml with a key of “TextBlockWithAutoToolTip” that is based on the implicit TextBlock style.  So to apply this to a DataTemplate you must add the reference to the merged dictionaries of the Form/UserControl/View:

    <ResourceDictionary Source="/RRA.Beacon.Recruiter.UI.Controls;component/Styles/TextBlockStyles.xaml" />

And then the TextBlock becomes:

          <TextBlock Text="{Binding Path=PersonSummary.Surname}"

                     Style="{StaticResource TextBlockWithAutoToolTip}"/>

From the above statement regarding “Templates are viewed as an encapsulation boundary”, you would think you could just include a typed style in the DataTemplate itself and then it would apply within that template.  Unfortunately this doesn’t work and you have to apply a keyed style on each TextBlock!

You will find TextBlockService.cs and TextBlockStyles.xaml files in the attached zip.

November 26 2009

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
Older Posts