Item Template in the ItemPresenter - silverlight

In this template ItemPresenter just defines host panel for the Items.
Is it possible to define ItemTemplate?
<ControlTemplate x:Key="ItemsControlTemplate" TargetType="ItemsControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer>
<ItemsPresenter Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"/>
</ScrollViewer>
</Grid>
</ControlTemplate>
To go further, I've created a class:
public class ItemsControlExtended : ItemsControl
{
public ItemsControlExtended()
{
DefaultStyleKey = typeof(ItemsControlExtended);
}
}
And I would like to create a dependency property "ItemsMargin".
After I've done that I'm supposed to bind an Item "Margin" property to "ItemsMargin".
How it would be possible to implement?
GetContainerForItemOverride ?
PrepareContainerForItemOverride ?
OnApplyTemplate ?

You can't define the ItemTemplate from with the ControlTemplate for the control as a whole.
Instead you would create a style that includes your control template and the the other templates as required:-
<Style x:Key="ItemsControlStyle" TargetType="ItemsControl>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ItemsControl">
<!-- your template as above -->
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<!-- An alternative to StackPanel if so desired -->
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DatalTemplate>
<!-- The item template you wanted -->
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Now you can style the ItemsControl:-
<ItemsControl Style="{StaticResource ItemsControlStyle}">

Related

Why does my ControlTemplate for Grid not work?

I want to set the ControlTemplate of grid to look like this:
Here is the markup:
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.1*"></ColumnDefinition>
<ColumnDefinition Width="0.8*"></ColumnDefinition>
<ColumnDefinition Width="0.1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.1*"></RowDefinition>
<RowDefinition Height="0.8*"></RowDefinition>
<RowDefinition Height="0.1*"></RowDefinition>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="1" Grid.Column="1"></ContentPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Style>
<Border Background="Red"></Border>
</Grid>
I don't know why the ControlTemplate doesn't work. Is there something wrong with my markup?
Grid.RowDefinitions and Grid.ColumnDefinitions are no Dependency properties, and can't be set by a Style. I guess this is why you used a ControlTemplate instead of setting it directly. But the default Style of the ControlTemplate does not show any Borders/Lines, therefore you may want to use a ContentPresenter (Wrap it around your Grid and not inside of it).
Additionally: To place the Border in the Center of the Grid, you must set the Row and Column, otherwise the Border is spread across the whole Grid. And place it right there where you have your ContentPresenter inside the Controltemplate, not outside of it.
Edit
Due to the clarification in the comments i suggest to use a Border which shrinks the Content with a RenderTransform.
Style
<Style x:Name="Shrink80pcBorder" TargetType="{x:Type Border}">
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="0.8" ScaleY="0.8"/>
</Setter.Value>
</Setter>
</Style>
Usage
<Border Background="Red" Style="{StaticResource Shrink80pcBorder}">
<Your Item/Content/>
</Border>

How to change the custom content of a ToggleButton in XAML

I have changed the style of a ToggleButton in a ResourceDictionary. In my style I'm changing the ControlTemplate of the ToggleButton and I place an image before the Content.
Now here is my question: The image needs to be changed for different ToggleButtons I use in XAML, should I define different styles for each ToggleButton with different image or is there some way I can change its image in XAML?
<Style x:Key="BaseToggleButton" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<StackPanel Orientation="Horizontal" x:Name="Border">
<Image Width="13" Height="13" Source="{StaticResource ColumnsLayoutMiniIcon}"/>
<TextBlock>
<ContentPresenter/>
</TextBlock>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Is there a way I can change the image here instead of in style?
<RadioButton Content="Plan View"
GroupName="View"
Style="{StaticResource BaseToggleButton}">
</RadioButton>
Sure, you can piggy back in on an unused property that comes in real handy for this sort of situation called Tag which you can you use to pass in your image path or resource declaration etc once we go bind it to the template like;
<Style x:Key="BaseToggleButton" TargetType="ToggleButton">
<!-- Let's give it a default -->
<Setter Property="Tag" Value="{StaticResource ColumnsLayoutMiniIcon}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<StackPanel Orientation="Horizontal" x:Name="Border">
<Image Width="13" Height="13"
Source="{TemplateBinding Tag}"/>
<ContentPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now we can either leave as is and it should display that mini icon as your default, you can specify a new one at the instance level like;
<ToggleButton Content="Plan View"
Tag="{StaticResource ADifferentImagePathOrResourcePointingToOne}"
Style="{StaticResource BaseToggleButton}"/>
Hope this helps, cheers.

wpf expander template: No ItemsPresenter present when created in Collapsed state

