TemplateBinding in ItemsPanelTemplate - silverlight

I'm building a custom ItemsControl in Silverlight that (amongst other things) allows items to be displayed horizontally or vertically at runtime. How can I bind the Orientation property of the ItemsPanel to the Orientation property of my parent control? I've tried using TemplateBinding (which works inside the ControlTemplate) but does not seem to work inside the ItemsPanelTemplate, am I doing something wrong?
<Style TargetType="CustomItemsControl">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="{TemplateBinding Orientation}" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>

Use a RelativeSource:
<Style TargetType="CustomItemsControl">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="{Binding Orientation, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type CustomItemsControl}}}" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
Edit after comment: Silverlight doesn't support RelativeSource, but this post by Colin Eberhardt explains how it can be implemented manually.

Related

WPF/Designer: ItemContainerStyle RadioButton as ToggleButton. Works when in runtime but fails in designer

I have a ItemsControl with an ItemContainerStyle based on ToggleButton.
This works just fine at runetime, but it casts an error in the designer. (So I have to comment it out if i want to use the designer)
I populate this with code behind. Using RadioButtons.
InvalidOperationException: A style intended for type 'RadioButton' cannot be applied to type 'ContentPresenter'.
Iv lived with this for along time now, but its time to see if there is a solution!
Any ideas how to work around this?
<ItemsControl x:Name="BrushButtons"
Grid.Row="1"
Grid.Column="3"
VerticalAlignment="Bottom">
<ItemsPanelTemplate>
<StackPanel VerticalAlignment="Bottom" Orientation="Vertical" />
</ItemsPanelTemplate>
<ItemsControl.ItemContainerStyle>
<Style BasedOn="{StaticResource {x:Type ToggleButton}}" TargetType="{x:Type RadioButton}">
<Setter Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="-90" />
</Setter.Value>
</Setter>
<Setter Property="Width" Value="70" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
Kind Regards.

Excess border selection in WPF's Lisbox [duplicate]

I have a ListBox in which each item is a StackPanel. The StackPanel consist of an Image and a TextBlock below it:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<Image>
<Image.Source>
<BitmapImage UriSource="{Binding Path=ImageFilePath}"/>
</Image.Source>
</Image>
<TextBlock Text="Title" TextAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
It looks like this:
When the user select an item, I get the default blue rectangle that surround the StackPanel:
Now, I want to make a different border for the selected-item, but I want it to surround only the image.
I know how to make a control template and put a custom border around the ContentPresenter, but this, of course, will surround the whole StackPanel, not only the Image.
I don’t know if making changes to the ContentPresenter is possible, and if it is a good idea at all. If there is other way to achieve the look I want, it will be fine as well.
Right, the ListBox's own ContentPresenter isn't helpful for what you're doing. You want to a) eliminate the ListBox's own selection visuals and b) replace them with something more suitable in the DataTemplate for your items.
The default selection visual is applied by the default template for ListBoxItem. So replace that template. Using a Style in the resources for your ListBox, apply your own control template to ListBoxItem. Not much to it, just present the content and don't provide a selection background. Then you handle the selection visuals with a trigger in your data template, where your image and your label are defined and you can apply changes to one and not the other. The below example works for me.
Note that there's some fiddling with the HorizontalAlignment on the Border element to make it cling to the Image element within it. Also, I wrote a quickie test viewmodel whose Items property is called Items; I assume this is not the name of the collection member you're using to populate your own ListBox.
<ListBox
Margin="8"
ItemsSource="{Binding Items}"
>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid>
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Border
x:Name="HighlightBorder"
BorderThickness="4"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="10"
>
<Border.Style>
<Style TargetType="Border">
<!-- MUST set default BorderBrush via a style, if you set it at all.
As an attribute on the Border tag, it would override the effects of
the trigger below.
-->
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
</Border.Style>
<Image Source="{Binding ImageFilePath}" />
</Border>
</Grid>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Value="True">
<Setter TargetName="HighlightBorder" Property="BorderBrush" Value="Orange" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Displaying images in a grid

I have an ObservableCollection of BitmapSources and I want to display them all in a grid and override the selected and not selected styles. I have been looking at a lot of different ways to do this but have not managed to get it working to my satisfaction. My latest attempt looks something like this:
<ListBox ItemsSource={Binding Images}>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel ItemsHost="True">
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="3">
<Image Source={Binding}>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This does display the images, but the WrapPanel is always just one row... I don't want to have to scroll horizontally, so I want it to make row breaks by itself or be able to tell it that it should only have 3 items per row or something like that. Without the WrapPanel the images take one row each. Also, I don't really understand how to override the style for selected items and such since the DataTemplate's DataType is BitmapSource now, not ListBoxItem...
I have also tried a DataGrid (which seems more appropriate) with similar results.
How do I do this?
Define UniformGrid as your ItemsPanel and set the Columns value to the number of items you want to have per row.
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
And your style to override default selection background and border brush setting:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Transparent"/>
</Style.Resources>
<Setter Property="BorderThickness" Value="0.5"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="blue"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="BorderBrush" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
You could simply disable horizontal scrolling. This will make the WrapPanel wrap its children.
<ListBox ItemsSource="{Binding Images}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="3">
<Image Stretch="None" Source="{Binding}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
-Edit-
The part about listbox width in this answer is not correct, see clemens answer instead
-Original post-
You need to constrict the width of the list box, either by setting the width or with some other parent layout. Otherwise the wrap panel will claim all the available width to put everything on one row. This happens to me alot when using scrollpanes for example
One tip is to setting different background colors to elements to see who is actually grabbing the space
You can change the selected styles by setting the ItemsContainerStyle

WPF ItemsControl -> ContentPresenter

