ListViewItem custom template: ContentPresenter stays empty - wpf

I have the following ListView in my code. views:GameCard is a custom UserControl and {Binding} is a valid DataContext object with three items. Without the custom ItemContainerStyle everything works perfectly — the list shows three GameCards with correct info, etc. As soon as I add the ItemContainerStyle part, I get nothing but three "ABCD"s; so the data is still loaded correctly, but my UserControl is no longer displayed (I only added the "ABCD" to check if the data was there, as otherwise I got nothing but empty box).
Every piece of info I could find online seems to indicate that just putting a ContentPresenter element in the template should work, but it doesn't seem to in this case. What am I missing?
<ListView Grid.Row="1" ItemsSource="{Binding}" BorderThickness="0,0,1,0"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF614B4B" Offset="0"/>
<GradientStop Color="#FFDA7070" Offset="1"/>
</LinearGradientBrush>
</ListView.Background>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<views:GameCard />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<TextBlock Text="ABCD" />
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>

You need to set the TargetType of your ControlTemplate. And in order to make your ItemTemplate work, you'd also need to bind the Content and ContentTemplate properties.
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Grid>
....
<ContentPresenter
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
... />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>

This may not be the case with you but so far I haven't had to modify the ItemContainerStyle yet, just the ListView.View. Since you're putting a Grid in your template style I'd assume you're looking for a GridView and this is how you do that:
<ListView.View>
<GridView>
<GridViewColumn Width="120">
<GridViewColumnHeader Height="14" >
<TextBlock Text="Type" FontSize="9"/>
</GridViewColumnHeader>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name, FallbackValue=MISSING}" />
<!-- or content presenter with bindings here -->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
...

your style and everything is fine .All what required is to Assign or bind the Content property to your ContentPresenter. I hope this will help.

Related

how to show selected text in combobox using custom template

I'm new in WPF, implementing application to show 2 combobox with different formated text.
I have created custom controltemplate for combobox
<ControlTemplate x:Key="GridGenreComboboxTemplate" TargetType="{x:Type ComboBox}">
<ControlTemplate.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent"/>
</ControlTemplate.Resources>
<StackPanel>
<ToggleButton
IsTabStop="False" x:Name="DropDownToggle"
HorizontalContentAlignment="Left"
IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
<ContentPresenter Content="{TemplateBinding SelectedItem}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="10,4"
VerticalAlignment="Center"
HorizontalAlignment="Left">
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}"/>
</Style>
</ContentPresenter.Resources>
<ContentPresenter.ContentTemplate>
<DataTemplate>
<TextBlock Text ="{Binding}"></TextBlock>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</ToggleButton>
<!-- Popup for dropdown when combobox is clicked and open -->
<Popup x:Name="PART_Popup" AllowsTransparency="True"
Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource TemplatedParent}}"
Placement="Bottom" IsOpen="{Binding Path=IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"
PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}">
<Grid MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border x:Name="Splitter" BorderThickness="0,3,0,0">
<Border.BorderBrush>
<SolidColorBrush>
<SolidColorBrush.Color>
<Color R="58" G="64" B="70" A="255"/>
</SolidColorBrush.Color>
</SolidColorBrush>
</Border.BorderBrush>
</Border>
<Grid Margin="0,3,0,0">
<Border CornerRadius="0,0,2,2" BorderThickness="1,0,1,1" x:Name="DropDownBorder"
Background="Green">
<Border.BorderBrush>
<SolidColorBrush Opacity="0.9">
<SolidColorBrush.Color>
<Color R="96" G="96" B="97" A="255"/>
</SolidColorBrush.Color>
</SolidColorBrush>
</Border.BorderBrush>
</Border>
<ScrollViewer CanContentScroll="true" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="Content"
KeyboardNavigation.DirectionalNavigation="Continue" HorizontalAlignment="Stretch"/>
</ScrollViewer>
</Grid>
</Grid>
</Popup>
</StackPanel>
</ControlTemplate>
in one combobox want to show formated text i.e. 1000 x 900, 200 x 300,.. and in another want to show
text like 1000 900 topology, 200 300 topology
I have formated combobox item and it shows correct value but selected value doesn't show on combobox.
how to do this using single combobox item template?
<ComboBox Grid.Row="0" ItemsSource="{Binding PossibleTopologysNew}"
SelectedItem="{Binding SelectedTopologyNew}"
Template="{StaticResource GridGenreComboboxTemplate}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Margin="10">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} x {1}">
<Binding Path="LeftNumber"/>
<Binding Path="RightNumber"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox Grid.Row="0" ItemsSource="{Binding PossibleTopologysNew}"
SelectedItem="{Binding SelectedTopologyNew}"
Template="{StaticResource GridGenreComboboxTemplate}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Margin="10">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1} topology">
<Binding Path="LeftNumber"/>
<Binding Path="RightNumber"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Your fix is simple... just don't declare a new ControlTemplate for the ComboBox - use the default one. If you really need to provide your own ControlTemplate, then just define one that contains the TextBox used to display the selected value. If you are new to WPF, then I very much doubt that you actually need to define your own ControlTemplate.
If however, you do need to declare your own ControlTemplate, then you should start with the default one (which you can find in the ComboBox Styles and Templates page on MSDN) and slowly and carefully remove the bits that you don't need and add the new bits that you do need. However, when declaring a ControlTemplate, you have to replace like with like. For example, look at the ContentPresenter from the default ControlTemplate:
<ContentPresenter x:Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="3,3,23,3"
VerticalAlignment="Stretch"
HorizontalAlignment="Left">
Now look at yours:
<ContentPresenter
Content="{TemplateBinding SelectedItem}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="10,4"
VerticalAlignment="Center"
HorizontalAlignment="Left">
To start with, you are attempting to data bind the ContentPresenter.Content property to a different property from the original ControlTemplate. So if I were you, I'd get rid of you non-working ControlTemplate and replace it with the original one. Ensure that it displays correctly before starting to change it and test it regularly as you continue to change it.

