ItemsControl where entire item is expander for sub-items - wpf

So it's kind of hard to describe the behavior I want, so I drew a nice picture with my amateur paint skills.
So basically I want the headings of the ItemsControl to operate like an expander, without the ugly expander icon that comes along (so just clicking anywhere in the box would expand to see the sub items). I already have the data-hierarchy in place, but I cannot for the life of me get the expander to behave as I want, anybody had any success overriding expander styles to accomplish something like this, or is there another control that can accomplish something like this easier? Here's some simple code, but has the ugly expander button, overriding the headertemplate and style of the expander has only made it look much worse.
<ItemsControl ItemsSource="{Binding Collection}"
HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="Heading">
<ItemsControl ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="Sub-Heading">
<ListBox ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch"/>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

You just have to use a custom style for your expander if you don't like the built in style :)
Here is a start :
<Style x:Key="customExpander">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="Expander">
<DockPanel>
<ToggleButton DockPanel.Dock="Top"
IsChecked="{Binding Path=IsExpanded,Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Background="{TemplateBinding Background}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
Foreground="{TemplateBinding Foreground}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontStretch="{TemplateBinding FontStretch}"
FontStyle="{TemplateBinding FontStyle}"
FontWeight="{TemplateBinding FontWeight}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="{TemplateBinding Padding}"
Name="HeaderSite"
MinWidth="0"
MinHeight="0"
Margin="1,1,1,1">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<TextBlock Text="{TemplateBinding Content}" Background="{TemplateBinding Background}" />
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
Name="ExpandSite" Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Visibility="Collapsed"
Focusable="False"
DockPanel.Dock="Bottom" />
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="HeaderSite" Property="Background" Value="Gold" />
<Setter TargetName="ExpandSite" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" Value="{DynamicResource DisabledTextBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then change your xaml to use this style :
<ItemsControl ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="Heading" Style="{StaticResource customExpander}" Background="LightSteelBlue" >
<ItemsControl ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="Sub-Heading" Style="{StaticResource customExpander}" Background="LightSalmon">
<ListBox ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch"/>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
EDIT The important part is the
<ControlTemplate TargetType="ToggleButton">
<TextBlock Text="{TemplateBinding Content}" Background="{TemplateBinding Background}" />
</ControlTemplate>
if you want to customize. This is a very raw solution so you have plenty of room to enhance it ;)

Related

XAML - Button Content disappeared after giving content through a TextBlock (Except for the last Row of the DataGrid)

I was using button content as <Setter Property="Content" Value="Accept">
Everything was okay till then
but, I had to put a TextBlock for the content since I want CharacterEllipsis in the button content.
Since then the content of the Button has disappeared
and the strange thing is that it has disappeared for all the rows except the last row. Button code for all the rows is the same. Since I am using
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<--code for this column of every row-->
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
here are the code snippet and a screenshot
<DataGrid.Columns>
<!--Item Desc column-->
<DataGridTemplateColumn Width="1.5*" CanUserResize="False" CanUserSort="False"
IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Margin="20 0 0 0">
<Label Style="{StaticResource supplyCabinetColumnHeaderStyle}">
<Label.Content>
<TextBlock Text="Description" TextTrimming="CharacterEllipsis"/>
</Label.Content>
</Label>
<TextBlock Text="{Binding ItemDescription}" Margin="5 6 0 0" Style="{StaticResource supplyCabinetReadOnlyDataStyle}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--Button Column-->
<DataGridTemplateColumn Width="0.7*" CanUserResize="False" CanUserSort="False" IsReadOnly="True" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Height="25" FontSize="12" FontFamily="Arial"
HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0 25 10 4" Padding="10 5 10 5">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="Accept" TextTrimming="CharacterEllipsis"/>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="#DFE0E2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="4" Background="{TemplateBinding Background}" BorderThickness="0.5" BorderBrush="Gray">
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
Button Content Issue
include <TextBlock Text="Accept" TextTrimming="CharacterEllipsis"/> into button's template instead of ContentPresenter. Setter uses only one instance of TextBlock for each Button. But TextBlock is FrameworkElement, and cannot have multiple Parent (can't be displayed in multiple places) . If you move it to template, each Button will get its own instance of TextBlock.
<Button.Style>
<Style TargetType="Button">
<Setter Property="Content" Value="Accept">
<Setter Property="Background" Value="#DFE0E2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="4" Background="{TemplateBinding Background}" BorderThickness="0.5" BorderBrush="Gray">
<TextBlock Text="{TemplateBinding Content}"
TextTrimming="CharacterEllipsis"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>

