TabControl binding tabItem Header to Module Property

Topics: Prism v4 - Silverlight 4
Dec 27, 2010 at 10:43 PM

Hello:

I am trying to accomplish what seems to have been accomplised before, in previous versions of PRISM. I am simply trying to bind the header(template) of a tabItem Control of tabControl to a public property of a module. For some reason I am not able to get this to work. here is some code....

 <sdk:TabControl Grid.Row="1"
                        x:Name="MyTabs"
                        regions:RegionManager.RegionName="ModuleRegion">
            <regions:TabControlRegionAdapter.ItemContainerStyle>
                <Style TargetType="sdk:TabItem"
                       BasedOn="{StaticResource BaseTabItem}">
                    <Setter Property="HeaderTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <TextBlock Text="{Binding TabCaption}" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>                   
                </Style>
            </regions:TabControlRegionAdapter.ItemContainerStyle>           
        </sdk:TabControl>
This is how I initialize the ModuleA.
 public class Module : IModule
    {
        IRegionManager regionManager;
        IUnityContainer container;

        public Module(IRegionManager _regionManger, IUnityContainer _container)
        {
            this.regionManager = _regionManger;
            this.container = _container;
        }
        #region IModule Members

        public void Initialize()
        {
            regionManager.RegisterViewWithRegion("ModuleRegion", typeof(ModelAView));
        }

        #endregion
    }
In the ViewModel of my ModuleA View I have defined a publc properyt named TabCaption.
 public string TabCaption
        {
            get { return "tolga"; }
        }
This is how I load my module into the catalog.
 public class Bootstrapper : UnityBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            Shell shell = Container.Resolve<Shell>();
            Application.Current.RootVisual = shell;
            return shell;
        }
        
        protected override IModuleCatalog CreateModuleCatalog()
        {
            ModuleCatalog catalog = new ModuleCatalog();
            catalog.AddModule(typeof(ModuleA.Module));
            return catalog;
        }      
    }
Any help is greatly appreciated
Thanks,
--tolga
Dec 27, 2010 at 10:55 PM

I found it....

in my view's code-behind. I had to change it to this...

 public ModelAView(ModelAViewModel _viewModel)
        {
            InitializeComponent();
            this.DataContext = _viewModel;

        }

 

instead of

public ModelAView(ModelAViewModel _viewModel)
        {
            InitializeComponent();
            this.Loaded += (s, e) =>
            {
                this.DataContext = _viewModel;
            };

        }
Dec 28, 2010 at 1:22 PM

Hi Tolga,

Thanks for sharing your insight with the rest of the community, as other users might benefit from knowing this.

Miguel Bronzovic
http://blogs.southworks.net/mbronzovic



Jan 24, 2011 at 12:40 PM

Hi Tolga,

 

Many Thanks for posting such a great question and the solution. I am also stuck with this.
And I still couldn't make it work til a minute ago. I have been so desperate that I blamed MEF and changed the test app to Unity from MEF. :)

Inside my Module I have the following:

public void Initialize()
        {
            _regionManager.RegisterViewWithRegion(Constants.MainRegion, () => _container.Resolve<ContactView>());
        }

Maybe because the View is created first and the viewmodel is added at runtime (Loaded), the tab doesnt see the property yet?  But the INotifyPropertyChanged should have really reported this later nonetheless. Unless the Header of a tab can't be changed once set.

You won't believe it but by by reversing your response, I got it working. This works for me but not if I use the Loaded event. 

public ContactView(ContactViewModel _viewModel)
        {
            InitializeComponent();
            this.DataContext = _viewModel;

        }

Can a PRISM pro explain why this is happening?

Many Thanks,

Jan 8, 2012 at 1:46 AM

Hi,

Thanks for sharing this post. It helps.

I faced the same problem in TabControl and it got solved after below changes done in my view. 

I was not able to bind tabItem to public property.

public MainView(MainViewModel viewModel)
        {
            InitializeComponent();

            this.DataContext = viewModel;
        }


Thanks

- Divyesh

 

Feb 27, 2012 at 3:22 PM

I think this is caused by a fix for a bug in SL 3. 
Although changing the prism libs my self is 

if you change (in TabControlRegionSyncBehavior.cs) :

 
protected virtual TabItem PrepareContainerForItem( object item, DependencyObject parent )
{
	TabItem container = item as TabItem;
	if ( container == null )
	{
		object dataContext = GetDataContext( item );
		container = new TabItem( );
		container.Content = item;
		container.Style = TabControlRegionAdapter.GetItemContainerStyle( parent );
		container.DataContext = dataContext; // To run with SL 2
		container.Header = dataContext; // To run with SL 3
		container.SetValue( IsGeneratedProperty, true );
	}
	return container;
}

To: (removing the line "container.Header = dataContext; // To run with SL 3" )

 

protected virtual TabItem PrepareContainerForItem( object item, DependencyObject parent )
{
	TabItem container = item as TabItem;
	if ( container == null )
	{
		object dataContext = GetDataContext( item );
		container = new TabItem( );
		container.Content = item;
		container.Style = TabControlRegionAdapter.GetItemContainerStyle( parent );
		container.DataContext = dataContext; // To run with SL 2
		//container.Header = dataContext; // To run with SL 3
		container.SetValue( IsGeneratedProperty, true );
	}
	return container;
}
Now it works correctly. In my case I use data binding in the TabItem style (in Silverlight 5):
<Style x:Key="CustomTabItemStyle" TargetType="sdk:TabItem">
	<Setter Property="Header" Value="{Binding Path=HeaderInfo}" />
</Style>