資料綁定是 XAML 應用開發的核心。Windows 8 Store Apps 提供多種進階綁定方式,滿足不同場景需求。本文詳細解析 ObservableCollection、CollectionViewSource、VirtualizedFilesVector、VirtualizedItemsVector 四種綁定模式的實作要點與適用場景。
ObservableCollection<T> 實作 INotifyCollectionChanged,當集合發生增、刪、改、移動時自動通知 UI 更新,是最常用的動態資料源。
<Page x:Class="XamlDemo.Binding.BindingObservableCollection" ...>
<Grid Background="Transparent">
<Grid Margin="120 0 0 10">
<Grid.Resources>
<DataTemplate x:Key="MyDataTemplate">
<Border Background="Blue" Width="200" CornerRadius="3" HorizontalAlignment="Left">
<TextBlock Text="{Binding Name}" FontSize="14.667" />
</Border>
</DataTemplate>
</Grid.Resources>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<Button Name="btnDelete" Content="刪除一筆記錄" Click="btnDelete_Click_1" />
<Button Name="btnUpdate" Content="更新一筆記錄" Click="btnUpdate_Click_1" Margin="10 0 0 0" />
</StackPanel>
<ListView x:Name="listView" ItemTemplate="{StaticResource MyDataTemplate}" Margin="0 50 0 0" />
</Grid>
</Grid>
</Page>
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using XamlDemo.Model;
namespace XamlDemo.Binding
{
public sealed partial class BindingObservableCollection : Page
{
private ObservableCollection<Employee> _employees;
public BindingObservableCollection()
{
this.InitializeComponent();
this.Loaded += BindingObservableCollection_Loaded;
}
void BindingObservableCollection_Loaded(object sender, RoutedEventArgs e)
{
_employees = new ObservableCollection<Employee>(TestData.GetEmployees());
_employees.CollectionChanged += _employees_CollectionChanged;
listView.ItemsSource = _employees;
}
void _employees_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// e.Action: Add, Remove, Replace, Move, Reset
// e.OldItems, e.NewItems, e.OldStartingIndex, e.NewStartingIndex
}
private void btnDelete_Click_1(object sender, RoutedEventArgs e)
{
_employees.RemoveAt(0); // 觸發 CollectionChanged
}
private void btnUpdate_Click_1(object sender, RoutedEventArgs e)
{
Random random = new Random();
// 透過 INotifyPropertyChanged 通知屬性變更
_employees.First().Name = random.Next(1000, 10000).ToString();
// 透過集合替換觸發通知
_employees[1] = new Employee() { Name = random.Next(1000, 10000).ToString() };
}
}
}
CollectionViewSource 提供分組、排序、篩選功能,配合 ListView.GroupStyle 可實現分組標題與明細的階層式呈現。
<Page x:Class="XamlDemo.Binding.BindingCollectionViewSource" ...>
<Grid Background="Transparent">
<Grid Margin="120 0 0 10">
<ListView x:Name="listView">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" FontSize="24.667" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" FontSize="14.667" Padding="50 0 0 0" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Grid>
</Page>
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
namespace XamlDemo.Binding
{
public sealed partial class BindingCollectionViewSource : Page
{
public BindingCollectionViewSource()
{
this.InitializeComponent();
this.Loaded += BindingCollectionViewSource_Loaded;
}
void BindingCollectionViewSource_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
XElement root = XElement.Load("SiteMap.xml");
var items = LoadData(root);
CollectionViewSource groupData = new CollectionViewSource();
groupData.IsSourceGrouped = true;
groupData.Source = items;
groupData.ItemsPath = new PropertyPath("Items");
listView.ItemsSource = groupData.View; // 綁定 ICollectionView
}
private List<GroupModel> LoadData(XElement root)
{
if (root == null) return null;
var items = from n in root.Elements("node")
select new GroupModel
{
Title = (string)n.Attribute("title"),
Items = LoadData(n)
};
return items.ToList();
}
class GroupModel
{
public string Title { get; set; }
public List<GroupModel> Items { get; set; }
}
}
}
適用於大量檔案(如圖片庫)的高效能顯示。FileInformationFactory 產生的 VirtualizedFilesVector 僅在需要時載入縮圖與中繼資料,避免一次性讀取所有檔案導致記憶體暴增。
<Page x:Class="XamlDemo.Binding.BindingVirtualizedFilesVector" ...>
<Grid Background="Transparent">
<Grid.Resources>
<converter:ThumbnailConverter x:Key="ThumbnailConverter"/>
<CollectionViewSource x:Name="itemsViewSource"/>
</Grid.Resources>
<GridView Name="gridView" Padding="120 0 0 10"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
SelectionMode="None">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Width="160" Height="120">
<Border Background="Red" Width="160" Height="120">
<Image Source="{Binding Path=Thumbnail, Converter={StaticResource ThumbnailConverter}}"
Stretch="None" Width="160" Height="120" />
</Border>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
</Page>
using Windows.Storage;
using Windows.Storage.BulkAccess;
using Windows.Storage.FileProperties;
using Windows.Storage.Search;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace XamlDemo.Binding
{
public sealed partial class BindingVirtualizedFilesVector : Page
{
public BindingVirtualizedFilesVector() { this.InitializeComponent(); }
protected override void OnNavigatedTo(NavigationEventArgs e)
{
QueryOptions queryOptions = new QueryOptions();
queryOptions.FolderDepth = FolderDepth.Deep;
queryOptions.IndexerOption = IndexerOption.UseIndexerWhenAvailable;
queryOptions.SortOrder.Clear();
queryOptions.SortOrder.Add(new SortEntry { PropertyName = "System.FileName", AscendingOrder = true });
StorageFileQueryResult fileQuery = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(queryOptions);
var fileInformationFactory = new FileInformationFactory(fileQuery, ThumbnailMode.PicturesView, 160, ThumbnailOptions.UseCurrentScale, true);
itemsViewSource.Source = fileInformationFactory.GetVirtualizedFilesVector();
}
}
}
VirtualizedItemsVector 同時包含檔案與資料夾,搭配 DataTemplateSelector 在執行期依項目類型動態選擇模板。
<Page x:Class="XamlDemo.Binding.BindingVirtualizedItemsVector" ...>
<Grid.Resources>
<converter:ThumbnailConverter x:Key="ThumbnailConverter"/>
<CollectionViewSource x:Name="itemsViewSource"/>
<DataTemplate x:Key="FolderTemplate"> ... </DataTemplate>
<DataTemplate x:Key="FileTemplate"> ... </DataTemplate>
<local:FileFolderInformationTemplateSelector x:Key="FileFolderInformationTemplateSelector"
FileInformationTemplate="{StaticResource FileTemplate}"
FolderInformationTemplate="{StaticResource FolderTemplate}" />
</Grid.Resources>
<GridView Name="gridView" Padding="120 0 0 10"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
ItemTemplateSelector="{StaticResource FileFolderInformationTemplateSelector}"
SelectionMode="None" />
</Page>
using Windows.Storage;
using Windows.Storage.BulkAccess;
using Windows.Storage.FileProperties;
using Windows.Storage.Search;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace XamlDemo.Binding
{
public sealed partial class BindingVirtualizedItemsVector : Page
{
public BindingVirtualizedItemsVector() { this.InitializeComponent(); }
protected override void OnNavigatedTo(NavigationEventArgs e)
{
QueryOptions queryOptions = new QueryOptions();
queryOptions.FolderDepth = FolderDepth.Shallow;
queryOptions.IndexerOption = IndexerOption.UseIndexerWhenAvailable;
queryOptions.SortOrder.Add(new SortEntry { PropertyName = "System.IsFolder", AscendingOrder = false });
queryOptions.SortOrder.Add(new SortEntry { PropertyName = "System.ItemName", AscendingOrder = true });
StorageItemQueryResult itemQuery = KnownFolders.PicturesLibrary.CreateItemQueryWithOptions(queryOptions);
var fileInformationFactory = new FileInformationFactory(itemQuery, ThumbnailMode.PicturesView, 160, ThumbnailOptions.UseCurrentScale, true);
itemsViewSource.Source = fileInformationFactory.GetVirtualizedItemsVector();
}
}
public class FileFolderInformationTemplateSelector : DataTemplateSelector
{
public DataTemplate FileInformationTemplate { get; set; }
public DataTemplate FolderInformationTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
return (item as FolderInformation) != null ? FolderInformationTemplate : FileInformationTemplate;
}
}
}
| 綁定類型 | 適用場景 | 關鍵優勢 |
|---|---|---|
| ObservableCollection<T> | 一般動態列表,需即時反映增刪改 | 簡單易用,自動通知 UI |
| CollectionViewSource | 需分組、排序、篩選的階層式資料 | 支援 ICollectionView,聲明式分組 |
| VirtualizedFilesVector | 大量檔案(圖片、影片)縮圖瀏覽 | 虛擬化載入,記憶體效率極高 |
| VirtualizedItemsVector | 檔案與資料夾混合顯示,需不同模板 | 支援 TemplateSelector,運行期動態切換 |