Assign an AutomationId to ContentPresenter in a GridViewRowPresenter (ListView)

I'm trying to do coded UI testing with a ListView that is has a list of checkbox items.
Due to trouble with the coded UI code selecting the checkbox cell, I have been trying to add AutomationId to the controls, so that the coded UI test works.
I'm almost there, in snoop I can see that the UIItemCell does not have AutomationId set, but I can't figure out how to set it in my app.
The UIItemCell is where I need to set AutomationId
I found with Snoop that it's the ContentPresenter
The ListView code is this complex, so I'll distill it a bit
<ListView HorizontalAlignment="Left"
Height="194"
Margin="53,123,0,0"
VerticalAlignment="Top"
Width="424"
AutomationProperties.AutomationId="listviewoption">
<ListView.Resources>
<Style x:Key="ListViewItemContainerStyle1" TargetType="{x:Type ListViewItem}">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Description }" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
AutomationProperties.AutomationId="Bxaid1" >
<Grid AutomationProperties.AutomationId="Gxaid1">
<!-- This is used when GridView is put inside the ListView -->
<GridViewRowPresenter AutomationProperties.AutomationId="gvrp"
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<!-- ... -->
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.Resources>
<ListView.ItemContainerStyle>
<StaticResource ResourceKey="ListViewItemContainerStyle1"/>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView AutomationProperties.AutomationId="aid1">
<GridViewColumn AutomationProperties.AutomationId="xc0"
DisplayMemberBinding="{Binding OptionName, Converter={StaticResource CamelCaseConverter}, Mode=OneWay}"
Width="180"/>
<GridViewColumn AutomationProperties.AutomationId="xc1"
Width="60">
<GridViewRowPresenter AutomationProperties.AutomationId="pp" />
<GridViewColumn.CellTemplate>
<DataTemplate >
<CheckBox Name="x1"
AutomationProperties.AutomationId="xaid1"
IsHitTestVisible="False"
HorizontalAlignment="Right"
Tag="{Binding OptionName}"
IsChecked=""
Padding="0"
Margin="0"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
There are some AutomationIds in there that haven't helped but are good points of reference; 'gvrp' is the GridViewRowPresenter [016] that holds the Content Presenter [017] that I want to put the id on, and 'xaid1' is the CheckBox inside the Content Presenter [017].
Please help before my head explodes.
I was able to do it eventually with
<GridViewRowPresenter Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<GridViewRowPresenter.Resources>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="AutomationProperties.AutomationId"
Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Name }"/>
</Style>
</GridViewRowPresenter.Resources>
</GridViewRowPresenter>
However, the automatically testing generated code (coded UI) still referenced the table column (even though it was redundant) which was the problem I was trying to avoid in the first place...
Anyway, it is possible to set the AutomationId in the ContentPresenter and in case it's helpful to anyone living in the future, here it is!

