SL 3 TabRegion Adapter no longer showing header

Aug 6, 2009 at 6:50 PM

After upgrading to SL 3 my tab headers are no longer binding.  Is there a workaround?  I'm using this...

<regions:TabControlRegionAdapter.ItemContainerStyle>
    <Style TargetType="basics:TabItem">
        <Setter Property="HeaderTemplate">
            <Setter.Value>
                <DataTemplate>
                    <TextBlock Text="{Binding HeaderInfo}" />
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</regions:TabControlRegionAdapter.ItemContainerStyle>
Thanks,
Kevin

 

 

Aug 6, 2009 at 8:25 PM

You may want to add your vote: http://compositewpf.codeplex.com/WorkItem/View.aspx?WorkItemId=4599

I believe I saw a work-around listed.

Aug 6, 2009 at 10:26 PM

Looks like this is a SL3 problem - not PRISM: https://silverlight.net/forums/t/113292.aspx

Aug 6, 2009 at 11:07 PM
Edited Aug 6, 2009 at 11:46 PM

I always enjoy a good challenge so I came up with a work-around until the Silverlight Toolkit folks provide a release to fix the Template binding problem.   I substitute "{Binding HeaderInfo}" with "Binding:HeaderInfo" and let the PrepareContainerForItem method handle it.  I'm sure with a wee bit more work it could be made more dynamic to support more than the "Header" property -- it's "a" way....

Composite.Presentation\Regions\TabControlRegionAdapter.cs

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.ToString().Contains("Binding:"))
        {
            // 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();
            PropertyInfo value =
                propInfo.FirstOrDefault(p => p.Name.ToString() == modelPropName);

            // If found then update the header with the value
            if (value != null)
                container.Header = value.GetValue(value, null);
        }
    }

    return container;
}

XAML value required for work-around

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

 Blogged about HERE

Aug 9, 2009 at 6:34 PM

Thanks Bill.  I had to change this

 

 

// If found then update the header with the value
            if (value != null)
                container.Header = value.GetValue(value, null);
to this

// If found then update the header with the value
            if (value != null)
                container.Header = value.GetValue(container.DataContext, null);
Thanks again,
Kevin

 

 

Aug 10, 2009 at 5:51 AM
Edited Aug 10, 2009 at 5:58 AM

Another fix that I found to work better was to modify the PrepareContainerForItem method to call the HeaderTemplate.LoadContent() method 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);
                    // Added to fix a problem with the data binding problem.
                    container.Header = container.HeaderTemplate.LoadContent();
                }

                return container;
            }

Aug 10, 2009 at 1:30 PM

Nice....

Oct 21, 2009 at 2:10 AM
unswitch wrote:

Another fix that I found to work better was to modify the PrepareContainerForItem method to call the HeaderTemplate.LoadContent() method 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);
                    // Added to fix a problem with the data binding problem.
                    container.Header = container.HeaderTemplate.LoadContent();
                }

                return container;
            }

Hello,

I am trying to bind text to my TabItem in SL3 (CAL), and found this workaround. However, I seem to have more problems, i.e., my Datacontext is null after the call to GetDataContext, and my container.HeaderTemplate is also null. Both these issues render the workarounds in this thread moot.

Any suggestions?

Thanks in advance.

Oct 21, 2009 at 5:28 PM

Victor,

This works for me

            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);

                    if (container.HeaderTemplate != null)
                        container.Header = container.HeaderTemplate.LoadContent();
                }

                return container;
            }
Oct 21, 2009 at 8:25 PM

I'm am using SL3 with CAL v2 and everything is working as expected after making these changes. Kza's double check to make sure HeaderTemplate is not null is a good addition to the fix I suggested in a past post. Your problem seems to be elsewhere. I'd suggest trying to find out why your HeaderTemplate is null, that would be why this isn't working for you. Possibly a XAML problem?

Oct 21, 2009 at 11:36 PM
Edited Oct 22, 2009 at 2:55 AM

Thanks for your quick response.

Yes, it seems both the container.Datacontext and the container.HeaderTemplate are null. I've tried setting the DataContext on the CustomTabItemStyle and on the Grid, and in code, but it's always null.

