WPF ContentControl inside of TabControl does not show DataTemplates - wpf

I have a ListView which has TabControl which shows 2 type of views.
<ListView x:Name="Devices" ItemsSource="{Binding Devices}">
<ListView.Resources>
<DataTemplate DataType="{x:Type app:DeviceViewModel}">
<Expander Header="{Binding Type}">
<StackPanel Orientation="Vertical">
<ContentControl>
<ContentControl.Resources>
<DataTemplate DataType="{x:Type app:DeviceAViewModel}">
<local:DeviceAView/>
</DataTemplate>
<DataTemplate DataType="{x:Type app:DeviceBViewModel}">
<local:DeviceBView/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
<TabControl x:Name="Channel" ItemsSource="{Binding Channels}" DisplayMemberPath="Index">
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Resources> <------ this part does't work
<DataTemplate DataType="{x:Type app:ChannelAViewModel}">
<local:ChannelAView DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type app:ChannelBViewModel}">
<local:ChannelBView DataContext="{Binding}"/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</StackPanel>
</Expander>
</DataTemplate>
</ListView.Resources>
</ListView>
The content for each tab item is just empty instead of showing the matching channel view.
If I remove the whole <TabControl.ContentTemplate> Tree It does show the matching objects strings inside the tabitem content.
Any idea why it doesn't show the data templates?
Thank you.

Try to Listview item template like this.
like this:
<ListView x:Name="Devices" ItemsSource="{Binding Devices}">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Vertical">
<ContentControl>
</ContentControl>
<TabControl x:Name="Channel" ItemsSource="{Binding Channels}">
</TabControl>
</StackPanel>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
or this:
<ListView x:Name="Devices" ItemsSource="{Binding Devices}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Vertical">
<ContentControl>
</ContentControl>
<TabControl x:Name="Channel" ItemsSource="{Binding Channels}">
</TabControl>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Okay so I figured it out.
I had to add Content={Binding} to the ContentControl or I could remove it and instead of using TabControl.ContentTemplate I placed the DataTemplates inside TabControl.Resources and it works fine.
<TabControl x:Name="Channel" ItemsSource="{Binding Channels}" DisplayMemberPath="Index">
<TabControl.Resources>
<DataTemplate DataType="{x:Type app:ChannelAViewModel}">
<local:ChannelAView DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type app:ChannelBViewModel}">
<local:ChannelBView DataContext="{Binding}"/>
</DataTemplate>
</TabControl.Resources>
</TabControl>

Related

WPF - Custom ErrorTemplate for all TextBoxes in App

I have a form with many textboxes, each require the same validation Error-template.
Now, i don't wanna write these validation error-templates for every textbox. So where do i have to put that, so that all textboxes are affected?
Textbox with Validation.ErrorTemplate:
<TextBox x:Name="textBox3" TextWrapping="Wrap" Height="23" Text="{Binding User_Id, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True}" VerticalAlignment="Top">
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="textBox"/>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}" Foreground="Red"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
My CustomControl:
public class ValidationTextBox : TextBox
{
static ValidationTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ValidationTextBox), new FrameworkPropertyMetadata(typeof(ValidationTextBox)));
//Validation.SetErrorTemplate(new ValidationTextBox(), )
}
public ValidationTextBox() { }
}
You need to define new Style for TextBox inside a "Resourse" tag of textbox's container. This style will be implemented for each textbox inside container.
Example:
<StackPanel>
<StackPanel.Resources>
<Style TargetType=TextBox>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="textBox"/>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}" Foreground="Red"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources
<TextBox/>
<TextBox/>
<TextBox/>
</StackPanel>

Change displayed text in XAML Combobox multi selection

I have the following code to allow users to select multiple items from a combobox. However when they click one item, it makes this the displayed text when combobox closes. Can I change the displayed text to something that isnt just the item selected. For example if the users select items A,B and D, I want the text part of the combobox to show "A, B, D"
<ComboBox ItemsSource="{Binding ListOfItems}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Width="20" />
<TextBlock Text="{Binding DisplayName}" Width="110" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Thanks
You could use a ContentControl with a Style that changes the ContentTemplate property for the selected item. The following sample markup should give you the idea.
<ComboBox ItemsSource="{Binding ListOfItems}">
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="ContentTemplate">
<Setter.Value>
<!-- the template for the items in the dropdown list -->
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Width="20" />
<TextBlock Text="{Binding DisplayName}" Width="110" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ComboBoxItem}}" Value="{x:Null}">
<Setter Property="ContentTemplate">
<Setter.Value>
<!-- the template for the selected item-->
<DataTemplate>
<ItemsControl ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=ComboBox}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" Margin="0 0 5 0"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Please refer to the following similar question for more information.
Can I use a different Template for the selected item in a WPF ComboBox than for the items in the dropdown part?

