Stretching the items in a WPF ListView within a ViewBox - wpf

I have a frustrating problem that I would much appreciate some help with. I have a ListView within a ViewBox and I can't get the items within the ListView to stretch horizontally.
Here is the XAML:
<Window x:Class="WpfApplication3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3"
Title="Window1">
<Window.Resources>
<local:Inning x:Key="inning">
<local:Inning.Skins>
<local:Skin SkinNumber="1"></local:Skin>
<local:Skin SkinNumber="2"></local:Skin>
<local:Skin SkinNumber="3"></local:Skin>
</local:Inning.Skins>
</local:Inning>
</Window.Resources>
<Grid DataContext="{StaticResource inning}">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Background="Black"
Text="SKINS"
Foreground="White"
FontSize="80"
FontWeight="Bold"
HorizontalAlignment="Center"></TextBlock>
<Grid Margin="2"
Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Viewbox>
<ListView Name="lvSkinNumbers"
ItemsSource="{Binding Skins}"
HorizontalAlignment="Stretch"
Background="Black"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="250"
VerticalAlignment="Stretch"
LineStackingStrategy="BlockLineHeight"
Margin="2"
TextAlignment="Center"
HorizontalAlignment="Stretch"
Background="Black"
Foreground="White"
Text="{Binding SkinNumber}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Viewbox>
<Viewbox Grid.Column="1">
<ListView Name="lvFirstInningSkins"
ItemsSource="{Binding Skins}"
Grid.Column="1"
HorizontalContentAlignment="Stretch"
Background="Black">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="250"
VerticalAlignment="Stretch"
LineStackingStrategy="BlockLineHeight"
Margin="2"
TextAlignment="Center"
HorizontalAlignment="Stretch"
Background="Green"
Foreground="White"
Text="{Binding SkinNumber}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Viewbox>
<Viewbox Grid.Column="2"
HorizontalAlignment="Stretch">
<ListView Name="lvSecondInningSkins"
ItemsSource="{Binding Skins}"
Grid.Column="2"
HorizontalAlignment="Stretch"
Background="Black">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="250"
VerticalAlignment="Stretch"
LineStackingStrategy="BlockLineHeight"
Margin="2"
TextAlignment="Center"
HorizontalAlignment="Stretch"
Background="Green"
Foreground="White"
Text="{Binding SkinNumber}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Viewbox>
</Grid>
</Grid>
</Window>
Here is the code behind with the definitions of the Skin and Inning objects:
using System;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfApplication3
{
public class Inning
{
private ObservableCollection<Skin> _skins = new ObservableCollection<Skin>();
public ObservableCollection<Skin> Skins
{
get { return _skins; }
set { _skins = value; }
}
}
public class Skin : INotifyPropertyChanged
{
public Skin()
{
}
public Skin( int skinNumber, Inning inning )
{
this.SkinNumber = skinNumber;
this.Inning = inning;
}
public Inning Inning { get; set; }
public int SkinNumber { get; set; }
public int SkinCount
{
get { return this.Inning.Skins.Count; }
}
public void Notify( string propertyName )
{
if ( PropertyChanged != null )
PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
}
The number of skins that can occur in an inning can change, and can be changed by the user, so I've put the ListViews into ViewBoxes so they automatically resize accordingly when the number of skins change. The resulting window can be seen here: http://i52.tinypic.com/244wqpl.jpg
I've tried all sorts of combinations of HorzontalAlignment="Stretch" and HorizontalContentAlignment="Stretch" and tried modifying the ItemsPanel template but I can't for the life of me seem to figure out how to get the ListView to stretch horizontally. Is what I'm trying to do impossible without some code behind to alter the width of the ListView dynamically? Or am I missing something really simple?
Any help that anyone can offer would be most appreciated.
Thanks,
Matthew

Try setting the ItemContainerStyle for your ListView to something like:
<ListView>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
You also might need to set <Viewbox Stretch="Fill"/>.
After this, I think you can remove all those other "HorizontalAlignment = Stretch" and "HorizontalContentAlignment = Stretch" setters in your code since it probably won't be necessary anymore.

Unexpectedly setting ScrollViewer.HorizontalScrollBarVisibility="Disabled" also worked for me:
<ListView ItemsSource="{Binding SourceList}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">

Related

ItemsControl fill remainder of columns

We have a page which we use to display a lot of information at once.
On this page we want a readouts section in the top left, and then for the remainder of that columns, and for the rest of the page we want to display some sensor outputs. Layout is roughly the following.
What I have attemtped so far is as follows
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<controls:ParameterDisplayPanel HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Columns="1"
Grid.Column="0"/>
<ItemsControl
Grid.Column="0"
Grid.ColumnSpan="3"
ItemsSource="{Binding ItemCollection, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<views:SensorControlView
DataContext="{Binding}"
Width="280"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
This returns a layout where the sensors overlap the readout panel.
If instead of using a grid I use a wrap panel, I get a row at the top which displays the readouts, and then all the sensor states start beneath that first row.
You could try sth. like this.
Note that this is not done yet. It's just a raw example.
Basically what I did is I added the SensorReadoutControl first and then added the remaining SensorItems to the same collection.
I then used a WrapPanel.
This is what it looks like (added some BackgroundColors to make more clear what control is where !)
So here is my example code:
XAML:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="SensorItemTemplate">
<Grid Background="Gray" Height="50">
<Border BorderThickness="1" BorderBrush="Pink"
Margin="5">
<TextBlock Text="{Binding Name}"
Width="200"
TextAlignment="Center"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</Border>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Items}"
Background="Green"
ItemTemplate="{StaticResource SensorItemTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical"></WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
ViewModel:
public class ViewModel
{
public ViewModel()
{
Grid grid = new Grid() { Width = 200, Height = 150, Background = Brushes.Blue };
grid.Children.Add(new TextBlock()
{
Text = "Sensor readouts" ,
VerticalAlignment = System.Windows.VerticalAlignment.Center,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center
});
Items.Add(grid);
for (int i = 0; i < 50; i ++)
{
Items.Add(new SensorItem() { Name = $"SensorItem {i}" });
}
}
public List<object> Items { get; set; } = new List<object>();
}
public class SensorItem
{
public string ItemTypeName { get; set; } = "SensorItem";
public string Name { get; set; }
}
I guess that won't satisfy your needs but it may help you to progress.

How to use WrapPanel's DataTemplate

I've a WrapPanel to show some elements. But I want to use DataTemplate to show them.
Here is my XAML code of WrapPanel
<WrapPanel Margin="10,57,12,10" x:Name="wrp1">
<WrapPanel.Resources>
<DataTemplate DataType="{x:Type local:DateItem}">
<Grid VerticalAlignment="Top" HorizontalAlignment="Stretch" Width="250" Height="300" Background="Blue">
<Label Content="{Binding Path=DateString}" FontSize="20" Cursor="Hand" Foreground="White" Background="Red" FontWeight="Bold" VerticalAlignment="Bottom" HorizontalAlignment="Left" Height="38" VerticalContentAlignment="Center" Padding="5,0,5,0"/>
</Grid>
</DataTemplate>
</WrapPanel.Resources>
</WrapPanel>
And this is the code of DateItem
public class DateItem : UIElement
{
public string DateString { get; set; }
}
When the window initialized, i'm creating one DateItem with DateString parameter and adding that to WrapPanel as child.
DateItem di = new DateItem();
di.DateString = "28.04.2014";
wrp1.Children.Add(di);
I think everything is fine but wrap panel shows nothing :(
Can you help me with this?
You have confused UI controls with DataTemplates which is used to define the presentation of your data. To render data, you have to set content of control which can be done using ContentControl.
Also, you can use ItemsControl if you want to add multiple times.
XAML:
<WrapPanel x:Name="wrp1">
<WrapPanel.Resources>
<DataTemplate DataType="{x:Type local:DateItem}">
<Grid VerticalAlignment="Top" HorizontalAlignment="Stretch" Width="250"
Height="300" Background="Blue">
<Label Content="{Binding Path=DateString}" FontSize="20" Cursor="Hand"
Foreground="White" Background="Red" FontWeight="Bold"
VerticalAlignment="Bottom" HorizontalAlignment="Left"
Height="38" VerticalContentAlignment="Center"
Padding="5,0,5,0"/>
</Grid>
</DataTemplate>
</WrapPanel.Resources>
<ItemsControl x:Name="itemsControl"/>
</WrapPanel>
Code behind:
DateItem di = new DateItem();
di.DateString = "28.04.2014";
itemsControl.Items.Add(di);
DateItem:
public class DateItem
{
public string DateString { get; set; }
}
In case you still interested in rendering it as a Control, you have to define default Style and not default template.
XAML:
<WrapPanel x:Name="wrp1">
<WrapPanel.Resources>
<Style TargetType="{x:Type local:DateItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid VerticalAlignment="Top" HorizontalAlignment="Stretch"
Width="250" Height="300" Background="Blue">
<Label
Content="{Binding Path=DateString, RelativeSource=
{RelativeSource Mode=TemplatedParent}}"
FontSize="20" Cursor="Hand" Foreground="White"
Background="Red" FontWeight="Bold"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
Height="38" VerticalContentAlignment="Center"
Padding="5,0,5,0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</WrapPanel.Resources>
</WrapPanel>
Code behind:
DateItem di = new DateItem();
di.DateString = "28.04.2014";
wrp1.Children.Add(di);
DateItem:
public class DateItem : Control
{
public string DateString { get; set; }
}

Silverlight Listbox scrolling only works if i set the height

i have a listbox and i am trying to make the scrolling work without having to set the height, is this possible? Thanks. Below is the code. The scrolling does not work.
<ListBox Name="EmployeeListBox" Background="Transparent"
SelectionMode="Single"
ItemsSource="{Binding Employees, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate >
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="140" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" FontWeight="Bold" Text="Name:" />
<TextBlock x:Name="TextBlock1" Grid.Column="1" Grid.Row="0"
Text="{Binding Name}" />
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and viewmodel:
public class EmployeeDataContext
{
public List<Employee> Employees { get; set; }
public EmployeeDataContext()
{
GetEmployeeList();
}
private void GetEmployeeList()
{
Employees = new List<Employee>();
for (int i = 0; i < 100; ++i)
{
Employees.Add(new Employee() { Name = "Gema Arterton" });
}
}
}
public class Employee
{
public string Name { get; set; }
}
(sorry for my bad english)
It depends too on what control is the listbox inside. If your listbox is inside a Grid, it should take all the space of that grid - if it is inside a stackpanel it will use the least amount os space possible so you will have to use fixed height/width. Try this:
<UserControl...>
<Grid>
<ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</ListBox>
</Grid>
</UserControl/>
The listbox should use all screen size and show vertical scrollbars if there is enough items.
You can set the height in scrollviewer
<ScrollViewer VerticalScrollBarVisibility="Visible" Height="480">
<Grid Margin="0,0,0,50">
</Grid>
</ScrollViewer>

Add Custom control to stackpanel using Viewmodel WPF

I have a customcontrol, need to be added as many times when clicing on a button. This has to achived from MVVM WPF pattern. i have pasted my code here. It will be great if you guys can help on this.
Please help me
<Window x:Class="DOCS_APP_ELEMENT.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:usercontrol="clr-namespace:DOCS_APP_ELEMENT"
xmlns:viewModel="clr-namespace:DOCS_APP_ELEMENT.ViewModels"
Title="MainWindow" Height="350" Width="400">
<Grid Margin="10" Name="myGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Border CornerRadius="5" BorderBrush="SteelBlue" BorderThickness="2" Grid.Row="0">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label Content="Type:" Margin="20,0,4,0"></Label>
<ComboBox Name="cmbQuestionType" Width="300" Style="{Binding ComboBoxStyle}" Margin="0,5,0,5" IsEnabled="False"> </ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5,0,5">
<Label Content="Question:" Margin="0,0,4,0"></Label>
<TextBox Name="txtQuestion" Width="300" Height="50" Margin="0,2,0,0" AcceptsReturn="True"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5,0,5" >
<Label Content="Answer:" Margin="7,0,4,0"></Label>
<TextBox Name="txtAnswer" Style="{StaticResource TextboxStyle}" Margin="0,2,0,0"></TextBox>
</StackPanel>
</StackPanel>
</Border>
<Border CornerRadius="5" BorderBrush="SteelBlue" BorderThickness="2" Grid.Row="1" Margin="0,10,0,0" >
<ScrollViewer VerticalScrollBarVisibility="Auto" Height="100">
<StackPanel Name="myCustom" Orientation="Vertical" >
**<!--<ADD CUSTOM CONTROl HERE>-->**
</StackPanel>
</ScrollViewer>
</Border>
<Border CornerRadius="5" BorderBrush="SteelBlue" BorderThickness="2" Grid.Row="2" Margin="0,10,0,0">
<Border.DataContext>
<viewModel:ViewElements/>
</Border.DataContext>
<Button Name="btnAdd" Content="Add" DataContext="{Binding }" Command="{Binding Path=AddInstace}"></Button>
</Border>
</Grid>
I'd do it the following way:
have a ObservableCollection<CustomClass> in your ViewModel. The representation of your CustomClass is a DataTemplate with your above Markup.
Here's a full working example:
<Grid>
<Grid.DataContext>
<local:MyViewModel></local:MyViewModel>
</Grid.DataContext>
<StackPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto" Height="200">
<ItemsControl ItemsSource="{Binding CustomControls}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="Green">
<StackPanel>
<TextBlock Text="I am a Custom Control"></TextBlock>
<TextBlock Text="{Binding DisplayValue}"></TextBlock>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
<Button Width="200" Height="50" Command="{Binding AddControlCommand}">Add Control</Button>
<Button Width="200" Height="50" Command="{Binding RemoveControlCommand}">Remove Control</Button>
</StackPanel>
</Grid>
ViewModel:
public abstract class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class RelayCommand : ICommand
{
... look that up yourself if you don't have a derived command class yet in your project...
}
public class MyViewModel : ViewModel
{
public ICommand AddControlCommand
{
get
{
return new RelayCommand(x=>this.AddControl());
}
}
public ICommand RemoveControlCommand
{
get
{
return new RelayCommand(x => this.RemoveControl());
}
}
private void AddControl()
{
CustomControls.Add(new CustomControl() {DisplayValue = "newControl"});
}
private void RemoveControl()
{
if (CustomControls.Count > 0)
{
CustomControls.Remove(CustomControls.Last());
}
}
private ObservableCollection<CustomControl> _customControls;
public ObservableCollection<CustomControl> CustomControls
{
get
{
if (_customControls == null)
{
_customControls = new ObservableCollection<CustomControl>()
{
new CustomControl() {DisplayValue = "Control1"},
new CustomControl() {DisplayValue = "Control2"},
new CustomControl() {DisplayValue = "Control3"}
};
}
return _customControls;
}
}
}
public class CustomControl : ViewModel
{
public string DisplayValue { get; set; }
}
To use the MVVM pattern you'll need a ViewModel that has a list of data objects which are bound to your custom controls. This controls can be generated by an ItemsControl. As I don't know your data I can just give you a general example.
MainWindow.xaml (View)
<ItemsControl ItemsSource="{Binding DataList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- instead of the TextBlock you would use your control -->
<TextBlock Text="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
MainWindow.xaml.cs (View code-behind)
public MainWindow()
{
DataContext = new MainWindowViewModel();
InitializeComponent();
}
MainWindowViewModel.cs (ViewModel)
public class MainWindowViewModel
{
public ObservableCollection<string> DataList { get; set; }
public MainWindowViewModel()
{
DataList = new ObservableCollection<string>
{
"Data 1",
"Data 2",
"Data 3"
};
}
}
The Binding for the Text property has no path as the DataContext here is the string object of DataList. If you use complex objects, you have to use the Path to the property of the object (e.g. Text={Binding Path=myProperty})

Removing left and right border sides from listbox datatemplate

Currently I have specified borders for all datatemplated items in my horizontal listbox which is fine because I DO want borders for all individual listboxitems, but I would like to remove the left border from the first item and the right border from the last item. Is this even possible?
Xaml:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Background="DimGray">
<Border BorderBrush="White" BorderThickness="1">
<Canvas Height="80" Width="140">
<TextBlock Text="{Binding Name}" TextAlignment="Center" Canvas.Top="22" Height="80" Width="140" FontSize="26"></TextBlock>
</Canvas>
</Border>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Thanks
It is, with a DataTemplateSelector, you just need to implement the logic to select the correct DataTemplate, here's a fulle example with your code. You should be able to just copy / paste the following code in your project. There are comments in there that explain what's going on. Hope it helps!
XAML:
<Window x:Class="StackOverflowTests.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:StackOverflowTests"
Title="Window1"
x:Name="window1"
Width="800"
Height="600">
<Window.Resources>
<!-- Instantiate the DataTemplateSelector -->
<local:ItemDataTemplateSelector x:Key="ItemDataTemplateSelector" />
</Window.Resources>
<!-- Assign the DataTemplateSelector to the ListBox's ItemTemplateSelector -->
<ListBox Margin="8" ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource ItemDataTemplateSelector}">
<ListBox.Resources>
<!-- Template without Left border -->
<DataTemplate x:Key="firstItemTemplate">
<StackPanel Orientation="Vertical" Background="DimGray">
<Border BorderBrush="Red" BorderThickness="0,1,1,1">
<Canvas Height="80" Width="140">
<TextBlock Text="{Binding Name}" TextAlignment="Center" Canvas.Top="22" Height="80" Width="140" FontSize="26"></TextBlock>
</Canvas>
</Border>
</StackPanel>
</DataTemplate>
<!-- Template with all borders -->
<DataTemplate x:Key="regularItemTemplate">
<StackPanel Orientation="Vertical" Background="DimGray">
<Border BorderBrush="Red" BorderThickness="1,1,1,1">
<Canvas Height="80" Width="140">
<TextBlock Text="{Binding Name}" TextAlignment="Center" Canvas.Top="22" Height="80" Width="140" FontSize="26"></TextBlock>
</Canvas>
</Border>
</StackPanel>
</DataTemplate>
<!-- Template without the Right border -->
<DataTemplate x:Key="lastItemTemplate">
<StackPanel Orientation="Vertical" Background="DimGray">
<Border BorderBrush="Red" BorderThickness="1,1,0,1">
<Canvas Height="80" Width="140">
<TextBlock Text="{Binding Name}" TextAlignment="Center" Canvas.Top="22" Height="80" Width="140" FontSize="26"></TextBlock>
</Canvas>
</Border>
</StackPanel>
</DataTemplate>
</ListBox.Resources>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Window>
C#:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace StackOverflowTests
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
this.DataContext = new List<Person>()
{
new Person() { Name = "Jim Morrison" },
new Person() { Name = "Ozzy Osbourne" },
new Person() { Name = "Slash" },
new Person() { Name = "Jimmy Page" }
};
}
}
public class Person
{
public string Name { get; set; }
}
public class ItemDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
// get the ListBoxItem
ListBoxItem listBoxItem = element.TemplatedParent as ListBoxItem;
// get the ListBoxItem's owner ListBox
ListBox listBox = ItemsControl.ItemsControlFromItemContainer(listBoxItem) as ListBox;
// get the index of the item in the ListBox
int index = listBox.Items.IndexOf(item);
// based on the index select the template
if (index == 0)
return element.FindResource("firstItemTemplate") as DataTemplate;
else if (index == listBox.Items.Count - 1)
return element.FindResource("lastItemTemplate") as DataTemplate;
else
return element.FindResource("regularItemTemplate") as DataTemplate;
}
}
}

Resources