在运行时动态创建WPF ItemTemplate

时间:2020-03-06 14:38:42  来源:igfitidea点击:

在运行时,我想在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用于动态模板)会影响性能。