Use CollectionViewSource with TabControl

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.

DataTrigger WPF

I have two lists. List of Things and List of Fruits. If an item in the list of things is a fruit from the fruits list, I would like that item to be highlighted.
I would like this with data-binding and thru xmal and not code-behind b/c I am doing MVVM pattern. I have tried it with DataTrigger and Converter but can't get it to work. Please help.
Thanks.
<ListBox ItemsSource="{Binding Things}"
Name="ListOfThigns"
Grid.Row="1">
<DataTemplate>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Fruits}" >
<Setter Property="ListBoxItem.Background" Value="Green"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox>
<ListBox ItemsSource="{Binding Fruits}"
Name="ListOfFruits"
Grid.Column="1"
Grid.Row="1">
</ListBox>
Or, if Fruit inherits from Thing (a fruit is a thing):
<ListView ItemsSource="{Binding Things}">
<ListView.Resources>
<DataTemplate DataType="{x:Type dm:Fruit}">
<TextBlock Text="{Binding Name}" Background="RoyalBlue"/>
</DataTemplate>
<DataTemplate DataType="{x:Type dm:Thing}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.Resources>
</ListView>

Binding from within a DataTemplate?

I'm building a Silverlight control and I'm trying to set up bindings for the Header and Body ContentControls through their respective DataTemplates. I'm not sure why, but this does not work (silently fails). My only guess is that it is because the DataTemplates are StaticResources. Can anyone offer advice? The control has the following default template:
<Style TargetType="local:LayoutItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:LayoutItem">
<StackPanel>
<StackPanel.Resources>
<DataTemplate x:Key="DefaultHeaderTemplate">
<StackPanel>
<TextBlock Text="{Binding HeaderText}" FontSize="15"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="DefaultBodyTemplate">
<StackPanel>
<TextBlock Text="{Binding BodyText}" FontSize="12"/>
</StackPanel>
</DataTemplate>
</StackPanel.Resources>
<ContentControl x:Name="Header"
ContentTemplate="{StaticResource DefaultHeaderTemplate}" />
<ContentControl x:Name="Body"
ContentTemplate="{StaticResource DefaultBodyTemplate}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
Thanks!
Update
Actually, the following code does not work either, so my assumption about the StaticResources might be wrong.
<ContentControl x:Name="Header">
<ContentControl.ContentTemplate>
<DataTemplate x:Key="DefaultHeaderTemplate">
<StackPanel>
<TextBlock Text="{Binding HeaderText}" FontSize="15" />
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
I hate to answer my own question, but I got it working. It was really due to a problem elsewhere. For future reference, the following code works for me:
<Style TargetType="local:LayoutItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:LayoutItem">
<StackPanel x:Name="LayoutRoot">
<StackPanel.Resources>
<DataTemplate x:Key="DefaultHeaderTemplate">
<StackPanel>
<TextBlock FontSize="50" Text="{Binding Path=HeaderText}" />
</StackPanel>
</DataTemplate>
</StackPanel.Resources>
<ContentControl x:Name="Header" Content="{Binding}" ContentTemplate="{StaticResource DefaultHeaderTemplate}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
Please note the Content="{Binding}" addition. This was important. :)
This works on mine (3 Beta):
<ContentControl x:Name="Header">
<ContentControl.ContentTemplate>
<DataTemplate >
<StackPanel>
<TextBlock Text="{Binding HeaderText}" FontSize="15" />
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
<ContentPresenter />
</ContentControl>
However, I don't know why it works...I thought it was because you could put your template round the presenter, but then played a bit more and realised that anything you wrap the contentpresenter in just gets completely ignored.
You need to set the data context of the page somewhere. can be done in the code behind
/// ctor
public MyClass()
{
this.DataContext = ObjectThatIsDataContext;
}
or in XAML:
<UserControl ...>
<UserControl.Resources>
<myNS:MyClass x:Name="TheContext" x:Key="TheContext" /> </UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource TheContext}" >
<TextBlock Text="{Binding Path=Field1}" />
</Grid>
</UserControl>
See also this post

Resources