在运行时动态创建WPF ItemTemplate
在运行时,我想在WPF ListView中动态构建网格列(或者其他显示布局)。我不知道之前的列数和名称。
我希望能够做到:
MyListView.ItemSource = MyDataset;
MyListView.CreateColumns();
解决方案
有一个DataTemplateselector来选择预定义的模板(相同DataType)之一,并将选择器应用于ListView。我们可以具有不同列的尽可能多的DataTemplates。
我会尝试以下方法:
A)我们需要使列表框显示网格视图,我相信我们已经完成了此操作
B)为GridViewColumnHeader定义样式:
<Style TargetType="{x:Type GridViewColumnHeader}" x:Key="gridViewColumnStyle"> <EventSetter Event="Click" Handler="OnHeaderClicked"/> <EventSetter Event="Loaded" Handler="OnHeaderLoaded"/> </Style>
就我而言,我设置了一堆其他属性,但是在基本情况下,我们需要Loaded事件。如果要添加排序和过滤功能,请单击此项。
C)在listview代码中,将模板与gridview绑定:
public MyListView() { InitializeComponent(); GridView gridViewHeader = this.listView.View as GridView; System.Diagnostics.Debug.Assert(gridViewHeader != null, "Expected ListView.View should be GridView"); if (null != gridViewHeader) { gridViewHeader.ColumnHeaderContainerStyle = (Style)this.FindResource("gridViewColumnStyle"); } }
D)然后在OnHeaderLoaded处理程序中,我们可以根据列的数据设置适当的模板
void OnHeaderLoaded(object sender, RoutedEventArgs e) { GridViewColumnHeader header = (GridViewColumnHeader)sender; GridViewColumn column = header.Column;
//在此处选择并应用数据模板。
e.Handled = true; }
E)我想我们还需要获取ItemsSource依赖项属性的所有权并处理它的更改事件。
ListView.ItemsSourceProperty.AddOwner(typeof(MyListView), new PropertyMetadata(OnItemsSourceChanged)); static void OnItemsSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { MyListView view = (MyListView)sender; //do reflection to get column names and types //and for each column, add it to your grid view: GridViewColumn column = new GridViewColumn(); //set column properties here... view.Columns.Add(column); }
GridViewColumn类本身没有太多属性,因此我们可能希望使用添加属性在其中添加一些信息,例如,唯一列标记标头很可能会用于本地化,并且我们将不会对此进行中继。
通常,即使相当复杂,此方法也可以使我们轻松扩展列表视图功能。
我们可以使用DataTemplateSelector返回在代码中动态创建的DataTemplate。但是,与使用XAML中的预定义代码相比,这有点繁琐且复杂,但是仍然可以实现。
看一下这个例子:http://dedjo.blogspot.com/2007/03/creating-datatemplates-from-code.html
我们可以使用添加属性将列动态添加到ListView。在CodeProject上查看这篇文章,它可以准确地说明...
WPF DynamicListView绑定到DataMatrix
从MSDN:
MyListBox.ItemsSource = view; ListView myListView = new ListView(); GridView myGridView = new GridView(); myGridView.AllowsColumnReorder = true; myGridView.ColumnHeaderToolTip = "Employee Information"; GridViewColumn gvc1 = new GridViewColumn(); gvc1.DisplayMemberBinding = new Binding("FirstName"); gvc1.Header = "FirstName"; gvc1.Width = 100; myGridView.Columns.Add(gvc1); GridViewColumn gvc2 = new GridViewColumn(); gvc2.DisplayMemberBinding = new Binding("LastName"); gvc2.Header = "Last Name"; gvc2.Width = 100; myGridView.Columns.Add(gvc2); GridViewColumn gvc3 = new GridViewColumn(); gvc3.DisplayMemberBinding = new Binding("EmployeeNumber"); gvc3.Header = "Employee No."; gvc3.Width = 100; myGridView.Columns.Add(gvc3); //ItemsSource is ObservableCollection of EmployeeInfo objects myListView.ItemsSource = new myEmployees(); myListView.View = myGridView; myStackPanel.Children.Add(myListView);
根据经验,我可以建议我们尽量避免使用动态数据模板...而是使用此处提供的建议来明确创建ListView列,而不是尝试动态创建DataTemplate。
原因是FrameworkElementFactory(或者任何在运行时用于生成DataTemplates的类名)在使用时都有些笨拙(并且不赞成将XAML用于动态模板)会影响性能。