WPF ContentControl ErrorTemplate

I have a content control that has 2 data templates as follows;
<ContentControl Content="{Binding ContentViewModel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type vm:TypeA}">
<vs:TypeAView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:TypeB}">
<vs:TypeBView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
My problem is that both TypeA view and TypeB view have a textbox that is bound to a Name property. On my containing control, when an item is selected from a treeview the ContentViewModel property is set.
The data displays and can be edited as I want. However I have a problem when my TypeB view's name field is made invalid (I'm using EntLib 5 validation and have implemented IDataErrorInfo - so my binding has ValidatesOnDataErrors=True). Whilst the error template for the control is displayed as expected, when I then click on to TypeA in the treeview the error message is still displayed even though TypeA's name textbox is valid.
It's as if the binding is getting mixed up which is which. Strange thing is that it only happens one way, i.e. going from an invalid TypeB name to the TypeA view. Doesn't seem to happen when going from an invalid TypeA view.
Can anyone help indicate what I've done wrong?
This is the error template I'm using for the textbox;
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="true">
<Border DockPanel.Dock="Left" Margin="0,0,2,0" Width="14" Height="14" CornerRadius="10"
ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors), Converter={StaticResource ErrorContentConverter}}">
<Border.Background>
<RadialGradientBrush GradientOrigin="0.2,0.2" Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5">
<RadialGradientBrush.GradientStops>
<GradientStop Color="Pink" Offset="0" />
<GradientStop Color="Red" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Border.Background>
<TextBlock Text="!" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold" Foreground="White" />
</Border>
<AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center">
<Border BorderBrush="Red" BorderThickness="1" />
</AdornedElementPlaceholder>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>

Horizontal Scrollbars on a Fluid Listbox

What I want: To get the darn horizontal scrollbar to appear. I will be editing it a bit just so i fits the rest of the app's style scheme, but not too much.
What I have
Here is the code for the listbox as of now. Everything runs perfectly except the scrollbars dont appear. You might say... "well you dont have a scrollviewer anywhere", but I have tried inserting a scrollviewer in numerous places and still no luck.
The Listbox Code:
<ListBox ItemsSource="{Binding Items}" ItemTemplate="{StaticResource itemsdatatemplate}" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="{x:Null}" ItemsPanel="{StaticResource HorizontalListBoxTemplate}" ItemContainerStyle="{StaticResource TransparentListBox}" VerticalAlignment="Center" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" />
The 'TransparentListBox' (to shut-up the selected background color):
<Style x:Key="TransparentListBox" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid>
<Border x:Name="HoverBorderBackgroundBrush" BorderThickness="1" Margin="0,0,25,0" Background="Transparent"/>
<Border x:Name="SelectedBorderBackgroundBrush" BorderThickness="1" Margin="0,0,25,0" Background="Transparent"/>
<ContentPresenter></ContentPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Horizontal List Box (to make the listbox Horizontal, rather than standard vertical)
<ItemsPanelTemplate x:Key="HorizontalListBoxTemplate">
<StackPanel Orientation="Horizontal">
</StackPanel>
</ItemsPanelTemplate>
The Datatemplate (to actually show the Items)
<DataTemplate x:Key="itemsdatatemplate">
<local:ListItemControl HorizontalAlignment="Left" VerticalAlignment="Top" DataContext="{Binding}"/>
</DataTemplate>
I have a feeling its going to be a simple addition, but Thanks in advance.
Update
It looks like the scrollbars now do appear with this:
<Style x:Key="ScrollingListBox" TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<ScrollViewer HorizontalScrollBarVisibility="Visible">
<ItemsPresenter></ItemsPresenter>
</ScrollViewer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But they do not function accordingly. They feel... broken. However, if one was to define a static width (say 300) of the grid, then the ScrollViewer acts perfectly. Right now I have a completely fluid layout (meaning things stretch to fill), is this not acceptable for scrollviewers?
When you create your own template, you have to define the ScrollViewer in there and use an ItemPresenter instead of a ContentPresenter.
<Style x:Key="TransparentListBox" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid>
<Border x:Name="HoverBorderBackgroundBrush" BorderThickness="1" Margin="0,0,25,0" Background="Transparent"/>
<Border x:Name="SelectedBorderBackgroundBrush" BorderThickness="1" Margin="0,0,25,0" Background="Transparent"/>
<ScrollViewer x:Name="ScrollViewer" HorizontalScrollBarVisibility="Visible">
<ItemsPresenter />
</ScrollViewer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