Here's in short a template I use for a wpf TreeViewItem:
<Style x:Key="MyExpanderStyle" TargetType="Expander">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<Border CornerRadius="2" BorderBrush="Gray" BorderThickness="1">
<Grid SnapsToDevicePixels="True">
<Grid.RowDefinitions>
<RowDefinition Name="ExpHeaderRow" Height="20"/>
<RowDefinition Name="ExpContentRow" Height="*"/>
</Grid.RowDefinitions>
<HeaderedContentControl Grid.Row="0" Header="{Binding}" HeaderTemplateSelector="{StaticResource MyHeaderTemplateSelector}"/>
<ContentPresenter x:Name="ExpandSite" Grid.Row="1" Visibility="Collapsed" Margin="{TemplateBinding Padding}">
</ContentPresenter>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
the style is used in another style applied to a TreeViewItem:
<Style x:Key="tviStyle" TargetType="TreeViewItem"}
...
<Expander Style="{StaticResource MyExpanderStyle}" IsExpanded="{Binding Path=IsExpanded}">
<Expander.Content>
<ItemsPresenter/> <!-- is null when not yet expanded !! -->
</Expander.Content>
</Expander>
...
now in code, i use this:
ItemsPresenter itemsPresenter = itemsControl.FindVisualDescendant<ItemsPresenter>();
which gives me the itemsPresenter, when I have set IsExpanded = true in the viewmodel constructor.
It gives me null, when IsExpanded = False at construction.
So wpf creates UIElements at the point the Expanders state goes to "Expanded". That is ok.
But there are cases when Items get created in a collapsed state, and I want to access the data that is bound on the Expanders Content (the itemsPresenter). The viewmodels have been created, all data is present, except the wpf UIElements.
There are clumsy ways like going Expanded = true, in the viewmodel constructor, then set Expanded = false (e.g. in OnLoaded)
Is there a nicer way? Thank you!

How to set GroupStyle for custom control

I have created a custom control library project and did the followings:
The custom control is derived from ComboBox
Add a resource dictionary file rd.xaml under Themes folder
Define some styles in the rd.xaml file
<Style x:Key="GroupComboBoxStyle" TargetType="{x:Type local:GroupComboBox}">
<Setter Property="ItemContainerStyle" >
<Setter.Value>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="IsEnabled" Value="{Binding Available}"/>
</Style>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:GroupComboBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" Width="150" Height="Auto" >
<!-- add scroll bar -->
</WrapPanel>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Item}" Width="40"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<CollectionViewSource x:Key="groupedData" Source="{Binding Items}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<Style x:Key="groupComboBoxItemStyle" TargetType="{x:Type ComboBoxItem}">
<Setter Property="Width" Value="50" />
</Style>
<GroupStyle x:Key="groupStyle">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2">
<TextBlock Text="{Binding Name}" HorizontalAlignment="Stretch" Background="YellowGreen"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
Then I want to set the group style to my custom control at runtime
But the groupstyle cannot be found, how to get it from the resource dictionary file?
public GroupComboBox()
{
GroupStyle style = new GroupStyle();
// get the groupstyle
style.HeaderTemplate = (DataTemplate)this.FindResource("groupStyle");
this.GroupStyle.Add(style);
}
A WPF CustomControl is supposed to be lookless. This means, that the code should only contain the control's logic but nothing related to how it looks, styling, etc. This should all be done using the style which is created for you in the Generic.xaml.
Anyway, it is totally valid to wish for a green background in your header... I would suggest to create a bindable dependency property for a DefaultGroupStyle in your control. I've implemented and tested it and it does the trick:
The control GroupComboBox:
public class GroupComboBox : ComboBox
{
public static readonly DependencyProperty DefaultGroupStyleProperty =
DependencyProperty.Register("DefaultGroupStyle", typeof (GroupStyle), typeof (GroupComboBox), new PropertyMetadata(default(GroupStyle), OnDefaultGroupStyleChanged));
private static void OnDefaultGroupStyleChanged(DependencyObject s, DependencyPropertyChangedEventArgs a)
{
var c = (GroupComboBox) s;
if (a.NewValue == null) return;
if (c.GroupStyle.Count == 0)
c.GroupStyle.Add((GroupStyle) a.NewValue);
}
public GroupStyle DefaultGroupStyle
{
get { return (GroupStyle) GetValue(DefaultGroupStyleProperty); }
set { SetValue(DefaultGroupStyleProperty, value); }
}
static GroupComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(GroupComboBox), new FrameworkPropertyMetadata(typeof(GroupComboBox)));
}
}
and the styles in Generic.xaml (feel free to move the styles to another file but don't forget to merge it into Generic.xaml. Note that I removed the key on the default style for the ComboBox. It won't get applied automatically otherwise...
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomControlLibrary1">
<GroupStyle x:Key="GroupStyle">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2">
<TextBlock Text="{Binding Name}" HorizontalAlignment="Stretch" Background="YellowGreen"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
<Style TargetType="{x:Type local:GroupComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:GroupComboBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="DefaultGroupStyle" Value="{StaticResource GroupStyle}" />
</Style>
</ResourceDictionary>
Please let me know if this works for you and feel free to ask, if there's anything unclear.

Does anyone have a simple example of a UserControl with a single ContentPresenter?

So far, I have this:
<UserControl
x:Class="MyConcept.ExpanderPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Border
Style="{StaticResource Border_PanelStyle}"
CornerRadius="3" />
<ContentPresenter />
</Grid>
</UserControl>
Sample usage of this UserControl:
<nc:ExpanderPanel
Grid.Row="0">
<Expander
IsExpanded="True"
Header="NMT Users">
<StackPanel>
...
</StackPanel>
</Expander>
</nc:ExpanderPanel>
Discussion
If I run this, I see nothing. No content is presented, not even the border that is built into the UserControl.
I thought maybe I needed to make the ContentPresenter a dependency property, but I couldn't figure out how I would link the property to the ContentPresenter in the UserControl's XAML.
Can someone provide a simple example that shows how to build a UserControl (or some kind of custom control) with a single ContentPresenter?
ContentPresenters are main used in ControlTemplates and bound with a TemplateBinding to the ContentControl.Content.
from this site... a control template for a button that uses a ContentPresenter
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="White" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Rectangle Fill="{TemplateBinding Property=Background}" />
<ContentPresenter
Content="{TemplateBinding Property=ContentControl.Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Resources