ItemsPanelTemplate Selector in wpf? - c#

I need to set the ItemsPanelTemplate property of a listbox based on a dependency property on the control. How do I use the DataTemplateSelector to do that?
I have something like:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<!-- Here I need to replace with either a StackPanel or a wrap panel-->
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
Thanks

There isn't an ItemsPanelSelector (probably because it isn't a DataTemplate) but you can bind it or use a Trigger
Binding example
<ListBox ItemsPanel="{Binding RelativeSource={RelativeSource Self},
Path=Background,
Converter={StaticResource MyItemsPanelConverter}}">
Trigger in Style example
<ListBox ItemsSource="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
<ListBox.Style>
<Style TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<!-- Your Trigger.. -->
<Trigger Property="Background" Value="Green">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>

I'm thinking the best route here would be to use a Style for your ListBox and set Triggers that change the ItemsPanel based on the DependencyProperty you reference.

Related

Bind Style-Resource to StackPanel items

I am trying to bind a a Style-Resource which is defined in the local ResourceDictionary of a UserControl to all items in a StackPanel.
The ItemSource of the StackPanel is bound to an ObservableCollection<Button> in the ViewModel.
Thus, the aim is to bind the Style-Resource to the Style-Property of these Buttons.
The following simplified approach results in this error:
ArgumentException: Style object is not allowed to affect the Style property of the object to which it applies.
MyViewModel.cs:
public class MyViewModel
{
public ObservableCollection<Button> MyButtons { get; private set; }
}
MyView.xaml
<UserControl x:Class="MyView"
d:DataContext="{d:DesignInstance Type=MyViewModel}">
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="StyleStackPanelButton" TargetType="{x:Type Button}"
BasedOn="{StaticResource MyDefaultStyle}">
<Setter Property="Margin" Value="15" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel>
<ItemsControl ItemsSource="{Binding MyButtons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Style" Value="{StaticResource StyleStackPanelButton}" />
</Style>
</StackPanel.Resources>
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Button Style="{StaticResource StyleStackPanelButton}" />
</StackPanel>
</UserControl>
I've also tried to use a Converter as suggested here
Binding for WPF Styles
as follows:
...
<ItemsControl ItemsSource="{Binding ButtonExtensions}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Style">
<Setter.Value>
<MultiBinding Converter="{StaticResource StyleConverter}">
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource Self}" />
<Binding Source="{StaticResource StyleStackPanelButton}" />
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
...
This results in the same error:
ArgumentException: Style object is not allowed to affect the Style property of the object to which it applies.
Is there a any other way to bind a lcoal Style-Resource to a Setter-Property of an Item in a Stackpanel-ItemSource?
Since you are adding Buttons directly to the ItemsSource collection, it is sufficient to assign the Button Style to the ItemContainerStyle property of the ItemsControl:
<ItemsControl ItemsSource="{Binding MyButtons}"
ItemContainerStyle="{StaticResource StyleStackPanelButton}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Alternatively, declare a default Button Style - without x:Key - in the StackPanel Resources:
<ItemsControl ItemsSource="{Binding MyButtons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="Button"
BasedOn="{StaticResource StyleStackPanelButton}"/>
</StackPanel.Resources>
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

Canvas Children Property Binding

