How can one apply an itemtemplate to default TabItems? - c#

I have the following TabControl with 2 default tabitems.
<TabControl>
<TabControl.ItemTemplate>
<DataTemplate>
<ScrollViewer VerticalScrollBarVisibility="Auto" Height="300" Content="{Binding}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabItem Header="Conexión"><local:ConnectionSettings/></TabItem>
<TabItem Header="Cuestionario"><local:QuestionEditor/></TabItem>
</TabControl>
The current xaml doesn't apply the ItemTemplate, for that to happen each tabitem would need to be "naked", like this:
<TabControl>
<TabControl.ItemTemplate>
<DataTemplate>
<ScrollViewer VerticalScrollBarVisibility="Auto" Height="300" Content="{Binding}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<!-- The ItemTemplate is only applied if tabitems are not wrapped in TabItem tags -->
<local:ConnectionSettings/>
<local:QuestionEditor/>
</TabControl>
But if I do that, then I can't specify the header.
I would like for every TabItem to be wrapped in a ScrollViewer (as specified in the ItemTemplate), but at the same be able to specify the header.
Is there a way to apply the ItemTemplate to <TabItem Header="MyHeader"/> tags?

ItemTemplate is the template to format the ItemsSource the headers on top of your tab control. ContentTemplate is the template to format the content of the tabs. To wrap your defined content to the scroll viewer, just make use of a new content control inside your template. Here is a minimal sample:
<TabControl>
<TabControl.ContentTemplate>
<DataTemplate>
<ScrollViewer>
<ContentControl Content="{TemplateBinding Property=ContentControl.Content}"/>
</ScrollViewer>
</DataTemplate>
</TabControl.ContentTemplate>
<TabItem>
<Button Content="Button1"/>
</TabItem>
<TabItem>
<Button Content="Button2"/>
</TabItem>
</TabControl>

Related

TabControl not showing content based on view model type

I have a TabControl which defines some data templates to show content based on the selected tab.
<TabControl
Grid.Row="1"
ItemsSource="{Binding ExerciseViewModels}"
SelectedItem="{Binding SelectedExercise}">
<TabControl.Resources>
<DataTemplate DataType="local:SubtractExerciseViewModel">
<local:SubtractUserControl/>
</DataTemplate>
<DataTemplate DataType="local:SumExerciseViewModel">
<local:SubtractUserControl/>
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
But when I run, the selected tab shows the name of the ViewModel (thus the DataTemplate for the specific type is not used).
What am I missing?
PS: Using MvvmLight but not really of any relevance. All the view models obviously exist (see screenshot, it has the reference to an object of type SuusRekenWonder.SumExerciseViewModel). So I am doing something wrong in XAML. But what?
In your TabControl-Resources you have to use x:Type for your DataType. Then your Resources look like:
<TabControl.Resources>
<DataTemplate DataType="{x:Type local:SubtractExerciseViewModel}">
<local:SubtractUserControl/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:SumExerciseViewModel}">
<local:SubtractUserControl/>
</DataTemplate>
</TabControl.Resources>

Insert a vertical scrollbar to see the content of tab items

I was working with WinForms and I am kind of new in WPF, I want to insert a vertical scrollbar to see all the elements content in my TabItems, this is my previous WinForm and this is my new WPF: I have implemented the following xaml-code:
<StackPanel Grid.Row="0" Grid.Column="0"
Orientation="Horizontal" Margin="0,0,0,199.2">
<TabControl x:Name="tabControl">
<TabItem Header="1" Background="#008000"/>
<TabItem Header="2" Background="#1e90ff"/>
<TabItem Header="3" Background="#bd3f02"/>
<ScrollBar></ScrollBar>
</TabControl>
</StackPanel>
Depending on what you want to display inside of your tabs, your issue might be solved by using a suitable items control which comes with a ScrollViewer out-of-the-box. Since I do not know your exact use case, I present you a general solution. From the images that you provided you want your tab content to be scrollable. To achieve this, remove the ScrollBar and place a ScrollViewer into each of the tabs that you want to scroll.
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Margin="0,0,0,199.2">
<TabControl x:Name="tabControl">
<TabItem Header="1" Background="#008000">
<ScrollViewer>
<!-- Your tab content here -->
</ScrollViewer>
</TabItem>
<TabItem Header="2" Background="#1e90ff"/>
<TabItem Header="3" Background="#bd3f02"/>
</TabControl>
</StackPanel>
The vertical scrollbar will be shown if the content exceed its container by default. If you want to display it always, set the VerticalScrollBarVisibility property to Visible.
<ScrollViewer VerticalScrollBarVisibility="Visible">
What you might want to try is encapsulating the content in a ScrollViewer like so :
<StackPanel>
<ScrollViewer>
<Grid>
<!-- YOUR CONTENT HERE -->
</Grid>
</ScrollViewer>
</StackPanel>

Multiple controls with same bindings in single xaml

