如何临时禁用 WPF Listview 虚拟化?

How to temporary disable WPF Listview virtualization?

在自定义 ListView 控件上,我正在开发一个 "Export to Excel" 小按钮,用于将 ListView 的所有内容发送到 Excel。由于此按钮嵌入在 ListView 控件中,因此我不知道列出了哪种 object 或显示了哪些列。

所以我的方法是通用的,只解析所有列 headers 和所有行以将数据导出到 Excel。但是当解析不可见的 ListViewItem 时,您会得到空结果。一种解决方案是在我的 ListView 上设置 VirtualizingStackPanel.VirtualizationMode="False",但当您有数千行时,它会强烈影响 GUI 体验。

我正在寻找一种方法来加载所有 ListViewItem 仅用于我的通用导出方法,并为 GUI 保持虚拟化。

有人可以帮我吗?

谢谢。

我终于来Reflection来解决我的问题了。基本上,我首先解析所有列以找到绑定的 属性 名称。然后对 ListView 中的 Items 集合进行一些反思以找到 属性 值。

这是代码(尚未完善):

private void OnExportToExcel(object sender, ExecutedRoutedEventArgs args)
    {
        if (this.Items.Count > 0)
        {
            // Ensure we have a visible and selected item
            if (this.SelectedItem == null)
                this.SelectedItem = this.Items[0];
            this.ScrollIntoView(this.SelectedItem);

            // Allow UI thread to update first or our selected lvi will be null if not visible
            UIHelpers.AllowUIToUpdate();

            List<string> columnPropertyBindingPathList = new List<string>();
            int colIndex = 0;

            // Get column binding path of underlying data
            foreach (GridViewColumn h in ((GridView)this.View).Columns)
            {
                string bindingPath = "";

                if (h.DisplayMemberBinding != null)
                {
                    bindingPath = (h.DisplayMemberBinding as Binding).Path.Path;
                }
                else
                {
                    ListViewItem lvi = this.ItemContainerGenerator.ContainerFromIndex(this.SelectedIndex) as ListViewItem;
                    if (lvi != null)
                    {
                        GridViewRowPresenter gvp = UIHelpers.FindVisualChild<GridViewRowPresenter>(lvi);
                        DependencyObject col = VisualTreeHelper.GetChild(gvp, colIndex);
                        TextBlock tb = col as TextBlock;
                        if (tb == null)
                        {
                            tb = UIHelpers.FindVisualChild<TextBlock>(col);
                            bindingPath = BindingOperations.GetBinding(tb, TextBlock.TextProperty).Path.Path;
                        }
                    }
                }

                colIndex++;
                if (bindingPath != "")
                    columnPropertyBindingPathList.Add(bindingPath);
            }

            if (columnPropertyBindingPathList.Count > 0)
            {
                Mouse.SetCursor(Cursors.Wait);

                // Init array to export to excel
                object[,] dataArray = new object[this.Items.Count + 1, columnPropertyBindingPathList.Count];

                // Add column header
                for (int colCnt = 0; colCnt < columnPropertyBindingPathList.Count; colCnt++)
                    dataArray[0, colCnt] = columnPropertyBindingPathList[colCnt];

                // Reflection to read underlying objects
                int rowCnt = 1;
                foreach (object obj in this.Items)
                {
                    for (int colCnt = 0; colCnt < columnPropertyBindingPathList.Count; colCnt++)
                        dataArray[rowCnt, colCnt] = UIHelpers.GetPropValue(columnPropertyBindingPathList[colCnt], obj);

                    rowCnt++;
                }

                Mouse.SetCursor(Cursors.Arrow);

                // Throw event
                RoutedEventArgs newEventArgs = new RoutedEventArgs(EmListView.ExportToExcelEvent);
                newEventArgs.Source = dataArray;
                RaiseEvent(newEventArgs);
            }
            else
            {
                // Throw event
                RoutedEventArgs newEventArgs = new RoutedEventArgs(EmListView.ExportToExcelEvent);
                newEventArgs.Source = null;
                RaiseEvent(newEventArgs);
            }
        }
        else
        {
            // Throw event
            RoutedEventArgs newEventArgs = new RoutedEventArgs(EmListView.ExportToExcelEvent);
            newEventArgs.Source = null;
            RaiseEvent(newEventArgs);
        }
    }

UIHelpers 方法:

/// <summary>
    /// Give the UI thread the time to refresh
    /// </summary>
    public static void AllowUIToUpdate()
    {
        DispatcherFrame frame = new DispatcherFrame();

        Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, new DispatcherOperationCallback(delegate(object parameter)
        {
            frame.Continue = false;
            return null;
        }), null);

        Dispatcher.PushFrame(frame);
    }

    /// <summary>
    /// Find a visual child type inside a DependencyObject
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="obj"></param>
    /// <returns></returns>
    public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(obj, i);
            if (child != null && child is T)
                return (T)child;
            else
            {
                T childOfChild = FindVisualChild<T>(child);
                if (childOfChild != null)
                    return childOfChild;
            }
        }
        return null;
    }

    /// <summary>
    /// Find a property value inside a object (even for multi level property)
    /// </summary>
    /// <param name="name"></param>
    /// <param name="obj"></param>
    /// <returns></returns>
    public static Object GetPropValue(String name, Object obj)
    {
        foreach (String part in name.Split('.'))
        {
            if (obj == null) { return null; }

            Type type = obj.GetType();
            PropertyInfo info = type.GetProperty(part);

            if (info == null) { return null; }

            obj = info.GetValue(obj, null);
        }

        return obj;
    }