How to style a WPF Expander Header?

I would like to apply a style on a WPF Expander Header. In the following XAML I have an Expander but the style is for all of it not just for the header.
Thanks.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="640"
>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Expander">
<Style.Resources>
<LinearGradientBrush x:Key="BackBrush" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#EF3132" Offset="0.1" />
<GradientStop Color="#D62B2B" Offset="0.9" />
</LinearGradientBrush>
</Style.Resources>
<Setter Property="Background" Value="{StaticResource BackBrush}"/>
</Style>
</StackPanel.Resources>
<Expander>
<StackPanel>
<TextBlock>Bike</TextBlock>
<TextBlock>Car</TextBlock>
<TextBlock>Truck</TextBlock>
</StackPanel>
</Expander>
</StackPanel>
</Page>
I have combined some XAML from Josh Smith and MSDN and came up with a solution. Indeed, the control (al least the header) must be retemplated.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400">
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Border" x:Key="RacePitBorderStyle" >
<Style.Resources>
<LinearGradientBrush x:Key="BackBrush" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#EF3132" Offset="0.1" />
<GradientStop Color="#D62B2B" Offset="0.9" />
</LinearGradientBrush>
</Style.Resources>
<Setter Property="Background" Value="{StaticResource BackBrush}"/>
</Style>
<DataTemplate x:Key="titleText">
<Border Style="{StaticResource RacePitBorderStyle}" Height="24">
<TextBlock Text="{Binding}"
Margin="4 0"
VerticalAlignment="Center"
Foreground="White"
FontSize="11"
FontWeight="Normal"
Width="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type Expander}},
Path=ActualWidth}"
TextWrapping="Wrap"/>
</Border>
</DataTemplate>
<Style TargetType="{x:Type Expander}">
<Setter Property="HeaderTemplate" Value="{StaticResource titleText}"/>
</Style>
</StackPanel.Resources>
<Expander Name="hcontCtrl" Header="This is the header.">
<StackPanel>
<TextBox>This is a textbox</TextBox>
<Button>A button</Button>
</StackPanel>
</Expander>
</StackPanel>
</Page>
I think Vasile's answer is on the right track, but it seems like it does a lot more than the original poster needed. All the original question was asking to do was to change the background of the header. While the change presented does do that, it also does other things.
One of these other things is to replace the default implementation, I believe a ContentPresenter, with a TextBlock. So what happens when later we change our Expander so that the header is more complicated? Maybe something like:
<Expander>
<Expander.Header>
<StackPanel>
<Border height="5" width="5" Foreground="Blue"/>
<TextBlock>Ha!</TextBlock>
</StackPanel>
</Expander.Header>
</Expander>
I don't know, but it's not good. Instead, I think we want to keep this simple.
<DataTemplate x:Key="expanderHeader">
<ContentPresenter
Content={Binding}
TextBlock.Background={StaticResource myBrush}/>
</DataTemplate>
<Style TargetType="Expander">
<Setter Property="HeaderTemplate" Value="{StaticResource expanderHeader}"/>
</Style>
That way when someone puts something that is not just text in our styled expander, we don't break. If you want to make sure you wrap the entirety of what they do with this background, which is probably desired, that would look like:
<DataTemplate x:Key="expanderHeader">
<Border Background={StaticResource myBrush}>
<ContentPresenter Content={Binding}/>
</Border>
</DataTemplate>
Depends what you want to style -- you can style any part of it. If you want to change the content in the header, just place all your UI in the Expander.Header property, and it'll show in the header area.
if that does't meet your needs, you probably need to re-template the control. Take a look at the control templates shipped in WPF here

Resources