文章目录
可以根据Github拉取示例程序运行
GitHub程序演示地址(点击直达)
也可以在本文资源中下载
一、ListBox控件概述
ListBox是Windows Presentation Foundation (WPF)中最常用的列表展示控件之一,它允许用户从一个项目列表中进行单项或多项选择。与传统Windows Forms中的ListBox相比,WPF的ListBox具有更强大的功能和更高的可定制性。
ListBox继承自ItemsControl,因此它继承了ItemsControl的所有功能,同时增加了选择功能。这使得它非常适合于显示数据集合并允许用户与之交互。
二、基本用法
2.1 基本XAML创建
最简单的ListBox创建方式如下:
<ListBox Width="200" Height="150">
<ListBoxItem>项目1</ListBoxItem>
<ListBoxItem>项目2</ListBoxItem>
<ListBoxItem>项目3</ListBoxItem>
<ListBoxItem>项目4</ListBoxItem>
<ListBoxItem>项目5</ListBoxItem>
</ListBox>
2.2 代码创建
通过C#代码创建ListBox也同样简单:
// 创建ListBox控件
ListBox myListBox = new ListBox();
myListBox.Width = 200;
myListBox.Height = 150;
// 添加项目
myListBox.Items.Add("项目1");
myListBox.Items.Add("项目2");
myListBox.Items.Add("项目3");
myListBox.Items.Add("项目4");
myListBox.Items.Add("项目5");
// 将ListBox添加到窗口的网格中
myGrid.Children.Add(myListBox);
三、核心属性和功能
3.1 项容器与项目源
ListBox有两种提供数据的主要方式:
3.1.1 Items属性
Items属性允许你直接向ListBox添加项目。每个项目会自动包装在一个ListBoxItem容器中:
<ListBox>
<ListBox.Items>
<ListBoxItem>红色</ListBoxItem>
<ListBoxItem>绿色</ListBoxItem>
<ListBoxItem>蓝色</ListBoxItem>
</ListBox.Items>
</ListBox>
3.1.2 ItemsSource属性
ItemsSource属性允许你通过数据绑定的方式提供数据源,这是WPF中推荐的做法:
<ListBox ItemsSource="{Binding ColorList}" />
对应的C#代码:
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> _colorList;
public ObservableCollection<string> ColorList
{
get { return _colorList; }
set
{
_colorList = value;
OnPropertyChanged("ColorList");
}
}
public ViewModel()
{
// 初始化颜色列表
_colorList = new ObservableCollection<string>
{
"红色", "绿色", "蓝色", "黄色", "紫色"
};
}
// INotifyPropertyChanged实现代码省略...
}
3.2 选择模式
ListBox支持三种选择模式,通过SelectionMode属性进行设置:
<ListBox SelectionMode="Multiple" />
选择模式 | 描述 |
---|---|
Single | 只允许选择单个项目(默认设置) |
Multiple | 允许选择多个项目,需要使用Ctrl键进行复选 |
Extended | 允许选择多个项目,可以使用Shift键进行范围选择,Ctrl键进行非连续选择 |
相关代码示例:
// 设置选择模式为多选
myListBox.SelectionMode = SelectionMode.Multiple;
// 获取选中的项目
ListBoxItem selectedItem = myListBox.SelectedItem as ListBoxItem;
// 或者获取索引
int selectedIndex = myListBox.SelectedIndex;
// 获取多选的项目
var selectedItems = myListBox.SelectedItems;
foreach (var item in selectedItems)
{
Console.WriteLine(item.ToString());
}
3.3 选择相关属性
ListBox提供了多个与选择相关的重要属性:
- SelectedItem:获取或设置当前选中的项目。
- SelectedItems:获取当前选中的所有项目(只读)。
- SelectedIndex:获取或设置当前选中项目的索引。
- SelectedValue:获取或设置选中项目的值(与SelectedValuePath配合使用)。
- SelectedValuePath:指定对象的哪个属性用于SelectedValue。
// 设置选中索引
myListBox.SelectedIndex = 2;
// 通过代码获取选中的内容和值
var item = myListBox.SelectedItem;
var index = myListBox.SelectedIndex;
var value = myListBox.SelectedValue; // 需要配合SelectedValuePath使用
四、样式和模板定制
4.1 ItemTemplate
ItemTemplate允许你自定义列表中每个项的外观:
<ListBox ItemsSource="{Binding PersonList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImagePath}" Width="32" Height="32" Margin="3"/>
<StackPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold"/>
<TextBlock Text="{Binding Email}" FontStyle="Italic" Foreground="Gray"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
上述代码使用DataTemplate定义了一个包含图片和两个文本块的布局。
4.2 ItemContainerStyle
ItemContainerStyle允许你自定义列表项容器的样式(即ListBoxItem):
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Padding" Value="5" />
<Setter Property="Margin" Value="0,2" />
<Setter Property="Background" Value="LightBlue" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="LightCoral" />
<Setter Property="Foreground" Value="White" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightGreen" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
此代码定义了列表项的基本样式,以及选中状态和鼠标悬停状态的视觉反馈。
4.3 交替背景色
通过ItemContainerStyle可以轻松实现奇偶行的交替背景色:
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="WhiteSmoke"/>
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="Lavender"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
注意:需要设置ListBox的AlternationCount属性指定交替周期:
<ListBox AlternationCount="2" ItemsSource="{Binding Items}" />
五、项目绑定
5.1 基本数据绑定
以下是一个绑定简单集合的例子:
// ViewModel代码
public class MainViewModel
{
public ObservableCollection<string> Fruits { get; } = new ObservableCollection<string>
{
"苹果", "香蕉", "橘子", "葡萄", "西瓜"
};
}
// 设置DataContext
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
对应的XAML:
<ListBox ItemsSource="{Binding Fruits}" />
5.2 复杂对象绑定
当绑定复杂对象时,需要使用ItemTemplate指定如何显示:
// 定义数据模型
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
// ViewModel代码
public class ShopViewModel
{
public ObservableCollection<Product> Products { get; } = new ObservableCollection<Product>
{
new Product { Id = 1, Name = "笔记本电脑", Price = 5999, Category = "电子产品" },
new Product { Id = 2, Name = "机械键盘", Price = 299, Category = "配件" },
new Product { Id = 3, Name = "无线鼠标", Price = 129, Category = "配件" },
new Product { Id = 4, Name = "显示器", Price = 1299, Category = "电子产品" }
};
}
XAML代码:
<ListBox ItemsSource="{Binding Products}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Id}" />
<TextBlock Grid.Column="1" Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Grid.Column="2" Text="{Binding Price, StringFormat={}{0:C}}" HorizontalAlignment="Right" />
<TextBlock Grid.Column="3" Text="{Binding Category}" Foreground="Gray" Margin="10,0,0,0" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
5.3 DisplayMemberPath
对于简单的显示需求,可以使用DisplayMemberPath属性指定要显示的属性名:
<ListBox ItemsSource="{Binding Products}" DisplayMemberPath="Name" />
这样只会显示Product对象的Name属性值。
六、事件处理
6.1 选择事件
ListBox提供了几个与选择相关的重要事件:
// 在XAML中订阅事件
<ListBox ItemsSource="{Binding Items}"
SelectionChanged="ListBox_SelectionChanged" />
// C#代码中的事件处理
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// 获取新选中的项目
var addedItems = e.AddedItems;
foreach (var item in addedItems)
{
Debug.WriteLine($"新选中的项目: {item}");
}
// 获取取消选中的项目
var removedItems = e.RemovedItems;
foreach (var item in removedItems)
{
Debug.WriteLine($"取消选中的项目: {item}");
}
// 获取当前选中的项目
ListBox listBox = sender as ListBox;
var selectedItem = listBox.SelectedItem;
var selectedItems = listBox.SelectedItems;
}
6.2 项目交互事件
除了选择事件外,还有一些项目级别的交互事件,需要在ItemContainerStyle中处理:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<EventSetter Event="MouseDoubleClick" Handler="ListBoxItem_MouseDoubleClick"/>
<EventSetter Event="PreviewMouseRightButtonDown" Handler="ListBoxItem_PreviewMouseRightButtonDown"/>
</Style>
</ListBox.ItemContainerStyle>
对应的C#事件处理代码:
private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
ListBoxItem item = sender as ListBoxItem;
var data = item.Content; // 获取项目中的数据
MessageBox.Show($"双击了项目: {data}");
}
private void ListBoxItem_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
ListBoxItem item = sender as ListBoxItem;
// 可以在这里显示上下文菜单
ContextMenu contextMenu = new ContextMenu();
contextMenu.Items.Add(new MenuItem { Header = "编辑" });
contextMenu.Items.Add(new MenuItem { Header = "删除" });
item.ContextMenu = contextMenu;
}
七、分组与排序
7.1 使用CollectionView进行数据分组
WPF提供了强大的CollectionView来处理数据分组和排序:
// ViewModel代码
public class GroupingViewModel
{
public ObservableCollection<Product> AllProducts { get; } = new ObservableCollection<Product>
{
new Product { Id = 1, Name = "笔记本电脑", Price = 5999, Category = "电子产品" },
new Product { Id = 2, Name = "机械键盘", Price = 299, Category = "配件" },
new Product { Id = 3, Name = "无线鼠标", Price = 129, Category = "配件" },
new Product { Id = 4, Name = "显示器", Price = 1299, Category = "电子产品" },
new Product { Id = 5, Name = "手机", Price = 2999, Category = "电子产品" },
new Product { Id = 6, Name = "耳机", Price = 499, Category = "配件" }
};
// 将数据按类别分组
public ICollectionView ProductsCollectionView
{
get
{
var view = CollectionViewSource.GetDefaultView(AllProducts);
// 清除现有分组描述
view.GroupDescriptions.Clear();
// 添加按Category分组的描述
view.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
return view;
}
}
}
在XAML中绑定分组的CollectionView:
<ListBox ItemsSource="{Binding ProductsCollectionView}">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="14" Margin="5"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5,2">
<TextBlock Text="{Binding Name}" Width="150"/>
<TextBlock Text="{Binding Price, StringFormat={}{0:C}}" Width="80" HorizontalAlignment="Right"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
7.2 数据排序
使用CollectionView可以轻松实现数据排序:
public ICollectionView SortedProductsView
{
get
{
var view = CollectionViewSource.GetDefaultView(AllProducts);
// 清除现有排序描述
view.SortDescriptions.Clear();
// 添加按价格升序排序的描述
view.SortDescriptions.Add(new SortDescription("Price", ListSortDirection.Ascending));
return view;
}
}
7.3 分组与排序结合
可以同时应用分组和排序:
public ICollectionView GroupedAndSortedView
{
get
{
var view = CollectionViewSource.GetDefaultView(AllProducts);
// 清除现有分组和排序描述
view.GroupDescriptions.Clear();
view.SortDescriptions.Clear();
// 添加分组描述
view.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
// 添加排序描述: 先按类别排序,再按名称排序
view.SortDescriptions.Add(new SortDescription("Category", ListSortDirection.Ascending));
view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
return view;
}
}
八、虚拟化与性能优化
8.1 UI虚拟化
当ListBox包含大量项目时,UI虚拟化是提高性能的关键。WPF默认开启了UI虚拟化,但可以通过VirtualizingPanel附加属性进行控制:
<ListBox ItemsSource="{Binding LargeCollection}"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
VirtualizingPanel.CacheLength="5,5"
VirtualizingPanel.CacheLengthUnit="Item" />
参数说明:
- IsVirtualizing: 启用/禁用虚拟化
- VirtualizationMode:
- Standard: 标准虚拟化,移出视图的项目会被销毁
- Recycling: 回收模式,移出视图的项目会被回收利用,性能更好
- CacheLength: 指定在可见区域之前和之后缓存多少项目
- CacheLengthUnit: 缓存单位(Item或Page)
8.2 大数据集优化
处理大型数据集时的一些建议:
// 处理大型数据集时,可以实现延迟加载
public class LazyLoadViewModel : INotifyPropertyChanged
{
private ObservableCollection<DataItem> _items = new ObservableCollection<DataItem>();
private CancellationTokenSource _cancellationTokenSource;
public ObservableCollection<DataItem> Items => _items;
public async Task LoadDataAsync(int startIndex, int count)
{
// 取消之前的加载操作
_cancellationTokenSource?.Cancel();
_cancellationTokenSource = new CancellationTokenSource();
var token = _cancellationTokenSource.Token;
try
{
// 模拟从数据库或API获取数据
await Task.Delay(200, token); // 模拟网络延迟
// 批量加载数据
for (int i = startIndex; i < startIndex + count; i++)
{
if (token.IsCancellationRequested)
break;
var item = new DataItem { Id = i, Name = $"项目 {i}" };
// 更新UI
await Application.Current.Dispatcher.InvokeAsync(() =>
{
_items.Add(item);
});
// 每批次添加后稍微暂停,避免UI冻结
if (i % 20 == 0)
await Task.Delay(10, token);
}
}
catch (TaskCanceledException)
{
// 处理取消操作
}
}
// INotifyPropertyChanged实现省略...
}
在滚动事件中实现分页加载:
private void ListBox_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
ListBox listBox = sender as ListBox;
ScrollViewer scrollViewer = FindVisualChild<ScrollViewer>(listBox);
// 当滚动到接近底部时加载更多数据
if (scrollViewer != null &&
scrollViewer.VerticalOffset > scrollViewer.ScrollableHeight * 0.8 &&
!_isLoading)
{
_isLoading = true;
LoadMoreDataAsync().ContinueWith(_ => _isLoading = false);
}
}
// 辅助方法:查找子控件
private T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (child is T found)
return found;
T childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
return null;
}
九、高级技术和实用案例
9.1 拖放功能实现
ListBox支持拖放操作,可以在同一个ListBox内部或不同ListBox之间进行项目拖放:
// 1. 在XAML中设置
<ListBox x:Name="sourceListBox" AllowDrop="True"
PreviewMouseLeftButtonDown="ListBox_PreviewMouseLeftButtonDown"
PreviewMouseMove="ListBox_PreviewMouseMove"
Drop="ListBox_Drop" />
// 2. C#中实现拖放逻辑
private Point _startPoint;
private ListBoxItem _draggedItem;
private void ListBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// 获取起始点
_startPoint = e.GetPosition(null);
// 获取当前点击的ListBoxItem
var item = FindAncestor<ListBoxItem>((DependencyObject)e.OriginalSource);
if (item != null)
{
_draggedItem = item;
}
}
private void ListBox_PreviewMouseMove(object sender, MouseEventArgs e)
{
// 确认是否开始拖拽
if (e.LeftButton == MouseButtonState.Pressed && _draggedItem != null)
{
Point currentPosition = e.GetPosition(null);
// 判断鼠标是否移动足够距离开始拖拽
if (Math.Abs(currentPosition.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(currentPosition.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
{
// 开始拖放
var data = _draggedItem.DataContext;
DragDrop.DoDragDrop(_draggedItem, data, DragDropEffects.Move);
_draggedItem = null;
}
}
}
private void ListBox_Drop(object sender, DragEventArgs e)
{
ListBox listBox = sender as ListBox;
// 获取拖放的数据
var data = e.Data.GetData(typeof(Product)) as Product;
if (data != null)
{
// 获取目标位置
Point dropPosition = e.GetPosition(listBox);
// 获取目标项目
var targetItem = GetItemAtPosition<ListBoxItem>(listBox, dropPosition);
// 处理拖放操作,例如重排序
if (targetItem != null)
{
int targetIndex = listBox.ItemContainerGenerator.IndexFromContainer(targetItem);
// 源列表中找到并移除该项
int sourceIndex = ((ObservableCollection<Product>)listBox.ItemsSource).IndexOf(data);
((ObservableCollection<Product>)listBox.ItemsSource).Move(sourceIndex, targetIndex);
}
}
}
// 辅助方法:查找祖先元素
private T FindAncestor<T>(DependencyObject current) where T : DependencyObject
{
do
{
if (current is T ancestor)
return ancestor;
current = VisualTreeHelper.GetParent(current);
}
while (current != null);
return null;
}
// 辅助方法:根据位置获取列表项
private T GetItemAtPosition<T>(ItemsControl itemsControl, Point position) where T : DependencyObject
{
UIElement element = itemsControl.InputHitTest(position) as UIElement;
while (element != null)
{
if (element is T item)
return item;
element = VisualTreeHelper.GetParent(element) as UIElement;
}
return null;
}
9.2 实现可编辑的ListBox
创建支持项目编辑的ListBox:
<ListBox ItemsSource="{Binding EditableItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- 显示或编辑模式切换 -->
<TextBlock Grid.Column="0" Text="{Binding Text}"
Visibility="{Binding IsEditing, Converter={StaticResource InverseBoolToVisConverter}}"/>
<TextBox Grid.Column="0" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding IsEditing, Converter={StaticResource BoolToVisConverter}}"
LostFocus="EditTextBox_LostFocus"/>
<!-- 编辑按钮 -->
<Button Grid.Column="1" Content="编辑" Margin="5,0,0,0"
Command="{Binding DataContext.EditCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}"
Visibility="{Binding IsEditing, Converter={StaticResource InverseBoolToVisConverter}}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
对应的ViewModel代码:
public class EditableItemsViewModel : INotifyPropertyChanged
{
public ObservableCollection<EditableItem> EditableItems { get; } = new ObservableCollection<EditableItem>();
public ICommand EditCommand { get; private set; }
public EditableItemsViewModel()
{
// 初始化数据
for (int i = 1; i <= 5; i++)
{
EditableItems.Add(new EditableItem { Text = $"项目 {i}" });
}
// 编辑命令
EditCommand = new RelayCommand<EditableItem>(item =>
{
item.IsEditing = true;
});
}
// INotifyPropertyChanged实现省略...
}
// 可编辑项类
public class EditableItem : INotifyPropertyChanged
{
private string _text;
private bool _isEditing;
public string Text
{
get => _text;
set
{
_text = value;
OnPropertyChanged("Text");
}
}
public bool IsEditing
{
get => _isEditing;
set
{
_isEditing = value;
OnPropertyChanged("IsEditing");
}
}
// INotifyPropertyChanged实现省略...
}
处理编辑完成的事件:
private void EditTextBox_LostFocus(object sender, RoutedEventArgs e)
{
TextBox textBox = sender as TextBox;
EditableItem item = textBox.DataContext as EditableItem;
if (item != null)
{
item.IsEditing = false;
}
}
十、ListBox与MVVM模式
10.1 MVVM模式中使用ListBox
在MVVM架构中,ListBox与ViewModel的结合使用:
public class ListViewModel : INotifyPropertyChanged
{
private ObservableCollection<ListItem> _items;
private ListItem _selectedItem;
public ObservableCollection<ListItem> Items
{
get => _items;
set
{
_items = value;
OnPropertyChanged(nameof(Items));
}
}
public ListItem SelectedItem
{
get => _selectedItem;
set
{
_selectedItem = value;
OnPropertyChanged(nameof(SelectedItem));
// 选择变更时可以触发其他逻辑
OnItemSelected();
}
}
public ICommand AddItemCommand { get; }
public ICommand RemoveItemCommand { get; }
public ListViewModel()
{
_items = new ObservableCollection<ListItem>();
// 初始化命令
AddItemCommand = new RelayCommand(AddNewItem);
RemoveItemCommand = new RelayCommand<ListItem>(RemoveItem, CanRemoveItem);
// 加载测试数据
LoadSampleData();
}
private void LoadSampleData()
{
for (int i = 1; i <= 10; i++)
{
_items.Add(new ListItem { Id = i, Title = $"项目 {i}" });
}
}
private void AddNewItem()
{
int nextId = Items.Count > 0 ? Items.Max(i => i.Id) + 1 : 1;
Items.Add(new ListItem { Id = nextId, Title = $"新项目 {nextId}" });
}
private void RemoveItem(ListItem item)
{
if (item != null)
Items.Remove(item);
}
private bool CanRemoveItem(ListItem item)
{
return item != null;
}
private void OnItemSelected()
{
// 在这里处理选择变更后的业务逻辑
if (_selectedItem != null)
{
Debug.WriteLine($"选中了: {_selectedItem.Title}");
}
}
// INotifyPropertyChanged实现省略...
}
XAML视图:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Id}" />
<TextBlock Grid.Column="1" Text="{Binding Title}" Margin="5,0"/>
<Button Grid.Column="2" Content="删除"
Command="{Binding DataContext.RemoveItemCommand,
RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Row="1" Content="添加新项目"
Command="{Binding AddItemCommand}"
HorizontalAlignment="Left"
Margin="10"
Padding="10,5"/>
</Grid>
总结
WPF的ListBox控件是一个强大而灵活的列表控件,提供了以下核心功能:
- 数据显示与绑定:通过Items或ItemsSource属性可以轻松显示各种类型的数据
- 项目选择:支持单选、多选和扩展选择模式
- 样式自定义:通过ItemTemplate和ItemContainerStyle可以完全控制外观
- 数据分组排序:结合CollectionView可实现复杂的数据组织方式
- 性能优化:内置虚拟化功能,可有效处理大量数据
- 交互功能:支持丰富的事件和命令,可实现拖放、编辑等高级功能
在MVVM架构中,ListBox是连接数据与用户界面的重要桥梁,通过数据绑定可以实现视图与模型的完全分离,使应用程序更易于维护和测试。