Use CollectionViewSource with TabControl - wpf

I'm trying to group and display the items of an ObservableCollection, just by using XAML code. It works well using a simple CollectionViewSource and a ListBox[1].
Actually, I would prefer to display the group's content in a tabcontrol. Google led me to the following social.msdn article wich presents a workaround to display the groups as a TabControl using code behind:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/e073f275-0826-4fca-b9da-e310ccf1713e/wpf-grouping?forum=wpf
However, as I'm using MVVM and must rely on xaml only, I can't get it to work. Actually, the CollectionViewSource populates the groups (the TabControl shows the correct tabItemHeaders), but clicking on any of these TabItems freezes the application. Here's what I've tried:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<Grid >
<TabControl ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups, Mode=OneWay}" DataContext="{Binding Source={StaticResource cvs}, Mode=OneWay}">
<!-- First Level -->
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding Items}">
Second Level
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Name}">
<ListBox ItemsSource="{Binding Items}">
The Item of the Collection
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value.Comment}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</StackPanel>
[1]: This peace of xaml does work as expected, but uses a wrappanel to display groups contents:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" VerticalContentAlignment="Top" ItemContainerStyle="{StaticResource ModulSelectInputParameterListBoxItemContainerStyle}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="DarkGray" BorderThickness="2" Margin="2">
<TextBlock FontWeight="Bold" FontSize="14" Text="{Binding Path=Name}" HorizontalAlignment="Center" MinWidth="100"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Margin="2"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Border BorderThickness="2" BorderBrush="DarkGray">
<StackPanel>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" />
<ItemsPresenter Margin="2,0,2,2" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Vertical" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</StackPanel>

I think there's something wrong with your binding your code should work.
To get the same items in both ListBoxes try to bind the second ListBox Itemssource to the first ListBox Itemssource like this :
<ListBox ItemsSource="{Binding Items}" Name="ListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Key}">
<ListBox ItemsSource="{Binding ItemsSource, ElementName=ListBox}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Thanks to Joh who helped with the appropriate DataBinding. However, the reason was totally different, a quick and dirty solution is given below[1]:
Basically, I was missing that the above mentioned tab control is nested within an outer Tab Control in my main window. I am not toally sure if the following description is entirely correct[1], but to my mind the reason is the following:
The outer TabControl uses a style to display its content. This content applies to a ViewModel which holds the above mentioned observable collection, which in turn should be the ItemsSource of the CollectionViewSource that feeds the inner tabControl with the groups.
As I have defined this style only in the outer TabControl.Resources, and missed to define a separate style for the inner tab Control, the inner tabcontrol inherits the outer style and tries to display its data using the same content.
This content is again another inner tabControl, which calls another inner tabControl and so on.
[1] Defining an empty style in the inenr tabControl.Resources solved the problem:
<TabControl.Resources>
<Style TargetType="TabItem">
</Style>
</TabControl.Resources>
I would be happy if someone could confirm this idea or provide some links to well known issues with shared styles in nested controls.

Related

How to set ItemTemplate and Template for contexmenu along with data binding for ItemTemplate

On click of toggle button I want to show a context menu. As I want to change the visual appearance of context menu, I have created controltemplate and data template as below,
<TabItem>
<TabItem.HeaderTemplate>
<DataTemplate>
<ContextMenu ItemSource="{Binding Collection}">
<ContextMenu.Template>
<ControlTemplate>
<Grid Margin="10">
<ListBox Width="150" Height="70"/>
</Grid>
</ControlTemplate>
<ContextMenu.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox Content="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu.Template>
</ContextMenu>
</DataTemplate>
</TabItem.HeaderTemplate>
</TabItem>
Post this change I could see only a LisBox being shown clicking upon toggle button. I am not able to visualize the Data template that I had set (Checkbox). Data binding is also not working. Not able to figure out what could be the issue.
You should bind the ItemsSource property of the ListBox to the ItemsSource of the ContextMenu and define an ItemTemplate for the ListBox:
<ContextMenu ItemsSource="{Binding Collection}">
<ContextMenu.Template>
<ControlTemplate TargetType="ContextMenu">
<Grid Margin="10">
<ListBox ItemsSource="{TemplateBinding ItemsSource}" Width="150" Height="70">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>

Collection Source different DataContext from ListView using it

I have the following DataTemplate for a ListView that uses a StackPanel for its ItemsPanelTemplate.
<DataTemplate x:Key="DayTemplate">
<Border BorderBrush="Black"
BorderThickness="2"
CornerRadius="5"
Width="150"
Height="440"
Background="White">
<StackPanel Orientation="Vertical">
<Border BorderBrush="Black"
BorderThickness="0 0 0 5"
Background="White">
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Session"
Command="{x:Static cmd:TimetableCommands.AddSession}"/>
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock FontWeight="Bold"
TextAlignment="Center"
Text="{Binding Path=DateInfo.Date, Mode=OneWay, Converter={StaticResource DateNoTime}}"/>
<TextBlock TextAlignment="Center"
FontWeight="Bold"
Text="{Binding Path=DateInfo.DayOfWeek}"/>
</StackPanel>
</Border>
<ListBox Name="lbSessions"
Background="Transparent"
HorizontalAlignment="Center"
Visibility="Visible"
ItemsSource="{Binding Source={StaticResource SessionList}}"
ItemTemplate="{StaticResource SessionTemplate}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</StackPanel>
</Border>
</DataTemplate>
I also have the following Collection Source that I'd like to use for the sorting functionality. It is in a seperate Resource Library, referenced correctly in the Resource Library where the DataTemplate is defined.
<CollectionViewSource Source="{Binding Path=Sessions, Mode=OneWay}"
x:Key="SessionList">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="StartTime"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
When run, no data is displayed, and the output window indicates that the collection source is trying to obtain the data for the list of items from the window level DataContext, not the DataContext the list uses (which is a couple of layers down from the Window ViewModel).
If I change the line
ItemsSource="{Binding Source={StaticResource SessionList}}"
to
ItemsSource="{Binding Path=Sessions}"
It works fine, though I no longer have the sorting fuctionality of the Collection Source, which I really want to make use of to avoid having to write my own sorting code.
What's going on? and how can I set the correct DataContext on the Collection Source.
Is there a reason you need to keep your CollectionViewSource in a separate ResourceDictionary?
Try nesting the CollectionViewSource in the ListBox's Items
<ListBox Name="lbSessions"
Background="Transparent"
HorizontalAlignment="Center"
Visibility="Visible"
ItemTemplate="{StaticResource SessionTemplate}">
<ListBox.Items>
<CollectionViewSource Source="{Binding Path=Sessions, Mode=OneWay}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="StartTime" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</ListBox.Items>
</ListBox>