Here's the XAML I'm using. It's from the RI.  shell.xaml


            <!-- content -->
            <Grid x:Name="ContentGrid" Grid.Row="1" Margin="10,0,10,160" RenderTransformOrigin="0.5,0.5" Grid.RowSpan="2">
               <Grid.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform/>
                        <SkewTransform/>
                        <RotateTransform/>
                        <TranslateTransform/>
                    </TransformGroup>
                </Grid.RenderTransform>
                <Controls:AnimatedTabControl x:Name="ATC" Regions:RegionManager.RegionName="MainRegion" Opacity="1"
            HorizontalAlignment="Stretch" Background="{StaticResource BorderBackground}"
            BorderBrush="{x:Null}"
                        Regions:TabControlRegionAdapter.ItemContainerStyle="{StaticResource CustomTabItemStyle}"                    
                        VerticalAlignment="Stretch" Padding="10,10,10,10" AutomationProperties.AutomationId="PositionSummaryTab" Margin="0,0,0,8" >
                </Controls:AnimatedTabControl>
            </Grid>

Shouldn't the highlighted line of XAML be setting the dependency property, "ItemContainerStyle", in TabControlRegionAdapter.cs, and thus the HeaderTemplate?

 

Oct 23, 2009 at 2:58 PM

Hi

The p&p team will soon provide a fix for this issue.

Please let me know if this helps.

Damian Schenkelman
http://blogs.southworks.net/dschenkelman

Apr 5, 2010 at 2:08 PM
kza wrote:

Victor,

This works for me

            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);

                    if (container.HeaderTemplate != null)
                        container.Header = container.HeaderTemplate.LoadContent();
                }

                return container;
            }

I've been trying to use the solutions listed here, however I have no idea how to reference the GetDataContext(item) and IsGeneratedProperty ? Can anyone assist?

 

Apr 5, 2010 at 7:40 PM

Hi,

This release of Prism for Silverlight 3 should fix this issue.

Please let me know if this helps.

Damian Schenkelman
http://blogs.southworks.net/dschenkelman

Apr 5, 2010 at 8:22 PM
dschenkelman wrote:

Hi,

This release of Prism for Silverlight 3 should fix this issue.

Please let me know if this helps.

Damian Schenkelman
http://blogs.southworks.net/dschenkelman

Hi, that's the release I've been using.

 

I've visited the RI StockTrader project due to a need for a control to present the modules. I've decided to go with the AnimatedTabControl for a start.

At first I was having problems with displaying the control in blend but after adding this code :

public static readonly DependencyProperty SelectedIndexProperty = DependencyProperty.Register("SelectedIndex", typeof(int), typeof(TabControl), new PropertyMetadata(0));

Blend managed to display the AnimatedTabControl.

 

Here's the Shell.xaml's fragment containing the actual control

<ctrls:AnimatedTabControl Grid.Row="1"
                        HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Opacity="1"
                        Regions:RegionManager.RegionName="MainRegion"
                        Regions:TabControlRegionAdapter.ItemContainerStyle="{StaticResource CustomTabItemStyle}"
                        AutomationProperties.AutomationId="TEST"
                        />

Here's the CustomTabItemStyle

<Style x:Key="CustomTabItemStyle" TargetType="Controls:TabItem">
        <Setter Property="IsTabStop" Value="False"/>
        <Setter Property="Background" Value="#FF1F3B53"/>
        <Setter Property="BorderBrush" Value="#FFA3AEB9"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Padding" Value="6,2,6,2"/>
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        <Setter Property="VerticalContentAlignment" Value="Stretch"/>
        <Setter Property="MinWidth" Value="5"/>
        <Setter Property="MinHeight" Value="5"/>
        <Setter Property="Template" Value="{StaticResource CustomTabItemTemplate}" />
        <Setter Property="FontFamily" Value="Trebuchet MS"/>        
    </Style>

And the CustomTabItemTemplate