I'm learning wpf and how to properly bind. This is the code I am working on:
<ItemsControl ItemsSource="{Binding CanvasChildren}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type espace:Entity}" />
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
<Setter Property="Canvas.ZIndex" Value="{Binding Z}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
So you can see I'm binding CanvasChildren in this ItemsControl. CanvasChildren is an ObservableCollection of type "Entity". Entity is my own class and it's another canvas object that will have images and such in it.
Entity has properties X,Y,Z in it and I WANT to be able to bind those properties to the Canvas.Left,Canvas.Top,Canvas.ZIndex but I am TOTALLY lost on how to do that. The style setters I have defined here do NOT WORK.
For one the binding values are checking for X,Y,Z coordinates in my viewmodel which is defined:
<base:SceneBase.DataContext>
<sceneGame:SceneGameViewModel />
</base:SceneBase.DataContext>
But changing the setters to something like:
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type espace:Entity}}, Path=X}" />
<Setter Property="Canvas.Top" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type espace:Entity}}, Path=Y}" />
<Setter Property="Canvas.ZIndex" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type espace:Entity}}, Path=Z}" />
</Style>
</ItemsControl.ItemContainerStyle>
does not work either.
It may possibly be something simple that I'm overlooking but I'm still learning WPF and I'm lost.
If you use an ItemsControl with an item type that is a UIElement (which is a bit unusual), the control will not create an additional item container - i.e. a ContentPresenter - element for it, but instead apply the ItemContainerStyle directly to the item. You can verify this by setting TargetType="espace:Entity" on the Style.
In this case, the ItemsControl does also not set the DataContext of the UIElement item, which means that Bindings without an explictly set source won't work. The Bindings in the ItemContainerStyle would use the item object directly as its source, i.e. use RelativeSource Self.
It is also useless to declare a DataTemplate for the item type (especially an empty one), because it would be ignored. The item is not considered to be "data", but UI.
<ItemsControl ItemsSource="{Binding CanvasChildren}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="espace:Entity">
<Setter
Property="Canvas.Left"
Value="{Binding X, RelativeSource={RelativeSource Self}}"/>
<Setter
Property="Canvas.Top"
Value="{Binding Y, RelativeSource={RelativeSource Self}}"/>
<Setter
Property="Panel.ZIndex"
Value="{Binding Z, RelativeSource={RelativeSource Self}}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>

Unable to bind item property in itemcontainerstyle

I created a .NET WPF Application with MvvmLight this evening. I'm trying to show a couple of circles on my main window.
My MainWindow.xaml is bound to MainViewModel.cs. MainViewModel contains an ObservableCollection with SpaceObjects. I'm trying to show this collection using itemcontrols.
<ItemsControl ItemsSource="{Binding SpaceObjects}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding }"></Setter>
<Setter Property="Canvas.Top" Value="{Binding Radius}"></Setter>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Width="{Binding Radius}" Height="{Binding Radius}" Fill="Blue" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In the ItemTemplate I am able to bind to a SpaceObject property (like Radius). In ItemContainerStyle I can only choose from the context:
I would like to choose from the items in the source (ItemsScourse SpaceObjects), so I can bind the X and Y coordinates from the circle. What am I doing wrong?
<DataGrid.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<d:Style.DataContext>
<x:Type Type="SpaceObject" />
</d:Style.DataContext>
<Setter Property="Canvas.Left" Value="{Binding }"/>
<Setter Property="Canvas.Top" Value="{Binding Radius}"/>
</Style>
</DataGrid.ItemContainerStyle>

How to set style for ItemsPanel from outside?

I define a style to make all StackPanel green:
<Window.Resources>
<Style TargetType="StackPanel">
<Setter Property="Background" Value="Green" />
</Style>
</Window.Resources>
But if I use StackPanel as panel template then it's NOT green:
<UniformGrid>
<StackPanel /><!-- this one is green -->
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel /><!-- this one is not -->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</UniformGrid>
Why? How to make it also green?
Either move the implicit Style to App.xaml or add resource that is based on the implicit Style to the ItemsPanelTemplate:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<ItemsPanelTemplate.Resources>
<Style TargetType="StackPanel" BasedOn="{StaticResource {x:Type StackPanel}}" />
</ItemsPanelTemplate.Resources>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Types that don't inherit from Control won't pick up implicit styles if you don't do any of this.

How to apply style to a custom control

I have a custom control inheriting from ListBox.
I have a style for it targeting my custom control.
For some reason this style is not working on my custom control.
Could you please tell me what I am missing here?
Code for style:
<Style x:Key="ListBoxStyle" TargetType="local:CustomListBox">
<Setter Property="Background" Value="Red"/>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
Code for custom control:
public class CustomListBox : ListBox
{
public CustomListBox()
{
this.DefaultStyleKey = typeof(CustomListBox).Name;
}
}
Usage of custom control:
<local:CustomListBox>
<ListBoxItem Content="AAA"></ListBoxItem>
<ListBoxItem Content="BBB"></ListBoxItem>
<ListBoxItem Content="CCC"></ListBoxItem>
</local:CustomListBox>
Any help?
Thank you!
In Windows Phone 8.1 when you create a Templated Control (Custom control) a file is automatically created called Generic.xaml under Themes folder.
Your style should be added in Generic.xaml like this.
<Style TargetType="local:CustomListBox">
<Setter Property="Background" Value="Red"/>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
Change the following line to
this.DefaultStyleKey = typeof(CustomListBox);

Categories