wpf listview column header vertical line not corresponding to item vertical line

In my wpf application I have a listView with its view set to a gridView. I've defined separately borders for column headers and item cells but they are not inline with each other. In case I'm not too clear, here's an image of the problem:
The styles are as follows:
-> GridView columnHeader:
<Style TargetType="{x:Type GridViewColumnHeader}" x:Key="style_header">
<Setter Property="BorderBrush">
<Setter.Value>
<ImageBrush ImageSource="/images/black_line.png"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
<Grid SnapsToDevicePixels="true">
<Border x:Name="HeaderBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0,1,0,1" Background="{TemplateBinding Background}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition MaxHeight="7"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle x:Name="UpperHighlight" Fill="#FFE3F7FF" Visibility="Collapsed"/>
<Border Padding="{TemplateBinding Padding}" Grid.RowSpan="2">
<ContentPresenter x:Name="HeaderContent" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="0,0,0,1" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</Grid>
</Border>
<Canvas>
<Thumb x:Name="PART_HeaderGripper" Style="{StaticResource GridViewColumnHeaderGripper}"/>
</Canvas>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
-> ListView item:
<ListView x:Name="lstContent">
<ListView.View>
<GridView>
<!-- checkbox column -->
<GridViewColumn HeaderContainerStyle="{StaticResource style_header}">
<GridViewColumn.Header>
<TextBlock Text=""/>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderBrush="{StaticResource imgBrushBorder}" BorderThickness="0,0,1,0" Margin="-6,-2,-6,-2">
<CheckBox Tag="{Binding Sync}" IsChecked="{Binding Sync}" Style="{StaticResource style_chkManagement}"/>
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
This one is in static resources:
<ImageBrush x:Key="imgBrushBorder" ImageSource="/images/black_line.png"/>
Q: What is wrong and how can that be solved?
Any help highly appreciated.

How do I databind a stylized Listview to a specific field?

I have a ListView that uses a template to display a check box. I can not figure out how to get it to databind to the MapName property. Any help would be greatly appreciated. Is there anyway to sort the ListView on if the checkbox is checked?
<Window.Resources>
<ControlTemplate x:Key="ItemTemplate" TargetType="ListViewItem">
<Border
BorderThickness="{TemplateBinding Border.BorderThickness}"
Padding="{TemplateBinding Control.Padding}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True"
>
<ContentPresenter
Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
/>
</Border>
</ControlTemplate>
<Style TargetType="ListViewItem">
<Setter Property="Template" Value="{StaticResource ItemTemplate}" />
</Style>
<DataTemplate x:Key="ItemDataTemplate">
<CheckBox
x:Name="checkbox"
Content="{Binding}"
IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}, Path=IsSelected}"
Foreground="{DynamicResource WhiteToLightGrayFlatBrush}"
/>
</DataTemplate>
</Window.Resources>
<Border Grid.Row="7" BorderThickness="0,1,0,0" Margin="0,0,0,0" Padding="1,3,3,3"
BorderBrush="{DynamicResource GrayBorderBrush}" >
<StackPanel Orientation="Vertical">
<Label HorizontalAlignment="Left"
Content="Selected Maps:"
Foreground="{DynamicResource WhiteToLightGrayFlatBrush}"
Style="{StaticResource LabelDefaultTransparentBkgr}" />
<ListView
x:Name="mListView"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Background="Transparent"
SelectionMode="Multiple"
MinHeight="73"
MaxHeight="73"
ItemTemplate="{StaticResource ItemDataTemplate}"/>
</StackPanel>
</Border>
Thanks!
Dave
Provided the ListView.ItemsSource is bound to collection of objects with MapName property, just setting Content="{Binding MapName}" in your DataTemplate won't work?
Sorting can be implemented easily as described here.

Display same content twice in WPF Button

