Why is my TabControl ignoring my ItemsPanelTemplate? - wpf

I've used the ItemsPanelTemplate on other controls such as the ListBox, so I figured doing the same thing for the TabControl would be simple.
Apparently, I'm missing something and the TabControl is completely ignoring what I place in the ItemsPanelTemplate.
I have xaml that looks kinda of like this:
<TabControl TabStripPlacement="Right" ItemsSource="{Binding Components}">
<TabControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</TabControl.ItemsPanel>
</TabControl>
I've tried replacing WrapPanel with UniformGrid to see if there was a difference and it behaves the same. I do have a ControlTemplate for TabItem, but I tried removing it and it made no difference so I don't think that's effecting my problem.

You're probably looking to overwrite the Template, not the ItemsPanel
You can overwrite TabControl.ItemTemplate (or TabItem.Template) to alter the appearance of the Tabs along the top, TabControl.ContentTemplate to alter the template used for the content of the Tab, or TabControl.Template to alter the overall template of the TabControl.
I wasn't even aware that TabControl's had an ItemsPanel. I've only ever used that with an ItemsControl, where the ItemsPanel affects what kind of control contains the items in the collection. If the TabControl has that property, I expect it's only because it inherited it from some base class

Related

How do you navigate a complex Visual Tree in order to re-bind an existing element?

In the above image, child is a ContentPresenter. Its Content is a ViewModel. However, its ContentTemplate is null.
In my XAML, I have a TabControl with the following structure:
<local:SuperTabControlEx DataContext="{Binding WorkSpaceListViewModel}"
x:Name="superTabControl1" CloseButtonVisibility="Visible" TabStyle="OneNote2007" ClipToBounds="False" ContentInnerBorderBrush="Red" FontSize="24" >
<local:SuperTabControlEx.ItemsSource>
<Binding Path="WorkSpaceViewModels" />
</local:SuperTabControlEx.ItemsSource>
<TabControl.Template>
<ControlTemplate
TargetType="TabControl">
<DockPanel>
<TabPanel
DockPanel.Dock="Top"
IsItemsHost="True" />
<Grid
DockPanel.Dock="Bottom"
x:Name="PART_ItemsHolder" />
</DockPanel>
<!-- no content presenter -->
</ControlTemplate>
</TabControl.Template>
<TabControl.Resources>
<DataTemplate DataType="{x:Type vm:WorkSpaceViewModel}">
....
WorkSpaceViewModels is an ObservableCollection of WorkSpaceViewModel. This code uses the code and technique from Keeping the WPF Tab Control from destroying its children.
The correct DataTemplate - shown above in the TabControl.Resource - appears to be rendering my ViewModel for two Tabs.
However, my basic question is, how is my view getting hooked up to my WorkSpaceViewModel, yet, the ContentTemplate on the ContentPresenter is null? My requirement is to access a visual component from the ViewModel because a setting for the view is becoming unbound from its property in the ViewModel upon certain user actions, and I need to rebind it.
The DataTemplate is "implicitly" defined. The ContentPresenter will first use it's ContentTemplate/Selector, if any is defined. If not, then it will search for a DataTemplate resource without an explicit x:Key and whose DataType matches the type of it's Content.
This is discussed here and here.
The View Model shouldn't really know about it's associated View. It sounds like there is something wrong with your Bindings, as in general you should not have to "rebind" them. Either way, an attached behavior would be a good way to accomplish that.
I think the full answer to this question entails DrWPF's full series ItemsControl: A to Z. However, I believe the gist lies in where the visual elements get stored when a DataTemplate is "inflated" to display the data item it has been linked to by the framework.
In the section Introduction to Control Templates of "ItemsControl: 'L' is for Lookless", DrWPF explains that "We’ve already learned that a DataTemplate is used to declare the visual representation of a data item that appears within an application’s logical tree. In ‘P’ is for Panel, we learned that an ItemsPanelTemplate is used to declare the items host used within an ItemsControl."
For my issue, I still have not successfully navigated the visual tree in order to get a reference to my splitter item. This is my best attempt so far:
// w1 is a Window
SuperTabControlEx stc = w1.FindName("superTabControl1") as SuperTabControlEx;
//SuperTabItem sti = (SuperTabItem)(stc.ItemContainerGenerator.ContainerFromItem(stc.Items.CurrentItem));
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(stc);
//ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(sti);
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
The above code is an attempt to implement the techniques shown on the msdn web site. However, when I apply it to my code, everything looks good, except myDataTemplate comes back null. As you can see, I attempted the same technique on SuperTabControlEx and SuperTabItem, derived from TabControl and TabItem, respectively. As described in my original post, and evident in the XAML snippet, the SuperTabControlEx also implements code from Keeping the WPF Tab Control from destroying its children.
At this point, perhaps more than anything else, I think this is an exercise in navigating the Visual Tree. I am going to modify the title of the question to reflect my new conceptions of the issue.