I hope someone can help me with my ItemsControl Problem :)
I use a ItemsControl with a Grid as an ItemsPanel to Add/Remove Column/RowDefinitions in MVVM (and also the Cell Content)
this works fine, but when I try to add a GridSplitter the splitter is shown but he doesnt work. The reason is the ItemsControl adds a new ContentPresenter for each item...
Is there any way to replace the ContentPresenter with the GridSplitter Control when the Type of an item in the children property is typeof SplitterVM ??
I'm also trying to bind the Splitters on the Grid as an DependencyProperty but when I use ItemsPanel it doesn't work because I cannot add Children to the Panel...
Here is my code:
<DataTemplate DataType="{x:Type vm:GridVM}">
<ItemsControl ItemsSource="{Binding Children}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<view:LayoutGridControl Rows="{Binding Rows}" Columns="{Binding Columns}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Model.RowIndex}"/>
<Setter Property="Grid.Column" Value="{Binding Model.ColumnIndex}"/>
<Setter Property="Grid.RowSpan" Value="{Binding Model.RowSpan}"/>
<Setter Property="Grid.ColumnSpan" Value="{Binding Model.ColumnSpan}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</DataTemplate>

How to use Canvas as the ItemsPanel for an ItemsControl in Silverlight 3

I am trying to set the Canvas properties in an ItemsControl DataTemplate with Silverlight 3. According to this post, the only way of doing that is to set it using the ItemsContainerStyle for the ContentPresenter type, since the Canvas properties only take effect on direct children of the Canvas. This doesn't seem to work in SL3, since the ItemsControl doesn't have an ItemsContainerStyle property, so I tried a ListBox as advised by this article, but it still doesn't work. From the XAML below, I would expect to see a green square, with the numbers 10, 30, 50, 70 cascading from "NW" to "SE" direction. Can anyone tell me why they are all stacked on top of eachother in the NW corner?
<UserControl x:Class="TestControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib" >
<StackPanel>
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Green" Width="100" Height="100" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding}" />
<Setter Property="Canvas.Top" Value="{Binding}" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.Items>
<System:Int32>10</System:Int32>
<System:Int32>30</System:Int32>
<System:Int32>50</System:Int32>
<System:Int32>70</System:Int32>
</ListBox.Items>
</ListBox>
</StackPanel>
</UserControl>
I'm not sure if it will work in your scenario, but I've accomplished this in the past using the RenderTransform.
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Green" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}">
<TextBox.RenderTransform>
<TranslateTransform X="100" Y="100" />
</TextBox.RenderTransform>
</TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Items>
<System:Int32>10</System:Int32>
<System:Int32>30</System:Int32>
<System:Int32>50</System:Int32>
<System:Int32>70</System:Int32>
</ItemsControl.Items>
</ItemsControl>
Or in the case of binding you will need to use a converter
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Green" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" RenderTransform="{Binding Converter={StaticResource NumberToTransformGroupConverter}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Items>
<System:Int32>10</System:Int32>
<System:Int32>30</System:Int32>
<System:Int32>50</System:Int32>
<System:Int32>70</System:Int32>
</ItemsControl.Items>
</ItemsControl>
Converter
public void ConvertTo(object value, ...)
{
int intValue = int.Parse(value.ToString());
return new TransformGroup()
{
Children = new TransformCollection()
{
new TranslateTransform { X = intValue, Y = intValue }
}
};
}
Silverlight4 does not bind to attached properties in style. I suggest using David Anson's approach described here.
<UserControl.Resources>
<Style x:Key="ScreenBindStyle" TargetType="ListBoxItem">
<Setter Property="Helpers:SetterValueBindingHelper.PropertyBinding">
<Setter.Value>
<Helpers:SetterValueBindingHelper>
<Helpers:SetterValueBindingHelper Type="Canvas" Property="Left" Binding="{Binding LocationField.Value.X}" />
<Helpers:SetterValueBindingHelper Type="Canvas" Property="Top" Binding="{Binding LocationField.Value.Y}" />
<Helpers:SetterValueBindingHelper Type="Canvas" Property="ZIndex" Binding="{Binding ZIndex.Value}" />
</Helpers:SetterValueBindingHelper>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
And in listbox:
ItemContainerStyle="{StaticResource ScreenBindStyle}"
Old post but I went into the same problem but now with SL5 that now allows Binding in style setters. I was trying to avoid to use the ListBox (because it handles selection and so on) and the ItemsControl still doesn't have an ItemContainerStyle. So I tried a few things.
I haven't found many subject discussing this problem so let me share my solution (sorry if it duplicates)
In fact, I found a very convenient way to solve the problem by adding an unnamed Style in the ItemsControl resources :
<ItemsControl ItemsSource="{Binding Path=MyData}">
<ItemsControl.Resources>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Top" Value="{Binding Path=Bounds.Top}"/>
<Setter Property="Canvas.Left" Value="{Binding Path=Bounds.Left}"/>
<Setter Property="Width" Value="{Binding Path=Bounds.Width}"/>
<Setter Property="Height" Value="{Binding Path=Bounds.Height}"/>
</Style>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="my:DataType">
...
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Works like a charm in SL5 :)
I can't explain what you are seeing either. Your Xaml is broken in at least a couple of ways.
First the Xaml itself fails because this:-
<Style TargetType="ContentPresenter">
should be
<Style TargetType="ContentControl">
the Item Containers in the case of ListBox are of type ListBoxItem which derive from ContentControl.
Still without that the placing {Binding} in style setters still doesn't work. I guess you were imagining that the style would be applied to each item in turn and get its Value from the current item. However even if binding worked in the style, there would only be one style and it would get its data binding from the ListBox DataContext. This is a different DataContext that applies to each ListBox item (which in this case is each Item in the Items collection).
Still I think Ben has a reasonable solution in place which eliminates this approach.

Resources