I have a xaml with a TabControl with two tabs. Each of the TabItems has a treeview which has exactly the same code. The bindings, VM etc are exactly same. They work of different data which is handled in my Model depending upon a property. So my VM and View do not need to worry about it.
Is there any way that I can write my treeview and the HierarchicalDataTemplate once and each tab refers instead of duplicating code in the same xaml?
Something like
<TabControl>
<TabItem Header="Tab1">
<Grid>
<!-- Refer to the tree view here -->
</Grid>
</TabItem>
<TabItem Header="Tab2">
<Grid>
<!-- Refer to the tree view here -->
</Grid>
</TabItem>
</TabControl>
But then How to write the treeview and HierarchicalDataTemplate and then refer them?
Define a DataTemplate that contains your TreeView layout and then use ContentPresenter or ContentControl to present it:
<Window.Resources>
<DataTemplate x:Key="TreeTemplate">
<Your TreeViewLayout ...>
</DataTemplate>
</Window.Resources>
...
<TabControl>
<TabItem Header="Tab1">
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource TreeTemplate}" />
</TabItem>
<TabItem Header="Tab2">
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource TreeTemplate}" />
</TabItem>
</TabControl>

How to bind elements in separate xaml files?

I have a MainWindow.xmal which has two TabItems:
<TabItem Header="Config" ... />
<TabItem Header="Results" ... />
I have a separated Config.xaml file for the Config TabItem which has a ListBox:
<ListBox Name="UrlConfig" ... />
I have another separated Results.xaml file for the Results TabItem which has a TaxtBlock:
<TextBlock Name="Url" .../>
The problem is that I'd like to bind the selected value in the ListBox to the TextBlock. How can I do that? Please help :)
I recomend you to work with MVVM, but if not you can implement it with XAML only:
Use sepparate resource like "Bridge" 'Config' and 'Results'
Config.xaml:
<UserControl x:Key="Config">
<ListBox x:Name="ListBox1" SelectedItem="{Binding Source={StaticResource SelectedValue}, Path=Y, Mode=TwoWay}" >
<System:String>Minsk</System:String>
<System:String>London</System:String>
<System:String>NY</System:String>
</ListBox>
</UserControl>
Results.xaml
<UserControl x:Key="Results">
<Label Name="Url" Content="{Binding Source={StaticResource SelectedValue}, Path=Y}" />
</UserControl>
Code of window with TabControl:
<Window.Resources>
<!--"Bridge" resource, here will be your own type-->
<X x:Key="SelectedValue" Y="Minsk"></X>
</Window.Resources>
<Grid>
<TabControl>
<TabItem Header="Config" Content="{StaticResource ResourceKey=Config}"/>
<TabItem Header="Results" Content="{StaticResource ResourceKey=Results}"/>
</TabControl>
</Grid>
If you're working in mvvm way you can bind those to a property on your viewmodel and set the DataContext for to that viewmodel

Show Open TabItem Names in a List View

I'm trying to display a list of open tab names in a listview or listbox (recommendations?).
Been going through the different type of binding options and I'm able to bind to a single tab name but it displays vertical instead of horizontal. Here is my XAML:
<ListView DockPanel.Dock="Left"
Height="352"
Name="listView1"
Width="132"
ItemsSource="{Binding ElementName=RulesTab, Path=Name}"
IsSynchronizedWithCurrentItem="True"
FlowDirection="LeftToRight"
HorizontalAlignment="Left"
HorizontalContentAlignment="Left"
DataContext="{Binding}">
Any pointers would be greatly appreciated as I'd like to be able to see a list of all the tabs open and then double click on one to bring the tab into focus. Many thanks!
Here is an example of a TabControl and a ListBox showing the names of the TabItems that are in it:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TabControl Grid.Column="0" Name="tabControl1">
<TabItem Header="Tab1"/>
<TabItem Header="Tab2"/>
<TabItem Header="Tab3"/>
<TabItem Header="Tab4"/>
</TabControl>
<ListBox Grid.Column="1" ItemsSource="{Binding Items, ElementName=tabControl1}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<EventSetter Event="MouseDoubleClick" Handler="ListBoxItem_DoubleClick"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
and here's the code behind:
private void ListBoxItem_DoubleClick(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
var tabItem = (TabItem)((ListBoxItem)sender).Content;
tabControl1.SelectedItem = tabItem;
}
Edited to add double-click behavior.
Simplified example how to enumerate the tabs in a tab control with a listview:
<TabControl Name="MyTabControl">
<TabItem Header="Tab1">
</TabItem>
<TabItem Header="Tab2">
</TabItem>
</TabControl>
<ListView DockPanel.Dock="Left"
ItemsSource="{Binding ElementName=MyTabControl, Path=Items}"
DataContext="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I'm still unsure as to what exactly you want, anyway, this can be adjusted if needed.
First of all if you bind to a specific item you will always have one item, you need to set ItemsSource to a collection.
Assuming you want to have the names or headers of all the tabs in your list you can set the tab control's Items as the ItemsSource and then apply a ItemTemplate, some example code:
<ListBox ItemsSource="{Binding ElementName=TabControlSrc, Path=Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" Margin="5"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If you do not use the ItemTemplate you'll get an error because the same item can only be a visual child of one parent.
Frankly this seems a bit pointless since it just reiterates your tabs, did i misunderstand something? If so please clarify further.
Edit: Oh lol, three almost identical answers...

Categories