<ControlTemplate x:Key="CustomTabItemTemplate" TargetType="Controls:TabItem">
        <Grid Width="Auto" Height="Auto" x:Name="TabItemRoot" Margin="10,0,10,0">
           <!-- VSM region -  removed to keep this bit readable, same as in  RI Stocktrader -->
            <TextBlock x:Name="textBlock" Text="{Binding Path=HeaderInfo}" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Trebuchet MS" FontSize="14" Foreground="#FFFFFFFF" Margin="13,5,13,5" Cursor="Hand" />
            <TextBlock x:Name="textBlock1" Text="{Binding Path=HeaderInfo}" HorizontalAlignment="Center" Margin="13,5,13,5" VerticalAlignment="Center" FontFamily="Trebuchet MS" FontSize="14" Foreground="#FFB2B2B2" Visibility="Visible" Opacity="0" Cursor="Hand" />
            <Border x:Name="border" Height="Auto" Padding="0,0,0,0" Opacity="0">
                <Border.Background>
                    <RadialGradientBrush>
                        <GradientStop Color="#A3FFFFFF" Offset="0"/>
                        <GradientStop Color="#00FFFFFF" Offset="1"/>
                    </RadialGradientBrush>
                </Border.Background>
            </Border>
        </Grid>
    </ControlTemplate>

Here's the Module :

public class ModuleAdmin : IModule
    {
        public ModuleAdmin(IRegionManager regionManager, IUnityContainer container)
        {
            m_regionManager = regionManager;
            m_container = container;            
        }

        #region IModule Members

        public void Initialize()
        {
            m_container.RegisterType<IUsersService, UsersService>();
            m_container.RegisterType<IModuleAdminView, ModuleAdminView>();

            m_regionManager.RegisterViewWithRegion("MainRegion", () => { return m_container.Resolve<IModuleAdminView>(); });
        }

        #endregion

        #region Fields
        IRegionManager m_regionManager;
        IUnityContainer m_container;
        #endregion
       
    }

Here's IModuleAdminView - IHeaderInfoProvider<string> is the same thing used in the RI StockTrader

public interface IModuleAdminView : IHeaderInfoProvider<string>
    {

    }


Here's the actual ModuleAdminView :
public partial class ModuleAdminView : UserControl, IModuleAdminView
    {
        public ModuleAdminView(IUnityContainer container)
        {
            InitializeComponent();

            ModuleAdminViewModel vm = LayoutRoot.DataContext as ModuleAdminViewModel;
            vm.UsersService = container.Resolve<IUsersService>();           
        }

        #region IHeaderInfoProvider<string> Members

        public string HeaderInfo
        {
            get { return "ADMINISTRATION"; }
         }        

        #endregion              
    }

I was expecting to have HeaderInfo ("ADMINISTRATION") be displayed in the TabItem's header but the text is not showing there. Perhaps I'm missunderstaning something from the RI Stocktrader. How should I fix this?

Apr 7, 2010 at 7:21 PM

Hi,

Based on the source code you provided one possible reason for this to happen could be that the DataContext of the View is not being set. Thus the binding cannot be performed. As you can check in the PositionSummaryView of the Position module that is part of the Reference Implementation, the ViewModel that is being set as the View’s DataContext  has a property named HeaderInfo which is bound to the TextBlock’s text.

This same scenario can be seen more easily (as there is less code) in the UIComposition Quickstarts by checking the ProjectsListView and its respective PresentationModel.

Please let me know if this helps.

Damian Schenkelman
http://blogs.southworks.net/dschenkelman

Apr 7, 2010 at 8:16 PM

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:my="clr-namespace:ModuleAdmin.Views"
    xmlns:cal="clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"     
    mc:Ignorable="d" xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" x:Class="ModuleAdmin.Views.ModuleAdminView"     
    Width="Auto" Height="Auto" d:DesignWidth="400" d:DesignHeight="300">
    <UserControl.Resources>
        <my:ModuleAdminViewModel x:Key="theViewModel"/>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot"
          DataContext="{Binding ., Source={StaticResource theViewModel}}">


The above snippet caused all the mess. I was using the in-xaml binding to a static resource (a view model).

Your suggestion helped me locate the issue, thank you.

May 18, 2010 at 10:14 PM
Edited May 18, 2010 at 10:14 PM

My copy of Prism does not even seem to have the TabControlRegionAdapter.ItemContainerStyle in the Microsoft.Practices.Composite.Presentation.Regions assembly=Microsoft.Practices.Composite.Presentation.

Developer
May 19, 2010 at 6:29 PM

Hi Joe,

If you are using this version of Prism, that property exists as a dependency property in the assembly you mentioned, inside the file TabControlRegionAdapter.cs. It might not be appearing in XAML because the assembly is not compiled or other possible reasons.

I hope you find this helpful.

Guido Leandro Maliandi
http://blogs.southworks.net/gmaliandi