Synchronize the size of controls that lay in different ItemsCollections

I want to synchronize the width of two controls.
<!--DataContext-->
<DockPanel LastChildFill="True" x:Name="HeaderDockPanel">
<DockPanel LastChildFill="True" MinHeight="60" DockPanel.Dock="Top" DataContext="{Binding Month}" d:DataContext="{d:DesignInstance Type=local:Month}" >
<TextBlock Text="{Binding Name}" Style="{StaticResource TitleTextBlock}" DockPanel.Dock="Left" />
<!--this shoud to set the Width-->
<!--Nested_1 DataContext Model_1-->
<local:DaysLine HorizontalAlignment="Left" Width="{Binding ElementName=HeaderDockPanel, Path=DataContext.DaysHeaderWidth, Mode=TwoWay}" />
</DockPanel>
<!--Nested_1 DataContext Model_2-->
<ListView Grid.Row="1" ItemsSource="{Binding CarBusiness}">
<ItemsControl.ItemTemplate>
<DataTemplate >
<DockPanel d:DataContext="{d:DesignInstance Type=local:CarBusiness}" LastChildFill="True">
<TextBlock Text="{Binding Name}" Style="{StaticResource TitleTextBlock}" DockPanel.Dock="Left" Margin="-6 0 0 0"/>
<!--this shoud to get the Width-->
<!--Nested_2 DataContext Model_3-->
<ItemsControl ItemsSource="{Binding Business}" Style="{StaticResource HorizontalItemsControl}" Width="{Binding DaysHeaderWidth}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:BusinessTextBlock d:DataContext="{d:DesignInstance Type=local:Business}" Business="{Binding}" ColumnWidth="20" HorizontalAlignment="Left" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListView>
</DockPanel>
The problem is that i can't find the source by RelativeSource Binding use FindByName.
The target control whatch on nested DataContext that is an item of the first and i cant set it in without adding to all this models the MyWidth property.
Is there a some enother way to bind them more correct?
I am thinking about a static property but it is not as correct as i want because i have planned to have a several instances of this control.

WPF - adding user control to listbox

I am trying to add a usercontrol to a ListBox.
My usercontrol contains a collection of basic elements like textbox and dropdowns, in a fashion so that it creates a row of elements.
The code for ListBox in my main window is as -
<GroupBox FontWeight="SemiBold" Foreground="#FF0CAEF9" Name="gbAddProducts" Style="{x:Null}" Header="ADD PRODUCTS" HorizontalAlignment="Left" Margin="0,256,0,0" VerticalAlignment="Top" Width="990" Height="207">
<ListBox Name="lstboxAddProduct" ItemsSource="{Binding Path=AddNewProductRowViewModelList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" BorderThickness="0" Margin="0,10,-2,23">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<ContentControl Content="{Binding AddNewProductRowViewModel}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="aepForError"/>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}" Foreground="White" Background="#DC000C" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</ListBox>
</GroupBox>
Here AddNewProductRowViewModelList is my user control list containing 5 controls.
The Problem that I see is that when I run the code, the screen has 5 rows, ie I can click on the area to figure out theer are 5 rows as that section gets highlighted. But they are not visible.
Could it be some 'bring to front' sort of issue.
Please advise.
Thanks in advance.
I would do it this way instead:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<ContentControl>
<local:AddNewProductRowView Datacontext="{Binding AddNewProductRowViewModel}"/>
</ContentControl>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>

Tabbing User Control In Listbox DataTemplate

I have a User Control in a ListBox and when I use the tab key to focus I'd like to get the focus on the TextBox in my Control instead of the Custom Control. How can I do it?
I simplified the code because In my control I have other UI Elements.
User Control Code:
<Grid>
<TextBox Name="txtFreeTextDescription" Style="{StaticResource TextBoxStyleLargeDynamic}" Text="{Binding Description, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
</Grid>
ListBox Code:
<ListBox Name="lsbItems" DataContext="{Binding}" KeyboardNavigation.TabNavigation="Local">
<ListBox.ItemTemplate>
<DataTemplate>
<local:SectionDynamicItem x:Name="ucSectionDynamicItem" Description="{Binding SectionItem.Description}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This works for me....
<ListBox ItemsSource="{Binding EmployeeList}"
KeyboardNavigation.TabNavigation="Continue">
<ItemsControl.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Focusable" Value="False"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="5" Focusable="False">
<TextBox Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
Check below link.
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/98d8423c-9719-4291-94e2-c5bf3d80cd46/
Thanks
Rajnikant

Resources