Overriding default ItemsPanelTemplate in Silverlight 4?

I am trying to override the default template for TabControl in Silverlight. Instead of having the tabs wrap around when they are full, I want to make it so the user can scroll through them, similar to a ListBox. However, Silverlight just ignores everything I put in "ItemsPanelTemplate" and renders the default. Here is the relevant code:
<swc:TabControl Grid.Row="0" Grid.Column="1" Name="Tabs">
<swc:TabControl.ItemsPanel>
<ItemsPanelTemplate>
<ScrollViewer>
<StackPanel Orientation="Horizontal" />
</ScrollViewer>
</ItemsPanelTemplate>
</swc:TabControl.ItemsPanel>
Even if I just put in a regular StackPanel, it still does nothing at all. I am using the Silverlight Toolkit for the tabs, so everything I find online is just for regular WPF and does not work for Silverlight. Thank you all very much for your advice.
The ItemsPanel needs to have a Panel in it as the root element. You have a ScrollViewer. If you want to add a ScrollViewer, you'd have to create a custom ControlTemplate that wraps the ItemsPresenter with a ScrollViewer. The ItemsPresenter will be where the ItemsPanel is shown.
You should be able to get the default Style and ControlTemplate from the Silverlight Toolkit source and tweak it to your needs. Then include your modified version in your application resources or apply it explicitly to individual TabControls.

Set WPF TabControl to show the same Content for every tab

I'd like to use a TabControl where each tab shows the same view just with different parameters. Therefore I do not want to create a new content control for each tab but reuse the same control for all tabs (binding some properties of it to the SelectedItem property of the TabControl)
I tried to my contained control as resource and set the Content property of the tab items to it, but this resulted in an exception, because the same element cannot appear as content in to different parents.
Any ideas?
<TabControl>
<TabControl.ContentTemplate>
<DataTemplate>
your view
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
I found a solution in this question
TabControl's TabItems sharing same content... Do Not Want
(even if the poster wanted specifically the opposite behaviour :) ...)

How to bind StackPanel Children from a ViewModel?

New to Silverlight. I'm working on a chat application where new chat messages are added to the bottom of a list. I had a working version that used as StackPanel inside a ScrollViewer and then in some code behind used StackPanel.Children.Add().
I'm trying to convert this to a View-ViewModel approach, and I can't figure out how to bind the Children of the StackPanel to any collection property. I've tried this:
<ScrollViewer Name="scrollMessages" Grid.Row="2" Margin="0,0,0,0" VerticalScrollBarVisibility="Visible">
<StackPanel x:Name="pnlMessages" Orientation="Vertical" Children="{Binding Path=ExampleTBs}" />
</ScrollViewer>
where ExampleTBs is a collection of TextBlocks created in code. This fails XAML parsing, the Children property isn't bindable in this way.
Is the approach of binding to the StackPanel itself fixable? Should I be using a different container type? I saw another question where the guy created the entire StackPanel in code and then used a ContentPresenter...
Bottom line, I'd like to find a way to databind my view to a viewmodel using something like a StackPanel as a container where successive items will be added to the container over time. Best approach?
Use a ListBox (or any other ItemsControl) and bind the ItemsSource property to an ObservableCollection in your ViewModel.
Do you need to use a StackPanel? If you use an ItemsControl instead, this still presents each chat message in a vertical list, and also allows for binding of the data.

Is it possible to create a WPF tab control template that looks nothing like a tab control?

What I'd like is a control that functions just like the tab control but instead of having the tabs along the top, the items would be displayed in a list box along the side. I imagine it's possible but haven't found any examples, I'm hoping there's someone here that's done something like this.
WPF controls are designed to enable exactly what you want. To reuse control functionality while completely replacing the visual representation. You will have to create your own ControlTemplate for the TabControl. You can find a TabControl ControlTemplate Example on MSDN. You will also have to study the Control Authoring Overview on MSDN.
I actually find the Silverlight 3 documentation somewhat easier to digest, and even though there are some differences when it comes to control styling the fundamental concepts are still the same. You can read Customizing the Appearance of an Existing Control by Using a ControlTemplate on MSDN to learn about control templates and then study TabControl Styles and Templates to discover what is required to create you own control template in Silverlight.
You can use Expression Blend to extract the the default TabControl template in WPF.
You don't need to use a TabControl at all. You could just bind your ListBox to a list of items, and put a ContentControl beside it, bound to the selected item :
<DockPanel>
<ListBox Name="listBox"
DockPanel.Dock="Left"
ItemsSource="{Binding Items}"
DisplayMemberPath="Name"/>
<ContentControl Content="{Binding SelectedItem, ElementName=listBox}"
ContentTemplate="{StaticResource theTemplate}"/>
</DockPanel>

Resources