ItemsControl基本概念
用法1:设置奇偶行不同
xml
<ItemsControl AlternationCount="2" ItemsSource="{Binding Stars}">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<DockPanel>
<TextBlock DockPanel.Dock="Top" Text="Header" />
<ItemsPresenter />
</DockPanel>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border x:Name="border">
<TextBlock Margin="10" Text="{Binding}" />
</Border>
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter TargetName="border" Property="Background" Value="#ccc" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
csharp
/// <summary>
/// 一些字符串,用来展示基础的 ItemsSource 绑定,奇偶行交替颜色,以及表头
/// </summary>
public ObservableCollection<string> Stars { get; } =
new() { "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune" };
用法2:展示几何
csharp
/// <summary>
/// 用来展示替换 ItemsPanel 为 Canvas,以及修改 ItemContainerStyle
/// </summary>
public ObservableCollection<Shape> Shapes { get; } =
new()
{
new(0, new(50, 50), Brushes.Red),
new(1, new Point(150, 70), Brushes.Green),
new(1, new Point(300, 160), Brushes.Blue),
new(0, new Point(240, 260), Brushes.Yellow),
};
xml
<ItemsControl ItemsSource="{Binding Shapes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- 主要使用ItemContainerStyle来控制Canvas等附加属性,因为元素会直接放在ContentPresenter上 -->
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Pos.X}" />
<Setter Property="Canvas.Top" Value="{Binding Pos.Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle
Name="rect"
Width="40"
Height="40"
Fill="{Binding Color}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Type}" Value="1">
<Setter TargetName="rect" Property="RadiusX" Value="20" />
<Setter TargetName="rect" Property="RadiusY" Value="20" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
用法3:不同数据类型展示
定义数据
csharp
public abstract class Fruit
{
public int Amount { get; set; }
public string FruitType => this.GetType().Name;
}
public class Apple : Fruit { }
public class Banana : Fruit { }
/// <summary>
/// 用于展示在 Resources 中存放多个 DataType 不同的 DataTemplate 的效果
/// </summary>
public ObservableCollection<Fruit> Fruits { get; } =
new()
{
new Apple { Amount = 3 },
new Apple { Amount = 2 },
new Banana { Amount = 6 },
new Apple { Amount = 4 },
new Banana { Amount = 1 },
new Banana { Amount = 5 },
};
xml
<ItemsControl ItemsSource="{Binding Fruits}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:Apple}">
<Border Height="30" Background="Aquamarine">
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
<TextBlock Width="80" Text="{Binding FruitType}" />
</StackPanel>
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Banana}">
<Border Height="30" Background="LightPink">
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
<TextBlock Width="80" Text="{Binding FruitType}" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
用法4,相同类型不同属性使用不同模板
定义数据
csharp
public record Employee(string Name, Gender Gender, int Age);
public enum Gender
{
Male,
Female
};
/// <summary>
/// 用于展示 DataTemplateSelector 的使用
/// </summary>
public ObservableCollection<Employee> Employees { get; } =
new()
{
new("John", Gender.Male, 27),
new("Anna", Gender.Female, 31),
new("Joyce", Gender.Female, 28),
new("Tony", Gender.Male, 40),
new("Brian", Gender.Male, 36)
};
要根据Gender性别属性显示不同的背景色,要使用ItemTemplateSelector。
ItemTemplateSelector
定义ItemTemplateSelector
csharp
public class EmployeeTemplateSelector : DataTemplateSelector
{
public DataTemplate MaleTemplate { get; set; }
public DataTemplate FemaleTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var element = container as FrameworkElement;
var employee = item as Employee;
return employee!.Gender switch
{
Gender.Male => MaleTemplate,
Gender.Female => FemaleTemplate,
_ => throw new ArgumentException()
};
}
}
使用
xml
<ItemsControl FontSize="18" ItemsSource="{Binding Employees}">
<ItemsControl.ItemTemplateSelector>
<local:EmployeeTemplateSelector>
<local:EmployeeTemplateSelector.MaleTemplate>
<DataTemplate>
<StackPanel Background="LightCyan" Orientation="Horizontal">
<TextBlock Width="80" Text="{Binding Name}" />
<TextBlock Width="80" Text="{Binding Gender}" />
<TextBlock Text="{Binding Age}" />
</StackPanel>
</DataTemplate>
</local:EmployeeTemplateSelector.MaleTemplate>
<local:EmployeeTemplateSelector.FemaleTemplate>
<DataTemplate>
<StackPanel Background="LightPink" Orientation="Horizontal">
<TextBlock Width="80" Text="{Binding Name}" />
<TextBlock Width="80" Text="{Binding Gender}" />
</StackPanel>
</DataTemplate>
</local:EmployeeTemplateSelector.FemaleTemplate>
</local:EmployeeTemplateSelector>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
ItemsControl和ListBox的区别
和ListBox不同的是,ItemsControl默认不启用UI虚拟化,在呈现数据量较大的时候会导致严重的性能问题。因此如果数据量较大,最好为其开启虚拟化,具体需要做的是:
1:(必需)将其ItemsPanel设置为虚拟化容器,如VirtualizingStackPanel。
2:(必需)重写ControlTemplate,为其添加ScrollViewer并将其的CanContentScroll属性设置为True。注意,用ScrollViewer包裹ItemsControl是无效的。
3:(非必需,进一步优化)在ItemsControl上设置附加属性VirtualizingPanel.VirtualizationMode为Recycling,以此进一步减少内存分配和回收压力