Binding from within a DataTemplate? - silverlight

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

Related

Datagrid - ItemsPresenter - vertical Scrollviewer

Hello Stackoverflow Members,
i'm new! sorry for the quick an short problem. But i need your help!
i have a Datagrid with 2000 rows. (grouped on CustomerNbr)
vb.net
Dim grouped As New ListCollectionView(full_list)
grouped.GroupDescriptions.Add(New PropertyGroupDescription("CustNbr"))
Datagrid.ItemsSource = grouped
This code worked fine
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander MaxHeight="400" FontWeight="Bold" Background="{Binding Path=Items, Converter={StaticResource convert_Backcolor}}" >
<Expander.Header>
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding Path=Name}" Margin="10,0,10,0" />
<TextBlock Text="{Binding Path=Items, Converter={StaticResource convert_Cust_Name}}" MinWidth="300" Margin="10,0,10,0" />
<TextBlock Text="{Binding Path=Items, Converter={StaticResource convert_Summe},ConverterCulture=de-DE, StringFormat={}{0:n2}}" HorizontalAlignment="Right" Margin="40,0,0,0" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
But when i add a scrollviewer at the ItemsPresenter i get an error:
<ScrollViewer>
<ItemsPresenter />
</ScrollViewer>
the error comes, when i make the second grouped custnbr
screen
Error: invalidOperationExeption was unhandled
I need the ScrollViewer because i have so many invoices on the CustNbr. The Problem is in Expander. In Expander.Content the vertical ScrollViewer is not support or something...
can anybody help me?
SOLUTION: in Datagrid Header this statements are not allowed: CanUserAddRows="False" CanUserDeleteRows="False" IsReadOnly="True"
also see here:
https://blogs.msdn.microsoft.com/vinsibal/2008/10/01/overview-of-the-editing-features-in-the-wpf-datagrid/

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>

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.

WPF Setting both DataTemplate and ControlTemplate on a control does not work

I am obviously missing something very basic here. I have found similar questions but from none of the answers I was able to comprehend what I am doing wrong.
When I set ControlTemplate, my DataTemplate is not picked up.
I have created a very simple example of my problem:
<Window x:Class="WpfTesterProject.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTesterProject"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Person}">
<StackPanel>
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text="{Binding LastName}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding Content}">
<ContentControl.Template>
<ControlTemplate>
<Border BorderBrush="Blue" BorderThickness="2">
<ContentPresenter />
</Border>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
</Window>
What I want to do is select user-defined data template loaded at runtime, but also I want to, for example, wrap every single element in border, no matter what the user template is or even if he did not specify any templates at all.
From what I have read from similar questions, I have to use <ContentPresenter /> in the ControlTemplate, but the result is the same as if I remove it - only border is shown.
I reproduced your application. It seems the problem is in the TargetType property of the ControlTemplate:
<ContentControl Content="{Binding}">
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Border BorderBrush="Blue" BorderThickness="2">
<ContentPresenter />
</Border>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>

What stops the tab?

I have the following xaml:
<ListBox ItemsSource="{Binding Path=ItemProperties.GeneralProperties}" Grid.Row="1"
Margin="0" Style="{StaticResource ListBoxStyle1}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="320" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" Grid.Column="0" />
<ContentPresenter Content="{Binding Converter={StaticResource PropertyInput}}" Grid.Column="1" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
where the ContentPresenter contains a TextBox, or a ComboBox, or a CheckBox.
To switch between the controls I need twice press the tab. Why???
I've already tried to comment the whole first column, without the TextBlock, unsuccessfully.
This worked for me for a DataGrid (which has a similar templating system).
<UserControl.Resources>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
</Style>
</UserControl.Resources>
Then anything which is a TabStop within the datagrid would work as a tabstop, but nothing else. Sorry I'm not sure what the equivalent code is for ListBox - but you may be able to figure it out from this.

Resources