SL3 TabItem HeaderTemplate does not support DataBinding - PRISM work-around

An excerpt from this message thread on Silverlight.net follows:

TabControl and Databinding is a sore point for a while now. You can show your support in fixing it on this codeplex bug:
http://silverlight.codeplex.com/WorkItem/View.aspx?WorkItemId=3604 

We're planning on overhauling TabControl for the Silverlight vNext release.

Sincerely,

--
Justin Angel,
Blog @ http://silverlight.net/blogs/JustinAngel
Twitter @ http://twitter.com/JustinAngel

The problem reveals itself in the PRISM View Injection QuickStart: 

Since PRISM has a TabcontrolRegionAdapter for Silverlight we have a hook into the problem (for a work-around).  Excerpt from documentation on the BootStrapper follows:

Conditional compiling is used to register the TabControlRegionAdapter only for Silverlight. This is because when you add an item in a TabControl control in WPF, this item is wrapped inside a TabItem type. This is not applicable for Silverlight, where you must manually wrap the item into the TabItem; because of that, the TabControlRegionAdapter adapter was modified to both adapt a region to the TabControl control and to automatically add this wrapping.

PRISM users can update the TabControlRegionAdapter.cs, in the Composite.Presentation project, as follows:

private static TabItem PrepareContainerForItem(object item, DependencyObject parent)
{
TabItem container = item as
TabItem;
if (container == null
)
{
    container = new
TabItem();
    container.Content = item;
    container.DataContext = GetDataContext(item);
    container.Style = GetItemContainerStyle(parent);
    container.SetValue(IsGeneratedProperty, true
);

    // BillKrat.2009.08.06 - SL3 Template binding bug workaround

    if (container.Header != null
         && container.Header.ToString().Contains("Binding:"
))
    {
     PropertyInfo modelProperty = null
;

     // Strip "Binding:" from the header - leaving model property

     string modelPropName = container.Header.ToString().Replace("Binding:", ""
);

     // Get model properties and search for the property name provided

     PropertyInfo[] propInfo = container.DataContext.GetType().GetProperties();

    
if (propInfo != null
)
        modelProperty =
         propInfo.FirstOrDefault(p => p.Name.ToString() == modelPropName);

     // If found then update the header with the value

     if (modelProperty != null
)
        container.Header = modelProperty.GetValue(modelProperty, null
);
    }
}

return
container;
}

Instead of using Value="{Binding HeaderInfo}", which won't work under SL3, you can use "Binding:HeaderInfo" which will be processed by the above method.  (note: Header will be set/available when the container.Style property is set above).  An example follows:


<Regions:TabControlRegionAdapter.ItemContainerStyle>
    <Style TargetType="Controls:TabItem">
        <Setter Property="Header" Value="Binding:HeaderInfo"/>
    </Style>
</Regions:TabControlRegionAdapter.ItemContainerStyle>

Until the Silverlight Toolkit team resolves the issue this can serve as a work-around.   With some work it could be made more dynamic but this is a good starting point.

 

The model being used follows:

namespace UIComposition.Modules.Project
{
    public class
ProjectsListPresentationModel
    {
        public ObservableCollection<BusinessEntities.Project> Projects { get; set
; }

        public static string
HeaderInfo
        {
            get { return "Current Projects"
; }
        }
    }
}

EDITED - a better solution was provided on the following link.  Although I haven't tested it yet I was impressed that it resolves the problem with one line of code - my kind of fix!   I found the above to be a great learning exercise in the power of styles so I leave it intact as is.

PRISM User forum addressing the issue HERE


Tags: , , , ,
Categories: CompositeWPF


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know