We want to style a wpf button so that the content set in the button is displayed twice. The reason for this is that we want to achieve a drop shadow effect of the button's content. Out thought was to have two ContentControls in the Button style like below:
<ContentControl x:Name="ContentControl" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
<ContentControl Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Foreground="White" Margin="0,1,0,0" />
So, one ContentControl is for displaying the real content and one ContentControl is for displaying the same content with a little margin so that it gives the effect of being the drop shadow. The problem is that it doesn't show content in both content controls. Only one of them shows content. How can I successfully show content in both content controls?
Also, the dropshadow effect is not an option since the button's content becomes blurry.
Thanks for help!
Buttons with Nested Content:
<Style x:Key="ShadowButton"
TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle Width="{Binding ActualWidth,
ElementName=presenter}"
Height="{Binding ActualHeight,
ElementName=presenter}">
<Rectangle.Fill>
<VisualBrush AlignmentX="Left"
Stretch="None"
Visual="{Binding ElementName=presenter}" />
</Rectangle.Fill>
<Rectangle.RenderTransform>
<TranslateTransform X="3"
Y="3" />
</Rectangle.RenderTransform>
</Rectangle>
<!-- You can replace the following line to a ContentControl if you absolutely have to -->
<ContentPresenter x:Name="presenter"
ContentSource="Content" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usage can then be dynamic like:
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="36"
Style="{StaticResource ShadowButton}">
<StackPanel>
<Button Margin="2"
Content="A" />
<Button Margin="2"
Content="B" />
<TextBox Margin="2"
Text="Blah" />
</StackPanel>
</Button>
By using a VisualBrush your not having 2 ContentControl / ContentPresenter in your Style and are just rendering one into a Brush to fill a rectangle and get your effect.
Duplicating Visual Tree with a Template
Try to have a UserControl do this than a Button in the first place. You need to use a Template if you want to duplicate the Visual Tree in your style.
<Style x:Key="ShadowButton"
TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<ContentControl x:Name="shadow"
ContentTemplate="{TemplateBinding ContentTemplate}"
Foreground="SpringGreen">
<ContentControl.RenderTransform>
<TranslateTransform X="50"
Y="50" />
</ContentControl.RenderTransform>
</ContentControl>
<ContentControl x:Name="presenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Foreground="SlateBlue" />
</Grid>
<ControlTemplate.Triggers>
<Trigger SourceName="presenter"
Property="IsMouseOver"
Value="True">
<Setter TargetName="shadow"
Property="Foreground"
Value="Teal" />
<Setter TargetName="presenter"
Property="Foreground"
Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and usage:
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Style="{StaticResource ShadowButton}">
<Button.ContentTemplate>
<DataTemplate>
<StackPanel>
<Button Margin="2"
Content="A"
Foreground="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContentControl}},
Path=Foreground}" />
<TextBox Margin="2"
Foreground="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContentControl}},
Path=Foreground}"
Text="Blah" />
</StackPanel>
</DataTemplate>
</Button.ContentTemplate>
</Button>
I tried this in a small dummy application and it works just fine. See if this is what you want.
<Window.Resources>
<Style x:Key="test" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<ContentControl Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"/>
<ContentControl Foreground="DarkGray"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}">
<ContentControl.RenderTransform>
<TranslateTransform Y="2" X="2"/>
</ContentControl.RenderTransform>
</ContentControl>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button Style="{StaticResource test}">
Test
</Button>
</Grid>

WPF combobox with image in case of null value

I would like to have a combobox in WPF whose items are of type string (like the default). The only difference is that I want to display an image (error) if the string is x:null. Anyone an idea how I can create an itemtemplate or style to display an image in the case of a null (or empty) string and the string in all other cases in the combobox.
Thanks!
-- Carsten
Here is an example that uses an ItemTemplate and a DataTrigger to only show an image if the item is null:
<Grid>
<Grid.Resources>
<x:Array x:Key="sampleData" Type="sys:String">
<sys:String>abc</sys:String>
<sys:String>def</sys:String>
<sys:String>ghi</sys:String>
<x:Null/>
</x:Array>
</Grid.Resources>
<StackPanel>
<ComboBox ItemsSource="{StaticResource sampleData}" Text="abc">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding}"/>
<Image x:Name="errorImage" Source="http://thecybershadow.net/misc/stackoverflow.png" Visibility="Collapsed"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Null}">
<Setter TargetName="errorImage" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Grid>
and it looks like this where the fourth null item is displayed as a stack overflow image:
Ok, here is the remaining piece in the style combobox (edit a copy of it and look for ContenPresenter and replace with this):
<Grid>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" IsHitTestVisible="false" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"/>
<Image x:Name="errorImage" Source="/Autodesk.UtilityDesign.Electric.UI;component/Themes/Images/Error.ico" Visibility="Collapsed" Width="16"/>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="SelectedItem" Value="{x:Null}">
<Setter TargetName="errorImage" Property="Visibility" Value="Visible"/>
</Trigger>

Resources